I push a web service call in my activity to a thread (shown below). The first time I do this in the activity it works fine (gets the text from my edittext and loads the service to get lat/lng data)
But when I click the back button (emulator) and try to fire off this thread a second time it blows up after the .start(); in my click handler. What might I be doing wrong here? thanks
private Thread getLocationByZip = new Thread() {
public void run() {
try {
EditText filterText = (EditText) findViewById(R.id.zipcode);
Editable zip = filterText.getText();
LocationLookupService locationLookupService = new LocationLookupService();
selectedLocation = locationLookupService.getLocationByZip(zip.toString());
locationHandler.post(launchFindWithLocationInfo);
} catch (Exception e) {
}
}
};
private Runnable launchFindWithLocationInfo = new Runnable() {
#Override
public void run() {
try {
Intent abc = new Intent(LocationLookup.this, FindWithLocation.class);
startActivity(abc);
} catch (Exception e) {
}
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.location);
locationHandler = new Handler();
findViewById(R.id.findbyzip).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getLocationByZip.start();
}
});
}
Update
After the great advice I went with an AsyncTask so if anyone finds this going forward the above thread/handler model looks something like the below as an asynctask
private class LocationLookupTask extends AsyncTask<String, Void, Location> {
private ProgressDialog dialog;
#Override
protected void onPreExecute() {
this.dialog = ProgressDialog.show(LocationLookup.this, "", "Loading...");
}
#Override
protected Location doInBackground(String... zips) {
Location selectedLocation = null;
for (String zip : zips) {
LocationLookupService locationLookupService = new LocationLookupService();
selectedLocation = locationLookupService.getLocationByZip(zip);
}
return selectedLocation;
}
#Override
protected void onPostExecute(Location location) {
this.dialog.dismiss();
((AppDelegate) getApplicationContext()).setSelectedLocation(location);
Intent abc = new Intent(LocationLookup.this, FindWithLocation.class);
startActivity(abc);
}
}
Now to call this in the onclick you would do this
findViewById(R.id.findbyzip).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
EditText filterText = (EditText) findViewById(R.id.zipcode);
Editable zip = filterText.getText();
LocationLookupTask task = new LocationLookupTask();
task.execute(new String[]{zip.toString()});
}
});
You can't start a thread twice:
It is never legal to start a thread more than once.
Taken from Thread.start().
So, you need to create a new thread and start that one.
You can not call twice the start method of the Thread class, I suggest you also control the logic within the method onCreate since according to the life cycle of an Activity that method may be called by Android lifecycle Activity Manager.
Furthermore i suggest you to avoid this approach and consider to use the AsyncTask provided by the Android SDK.
http://developer.android.com/reference/android/os/AsyncTask.html
If you really want to do this without creating a new class or using AsyncTask, you could just make a method to get a new Thread on each call:
private Thread getLocationByZip;
private void getLocation() {
getLocationByZip = new Thread() {
public void run() {
try {
EditText filterText = (EditText) findViewById(R.id.zipcode);
Editable zip = filterText.getText();
LocationLookupService locationLookupService = new LocationLookupService();
selectedLocation = locationLookupService.getLocationByZip(zip.toString());
locationHandler.post(launchFindWithLocationInfo);
} catch (Exception e) {
}
}
};
getLocationByZip.start();
}
Then replace getLocationByZip.start() in your code with getLocation(). However, I agree that an AsyncTask would be a better way to go, though this would work for you.
Related
I've seen some of the answers to similar questions on here but they aren't working for me.
What I want to do is start a Thread and run some code in it, but before I start the thread, I want to display a loading dialog, and as soon as the thread is finished running, close the loading dialog.
So something like this:
LoadingDialog dialog = new LoadingDialog(MainActivity.this);
class MyThread {
...
void send() {
dialog.displayDialog();
// DO THINGS HERE
new Thread((Runnable) () -> {
try{
...
} catch (...) {
}
}).start();
// Close the dialog with dialog.closeDialog();
}
}
(displayDialog() and closeDialog() are just methods I created in my LoadingDialog class to start and dismiss the dialogs)
I'm not sure exactly how to do this, I've seen some uses of handler on this site but they're all different and not working for me so I'm a little stuck.
Thanks
Solution
public class MainActivity extends AppCompatActivity {
LoadingDialog dialog;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dialog = new LoadingDialog(MainActivity.this);
MyThread myThread = new MyThread(MainActivity.this);
myThread.start();
}
static class MyThread extends Thread {
private WeakReference<MainActivity> activityRef;
public MyThread(MainActivity activity) {
activityRef = new WeakReference<>(activity);
}
#Override
public void run() {
send();
}
void send() {
// Display dialog
if (getActivity() != null) {
getActivity().dialog.displayDialog();
}
// DO THINGS HERE
// TODO: Write your code that execute in a background here
// Close dialog
if (getActivity() != null) {
getActivity().dialog.closeDialog();
}
}
private MainActivity getActivity() {
return activityRef.get();
}
}
}
I am pretty new to Android studio, but so far, so good!
I have an android app which is up and running together with an websocket that is running on my computer.
So when both the websocket and the app is up and running.
I could write a message, which then gets printed in the websocket terminal, and the websocket also return an message which get printed on the list on my phone. Also, if the websocket receive a special message from the app, the webscocket also return a special message which is shown on and added on the list! Perfect! Isnt it?
Now what I am trying to do is that, the app listens to a special message/event from the websocket which in this case is "SuperSpecialMessage". If the websocket sends this specific message, the application is
going to react and do something. In this case make an Invisible buttong to be visible.
SO WHAT IS THE PROBLEM? The problem here is that I get the error:
"android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
Could someone help me please?
Here is my code:
public class WebSocketActivity extends AppCompatActivity {
private Socket mSocket;
private TextView mSocketStatus;
EditText messageTextview;
ListView the_list_view_on_layout;
ArrayAdapter arrayAdapter;
String TheMessages;
List<String> list_with_messages;
Button theExtraButton;
Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_websocket);
this.mHandler = new Handler();
m_Runnable.run();
// Elements that are created on the layout
mSocketStatus = (TextView)findViewById(R.id.socketStatus);
theExtraButton = (Button)findViewById(R.id.extraBtn);
theExtraButton.setVisibility(View.INVISIBLE);
the_list_view_on_layout = findViewById(R.id.listViewID);
messageTextview = findViewById(R.id.messageTextView);
String[] TheMessages = new String[]{};
list_with_messages = new ArrayList<String>(Arrays.asList(TheMessages));
//ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list_with_messages);
arrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list_with_messages);
the_list_view_on_layout.setAdapter(arrayAdapter);
TheSocketClass app = (TheSocketClass) getApplication();
mSocket = app.getSocket();
mSocket.connect();
checkIfConnectedToSocket();
Button sendMessageToSocketServer = (Button) findViewById(R.id.sendMessageToSocketBtn);
sendMessageToSocketServer.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String theTextWeWrite = messageTextview.getText().toString();
mSocket.emit("sendTextToServer", theTextWeWrite);
list_with_messages.add(theTextWeWrite);
//arrayAdapter.notifyDataSetChanged();
}
});
runOnUiThread(new Runnable() {
#Override
public void run() {
mSocket.on("ServerMessage", new Emitter.Listener() {
#Override
public void call(Object... args) {
JSONObject data = (JSONObject) args[0];
list_with_messages.add(data.toString());
//arrayAdapter.notifyDataSetChanged(); // I GET ERROR HERE
}
});
}
});
runOnUiThread(new Runnable() {
#Override
public void run() {
mSocket.on("SuperSpecialMessage", new Emitter.Listener() {
#Override
public void call(Object... args) {
theExtraButton.setVisibility(View.VISIBLE);
}
});
}
});
}
private void checkIfConnectedToSocket(){
mSocket.connect();
if (mSocket.connected() == true) {
mSocketStatus.setText("Connected to socket!");
mSocketStatus.setTextColor(Color.GREEN);
}
else if (!mSocket.connected()){
mSocketStatus.setText("Error connecting to socket!");
mSocketStatus.setTextColor(Color.RED);
}
}
}
Thank you.
EDIT: Solved!
First i made an method like this:
private void makeTheButtonVisible(){
runOnUiThread(new Runnable() {
#Override
public void run() {
theExtraButton.setVisibility(View.VISIBLE);
}
});
}
and when the app recieve the message, the method is called like this:
runOnUiThread(new Runnable() {
#Override
public void run() {
mSocket.on("SuperSpecialMessage", new Emitter.Listener() {
#Override
public void call(Object... args) {
makeTheButtonVisible();
}
});
}
});
I ran into a problem and need some help. I want to remove some user's files after a button click and also show the files removing progress (in progressbar) and also show some Fancy UI. First i changed the layout after button click and hide some elements and visible the others. after that i called methods to remove files. The problem is that i can not see any UI changes and system hangs until all user file removed and after that based on my scenario it go to another activity. I've google around and found that i should use thread or UI thread but exactly don't know how. Here is my code :
new Thread() {
public void run() {
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
ProgressBar spinner;
spinner = (ProgressBar) findViewById(R.id.progressBar);
listview.setVisibility(View.GONE);
spinner.setVisibility(View.VISIBLE);
preresult.setVisibility(View.VISIBLE);
resulttxt.setVisibility(View.VISIBLE);
wv.setVisibility(View.VISIBLE);
btnClear.setVisibility(View.GONE);
wv.loadUrl("file:///android_asset/rocket.gif");
resulttxt.setText("");
}
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
Thread b = new Thread() {
#Override
public void run() {
Long TotalJunk = 0L;
for (Apps social : checkedSocial) {
if (social.getName() == "Telegram") {
preresult.setText("Calculating Files :");
resulttxt.setText("Telegram");
preresult.setText("Removing Files...");
clearMediashistory(social.path);
TotalJunk = TotalJunk + social.junksize;
}
}
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = pref.edit();
editor.putString("FreeUp", formatSize(TotalJunk));
//commits your edits
editor.commit();
}
};
b.start();
What is wrong with my code. Is there any better method to do that?
Use AsyncTask instead of Thread
https://developer.android.com/reference/android/os/AsyncTask.html
Android AsyncTask example and explanation
Try
new AsyncTask<String, String, String> () {
#Override
protected void onPreExecute() {
//show loader if requried
}
#Override
protected String doInBackground(String... params) {
Long TotalJunk = 0L;
for (Apps social : checkedSocial) {
if (social.getName() == "Telegram") {
preresult.setText("Calculating Files :");
resulttxt.setText("Telegram");
preresult.setText("Removing Files...");
clearMediashistory(social.path);
TotalJunk = TotalJunk + social.junksize;
}
}
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
SharedPreferences.Editor editor = pref.edit();
editor.putString("FreeUp", formatSize(TotalJunk));
//commits your edits
editor.commit();
}
#Override
protected void onPostExecute(String result){
ProgressBar spinner;
spinner = (ProgressBar) findViewById(R.id.progressBar);
listview.setVisibility(View.GONE);
spinner.setVisibility(View.VISIBLE);
preresult.setVisibility(View.VISIBLE);
resulttxt.setVisibility(View.VISIBLE);
wv.setVisibility(View.VISIBLE);
btnClear.setVisibility(View.GONE);
wv.loadUrl("file:///android_asset/rocket.gif");
resulttxt.setText("");
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTER);
I have a class called RestClient that gets some information from my webService and then return and I'm trying to make a Progress dialog run while it is accessing the internet. And as I use this class in more than one place I won't make in the Activity itself. Here is my RestClient class:
public class RestClient extends AsyncTask<URL, String, String> {
private Context context;
private String string;
public RestClient(Context context, String string)
{
this.context = context;
this.string = string;
}
#Override
protected void onPreExecute() {
dialog = ProgressDialog.show(context, "Buscando seu Produto","Por favor, espere um momento...",true ,false);
//I've already tried:
/*ProgressDialog dialog = new ProgressDialog(context);
dialog.setTitle("Buscando seu Produto");
dialog.setMessage("Por favor, espere um momento...");
dialog.setIndeterminate(true);
dialog.setCancelable(false);*/
dialog.show();
super.onPreExecute();
}
#Override
protected String doInBackground(URL... params) {
try {
//Some WebService gets and Json conversions using my string variable
//and some Thread.sleep that counts 2000 miliseconds to do all the queries
dialog.dismiss();
} catch (IOException | InterruptedException |JSONException e) {
e.printStackTrace();
dialog.dismiss();
return e.getMessage();
}
return null;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}
And in my activity I call the class RestClient when I click a button like this:
--- EDIT : I forgot to mention that I have an AlertDialog in this same activity that CAN be shown sometimes before and after the ProgressDialog ---
private Button buttonConfirm;
private EditView evString;
private String theString;
private String returnFromExecute;
private RestClient restClient;
private AlertDialog.Builder dialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_access_webservice);
evString = (EditText) findViewById(R.id.editViewMyString);
buttonConfirm = (Button) findViewById(R.id.buttonConfirm);
dialog = new ProgressDialog(IdentificacaoDeProdutoActivity.this);
dialog.setTitle("Error");
dialog.setMessage("Please try again");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
buttonConfirmar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
theString = evString.getText().toString();
if(!(theString!=null && theString.trim().length()>0)) //To check if theString is not null
{
dialog.show();
}
restClient = new RestClient(AccessWebserviceActivity.this, theString);
//Then I call execute and put a Thread.sleep a bit longer to compensate the ones I have in my doInBackground
restClient.execute();
try {
Thread.sleep(2050);
} catch (Exception e) {
dialog.show();
return;
}
}
}
}
The problem is that my ProgressDialog never shows. I've already tried getParent(), getApplication() and getApplicationContext() instead of AccessWebserviceActivity.this but none have worked. Someone Have any idea what is happening and what should I do?
you have not created progress dialog try this.
ProgressDialog dialog;
#Override
protected void onPreExecute() {
dialog= new ProgressDialog(context);
dialog.setMessage("on Progress");
dialog.show();
super.onPreExecute();
}
returnFromExecute = restClient.get();
Remove that statement. You have already:
restClient.execute();
That should do.
The result of doInBackground() you should handle in onPostExecute(). It cannot be handled or retrieved in onCreate().
You need to call
dialog = ProgressDialog.show(context, "Buscando seu Produto","Por favor, espere um momento...",true ,false);
and remove
dialog.show();
Also put your dialog.dismiss(); method in onPostExecute(). This dialog.dismiss() method is good in catch block but what's its purpose if you are calling this method in try block. It will remove progress dialog as soon as you call this AsyncTask.
After a lot of researches about Threads and Process I found out that I had to encapsulate the all the code I have after my
RestClient.execute in a
new Thread(new Runnable() { public void run() { // My code } });
so that the execution of the code happened in background as well as the WebService query.
EDIT:
Even if creating a new Thread works, it is not recommended! The right thing to do would be to create another class that extends AsyncTask to do job.
I get this error "Can't create handler inside thread that has not called Looper.prepare()"
Can you tell me how to fix it?
public class PaymentActivity extends BaseActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.payment);
final Button buttonBank = (Button) findViewById(R.id.buttonBank);
buttonBank.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
progressDialog = ProgressDialog.show(PaymentActivity.this, "",
"Redirecting to payment gateway...", true, true);
new Thread() {
public void run() {
try {
startPayment("Bank");
} catch (Exception e) {
alertDialog.setMessage(e.getMessage());
handler.sendEmptyMessage(1);
progressDialog.cancel();
}
}
}.start();
}
});
StartPayment Method:
private void startPayment(String id) {
Bundle b = getIntent().getExtras();
final Sail sail = b.getParcelable(Constant.SAIL);
final Intent bankIntent = new Intent(this, BankActivity.class);
try {
Reservation reservation = RestService.createReservation(
sail.getId(),
getSharedPreferences(Constant.PREF_NAME_CONTACT, 0));
bankIntent.putExtra(Constant.RESERVATION, reservation);
// <workingWithDB> Storing Reservation info in Database
DBAdapter db = new DBAdapter(this);
db.open();
#SuppressWarnings("unused")
long rowid;
rowid = db.insertRow(sail.getId(), sail.getFrom(),
sail.getTo(), sail.getShip(), sail.getDateFrom().getTime(),
sail.getPrice().toString(), reservation.getId().floatValue());
db.close();
// </workingWithDB>
String html = PaymentService.getRedirectHTML(id, reservation);
bankIntent.putExtra(Constant.BANK, html);
} catch (Exception e) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog alertDialog = builder.create();
alertDialog.setMessage(e.getMessage());
alertDialog.show();
}
startActivity(bankIntent);
}
You should know that when you try to modify your UI , the only thread who can do that is the UiThread.
So if you want to modify your UI in another thread, try to use the method: Activity.runOnUiThread(new Runnable);
Your code should be like this :
new Thread() {
public void run() {
YourActivity.this.runOnUiThread(new Runnable(){
#Override
public void run(){
try {
startPayment("Bank");//Edit,integrate this on the runOnUiThread
} catch (Exception e) {
alertDialog.setMessage(e.getMessage());
handler.sendEmptyMessage(1);
progressDialog.cancel();
}
});
}
}
}.start();
I assume you create a Handler in startPayment() method. You can't do that, as handlers can be created on th UI thread only. Just create it in your activity class.
Instead of new Thread() line, try giving
this.runOnUiThread(new Runnable() {
you cant change any UI in thread you can use runOnUIThread or AsyncTask for more detail about this click here
I've found that most thread handling can be replaced by AsyncTasks like this:
public class TestStuff extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button buttonBank = (Button) findViewById(R.id.button);
buttonBank.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new StartPaymentAsyncTask(TestStuff.this).execute((Void []) null);
}
});
}
private class StartPaymentAsyncTask extends AsyncTask<Void, Void, String> {
private ProgressDialog dialog;
private final Context context;
public StartPaymentAsyncTask(Context context) {
this.context = context;
}
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(context);
// setup your dialog here
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setMessage(context.getString(R.string.doing_db_work));
dialog.setCancelable(false);
dialog.show();
}
#Override
protected String doInBackground(Void... ignored) {
String returnMessage = null;
try {
startPayment("Bank");
} catch (Exception e) {
returnMessage = e.getMessage();
}
return returnMessage;
}
#Override
protected void onPostExecute(String message) {
dialog.dismiss();
if (message != null) {
// process the error (show alert etc)
Log.e("StartPaymentAsyncTask", String.format("I received an error: %s", message));
} else {
Log.i("StartPaymentAsyncTask", "No problems");
}
}
}
public void startPayment(String string) throws Exception {
SystemClock.sleep(2000); // pause for 2 seconds for dialog
Log.i("PaymentStuff", "I am pretending to do some work");
throw new Exception("Oh dear, database error");
}
}
I pass in the Application Context to the Async so it can create dialogs from it.
The advantage of doing it this way is you know exactly which methods are run in your UI and which are in a separate background thread. Your main UI thread isn't delayed, and the separation into small async tasks is quite nice.
The code assumes your startPayment() method does nothing with the UI, and if it does, move it into the onPostExecute of the AsyncTask so it's done in the UI thread.
Try
final Handler handlerTimer = new Handler(Looper.getMainLooper());
handlerTimer.postDelayed(new Runnable() {
public void run() {
......
}
}, time_interval});