I need a "Loading" progressdialog to appear at the start of an activity, and then display some stuff after loading is finished. In my onresume, I have code similar to this:
loadThread = true;
Thread showDetailsThread = new Thread() {
#Override
public void run() {
while (loadThread == true) {
Looper.prepare();
try {
dialog = ProgressDialog.show(myactivity.this, "", "Loading data...", true);
} finally {
handler.sendEmptyMessage(0);
}
Looper.loop();
}
}}; showDetailsThread.start();
The handler (not shown) displays everything I need displayed, and while the data is loading, the progressdialog displays as expected. The problem is, after running the memory analyzer in eclipse, I realized that every time I visit this activity it spawns a new instance of everything created by the handler in a new thread, without destroying the previous one. With multiple instances of the activity running forever in multiple threads, I eventually run out of memory.
Here's what I don't understand. I was under the impression that, given the above example, the handler would run in the main ui thread, not in the new thread containing the dialog. The only thing that should exist in the new thread is the progressdialog, and the dialog is instantly dismissed at the beginning of the handler (not shown), which should stop the thread, right? Why are these threads being created and running indefinitely? BTW, I have "loadThread = false;" in my onpause, ondestroy, and onstop in an attempt to halt the thread after navigating to another activity, but it doesn't work.
If I just call the handler directly instead of running it in the showdetailsThread finally{}, everything is fine and there is no leak, but I really need the progressdialog to appear during the 2 second delay that occurs during loading.
There is probably a better way to do this anyway. Any help is greatly appreciated.
No more memory leak now. I needed to create a handler within the new thread, which would bind to the Looper (somewhere between Looper.prepare() and Looper.loop():
threadKillerHandler = new Handler();
And then call quit() on the looper's handler when I'm done with the thread (in onDestroy in my case):
threadKillerHandler.getLooper().quit();
Otherwise the thread will run forever, and instances will keep piling up every time a user revisits the activity.
I would suggest using AsynchTask instead of the way you are doing it. This will precent the multiple instances appearing.
Documentation
Example
Related
I have a web service request in my Android app which might take a second or two to run. I have an event handler which fires when it completes. I am trying to show a dialog to show the progress of this request.
So far I have this:
var progressDialog = Android.App.ProgressDialog.Show(this.Activity, "Please wait...", "Communicating with server...", true);
new Thread(new ThreadStart(delegate {
WCFClient WCF = WCFService();
WCF.TestCompleted+= TestCompleted;
WCF.TestAsync(GetID());
progressDialog.Dismiss();}
)
).Start();
The problem is obvious, the dialog displays and disappears in a flash. I am declaring var progressDialog in the UI thread. How can I reference it again in an event handler?
I would normally use RunOnUIThread but I cannot target "progressDialog" that way as it does not exist in my layout.
It seems like this should be easy - am I missing something in my approach?
There are a few issues with your code. The first is there is a memory leak. You are holding onto the progress dialog in your thread. If the user rotates the device, your Activity will be killed but the garbage collector won't be able to garbage collect the reference because your Thread is holding onto it.
Secondly, you cannot update the UI from another thread, you have to update on the UI thread.
You need to use a Handler, pass a reference to the other threads (in the constructor), then when needed send a message to UIthread to dismiss the progress dialog.
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (progressDialog.isShowing()) progressDialog.dismiss();
}
};
in the thread, do this:
mHandler.obtainMessage().sendToTarget();
Also there is another issue in the code, you don't seem to check if you completed the task, BEFORE dismissing the dialog.
I have a MainActivity that uses fragments.
The onCreate of MainActivity completes its onCreate with the use of
welcomeFragment = new MyWelcomeFragment();
fr.beginTransaction().replace(R.id.mainContent, welcomeFragment).commit()
As a part of MyWelcomeFragment's on onResume, a thread is started to get updates from my webserver. If the user selects an action before the thread is completed and goes to MyNewsFragment, what happens to the thread that has yet to complete running in MyWelcomeFragment's thread stack?
Thread was created with: (myThread and handler are instance variables)
myThread = new Thread(new Runnable() {
#Override
public void run() {
sendDataToServer("");
handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
onTaskDone();
}
});
}
});
myThread.start();
Dalvik keeps all Thread references in the runtime so your thread will keep running unless it is terminated or completes (some reference). So depending on where you start your thread, you may be creating more than one. There is no clean way to cancel a Thread and in this case you may need to first cancel the http request inside sendDataToServer and use a shared flag to stop the thread.
In a bigger picture, I would suggest
move the networking method to Activity and handle it there since it has longer lifespan than
Fragment
use Android Volley to handle networking. With it you can manage inadvertent multiple requests to send data to your server. Since each request can be attached with tag, you can cancel any with a particular tag in the queue (in your case the one corresponding to sendDataToServer process) before starting a new one.
and finally use Publisher-Subsriber pattern which has already been made available by libraries like Otto or EventBus. This allows communication between Fragments or Activities while avoiding life cycle related problems. In a gist: a publisher emits events to subscribers registered to it and unlike listeners both publisher and subscriber are totally decoupled. In your case, when sendDataToServer completes, you will not know if the fragment containing onTaskDone is still around. If this method manipulates UI while the fragment has destroyed its view then you will definitely get an error. So onTaskDone should be wrapped inside a subscriber method whose parent fragment is registered to the http event publisher and deregistered as soon as its view is destroyed.
It'll keep running until run() method completes, which is probably for how long it takes for sendDataToServer("") takes to complete, as the handler should be fairly quick in comparison to network IO - or the thread is force interrupted.
Are you still interested in the result if the user switches fragments?
Are you keeping a reference to the welcome fragment? (Via either fragment manager or activity) - if so you could still access the result.
If the user goes back to welcome fragment, the previous thread reference will be lost.
Thread will keep on running till MyWelcomeFragment is alive and If you don't kill it in onPause().
Quick question: I have been using frameworks that spawn worker threads to perform asynchronous tasks, a good example is Retrofit. Within the success/failure sections, I may pop up a Dialog box which would need to be on the UI thread. I have been accessing the underlying
Activity/UI thread in this fashion within the success/failure sections of Retrofit:
Dialog dialog = new Dialog(LoginActivity.this, R.style.ThemeDialogCustom);
This works well 99.9% of the time but every once in a while, I receive the following error when creating a Dialog box:
android.view.WindowManager$BadTokenException
LoginActivity.java line 343 in LoginActivity$6.success()
Unable to add window -- token android.os.BinderProxy#41662138 is not valid;
is your activity running?
So, is my approach the most stable way to access the Activity context/UI thread from a worker thread or do I need a different approach?
If you work with threads and not using Asynctasks, always run everything that changes UI in runOnUIThread like this
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
//change UI
}
});
The more generic way to do it is this, which is pretty much the same
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//change UI
}
})
See here the minimal difference between runOnUIThread and MainLooper
If you want to check if you are on the main/ui thread
if(Thread.currentThread() == Looper.getMainLooper().getThread()) {
//you are on the main thread
}
AFAIK, there is nothing wrong with the approach you are using. The problem is occurring because the by the time the worker thread finishes and you are trying to show the dialog, the instance of the Activity has finished. So, the crash is totally dependent on the amount of time it takes for the thread to finish. And it seems that in your case, the thread mostly finishes when the Activity is still active; hence you don't get the error is most cases.
What you need to do is to check if the Activity is still running before trying to show the Dialog. One of the simplest ways would be to
if(!((Activity) LoginActivity.this).isFinishing())
{
//safe to show your dialog
}
I want to force android to wait AND continue processing something at the same time. I have seen the Thread wait function, but that just makes things hang for a while not actually letting the app do anything. Subsequent processes are simply queued up waiting their turn.
I want to force the timing of a process. This is kind of a combination between having a thread with a wait AND an asynctask
insight appreciated
public class yourActivity extends Activity{
final WebView yourWebview; //this is the webview
Context mContext = this;
public void onCreate(Bundle B){
setContentView(R.id.somethingtoshow);//this will be shown while webview working
Runnable yourRun = new Runnable(){
public void run(){
yourWebview = new WebView(mContext);
//do whatever you want with it
//loadUrl and whatever you want
//when your done
runOnUiThread(new Runnable(){
public void run(){
setContentView(yourWebView);
}
});
}
};
Thread T= new Thread(yourRun);
T.start();
}
}
'Waiting' means to put the thread in a suspended state - do you mean having the app simply do nothing until the process is completed?
You never want to make the main event thread hang or wait, that will make the user think the app is frozen. To do what you are wanting, you will probably spawn an async thread that loads the page from the main activity. The activity will continue to display whatever you had it doing last, and will not hang up or freeze while the async is going in the background. However, the user will still be able to press buttons, and might mess you up.
So to get the app to appear unfrozen and allow a process to occur in the background, you will want to enter into some loading screen or limit the user's options on the main layout. This will allow activity to continue occurring but allow the user a smooth experience.
I've got a few activities. In the main activity I have a login screen, when the user presses the login button, a thread is started to show a progress dialog until the user has been authenticated. At this point i load the next activity which has several fields for the user to input data.
Here the user inputs some data and presses a button to process it. The data is passed to a new activity where the data is actually processed and displayed. This is where i create the new thread and where it's crashing when i call thread.start(); and I have no idea why this is happening.
Both activities are implementing Runnable.
I'm using the same code below to create and call thread.start() in the button press of the first activity and the onCreate method of the last one:
pd = ProgressDialog.show(search_results.this, "", "Searching...", true, false);
Thread thread = new Thread(this);
thread.start();
I'm using the same code below as well to handle the threads for both as well.
public void run() {
handler.sendEmptyMessage(0);
}
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
pd.dismiss();
}
};
Am I missing something? I don't really understand why it's crashing.
While I encourage people to use AsyncTask, it's not really needed, especially for simple things like progress/loading dialogs. That's not the problem here.
Your question and your code is confusing. I'm not sure which code goes where, on which activity, and I hope you're not leaving dialogs open between activities, trying to access them across them (it won't work, of course). Also, providing a Context to a Thread does not even compile (it's marked with errors at design time). To sum it all up, you didn't provide the Log entry. Sorry, I can't make sense of what you're doing or where the error is. We can only guess.
Below are one of the possible ways to do it with a Handler, a Runnable and a Thread. This was taken from the Developer Resource when I first learn how to use it:
1- You declare a Handler. Most people do this on the onCreate section to reuse it often:
Handler mHandler = new Handler();
2- When you need, you start a Thread:
new Thread() { public void run() {
mHandler.post(mLoadingData);
// ... do work
mHandler.post(mLoadingDataStop);
}}.start()
3- These are the Runnables that are posted to the Handler:
private final Runnable mLoadingData = new Runnable() {public void run() {
showDialog(LOADING_DIALOG); // In your case, show your custom dialog
}};
private final Runnable mLoadingDataStop = new Runnable() {public void run() {
dismissDialog(LOADING_DIALOG); // In your case, dismiss the dialog
}};
For a progress dialog, things need a bit more work (update the progress etc.), but for a loading dialog, you don't need to really mess with messages.
I had this same issue when developing for the tablet. After a certain API, I'm thinking 3.0 (sdk 11), Android enforces applications to run long running processes on a separate thread, otherwise it kills it. Logcat will confirm this.
I know you are using another thread, but that didn't work for me either. Try using AsyncTask. You can create a quick inner class that, in my opinion, is way easier than handling your own threads. AsyncTask has several functions that run on the UI thread and a couple that run on their own thread. This allows you to start a "Loading" user interface object on the user interface thread, process on the back end thread, and then when its done, it'll notify a user interface thread function.
You'll want to specifically look at override
onPreExecute()
doInBackground()
onPostExecute()