I have a complete button in page, when user click button, it will start an async task and pass the activity context into it, then finish the activity to dismiss the activity view.
What will happen if the activity be finished but the background task still need to use the context reference passed in? will it have any concerns? or have alternative/better way for this kind of situation.
any suggestions will be appreciated!
First of all, in this situation you better go for service instead of asynctask. In your case, Service should stop itself once task is finished.
Even if you go for asynctask, try to use application context that you can get from activity context as below:
activity_context.getApplicationContext();
The application context will be there even if activity is finished and also it will avoid memory leak.
And if you want to make the asynctask run safely even if activity is finished, then try not to update any UI in postExecute(..) method as it will run on the UI of the activity which is already finished leading to exception. Try to do only any background task inside doInBackground(...) which runs on different thread.
Hope this answers your question.
Related
I would like to run an async task when the app is closed out and only then. I tried putting it in the onDestroy() method but it only gets called sometimes (such as when the app is crashed). I also tried to put it on the onPause() method, but it also runs when the user tries to look at a different app rather than only when the app closes. Thanks!
Sincerely, Rocky
You should use Thread instead of AsyncTask, but if you want to use AsyncTask you'll need to do the following:
Don't update the UI in AsyncTask because onDestroy removes the resources and context for that activity. It will crash if you try to update them.
Use the application Context not the activity Context when you create your AsyncTask, because the activity Context is invalid after the activity is destroyed.
I have the following situation
I spawn a long running user thread (Thread) from an Activity.
I press soft back button. So, the Activity was destroyed.
I launch the same Activity again. Note, the previous launched Thread is still running.
In order for me to prevent the Activity from launching another same Thread while the previous Thread is still running, here is what I am doing
After I launch the Thread, I will store it into a static variable. So, next time when I try to launch a same thread, I will check against the liveness of previous thread through the static variable. Is this a good practice? What is the best practice to overcome this?
Note, the user thread is holding reference to the Activity which launched it. However, the Activity might destroyed when user press on soft back button. So, when I launch the new Activity again, the thread is not aware of that, and it is still referring to old Activity. So, when user thread try to access any members of the old Activity, crashes will happen as the Activity already being destroyed. What is the best practice to overcome this?
The best way to overcome this is to use an AsyncTask instead. If the Activity is destroyed while the task is executing, the AsyncTask (and its underlying Thread that is performing the operation) will continue its execution until doInBackground() has completed. If the Activity is null by the time onPostExecute is called, you won't run into any NullPointerExceptions for the reasons stated in this blog post.
If you want to immediately cancel the task if the user "backs out" of the Activity, you can call mTask.cancel() on your AsyncTask in your Activity's onDestroy method.
If you don't want to immediately cancel the task if the user "backs out" of the Activity, then your long-term operation doesn't sound like it is specific to any Activity instance. In this situation, it is often advised to use a Service instead.
If I have an AsyncTask started in an Activity by user interaction. The AsyncTask, when finished, will modify the UI and execute a Toast. Let's say that the user exits the Activity before the AsyncTask has finished. Can this cause problems as in Exceptions: I.e. could it happen that an UI element pointer goes null and that when the AsyncTask finishes it could cause runtime exceptions?
As it is now I've done a design where the Application class handles the AsyncTask and notifies the Activity through a BroadcastReceiver to do UI tasks if Activity still is around (i.e. more of an Observer pattern). Is this a "safer" design?
/ Henrik
I believe this does cause a problem. If the activity that created the AsyncTask is not around anymore, the an exception is thrown because the parent handler is not there anymore. The correct approach is to keep the reference of the AsyncTask in that activity, and capture onPause() event. In the pause event, I would cancel the AsyncTask and clean up if there is anything that needs to be cleaned up.
To answer your second question, it all depends on what is the requirement. If the requirement is for that task to still be around then yes you can attach the AsyncTask to the application. But it sounds like there is something that might be not correct here. You said if Activity still is around. If you don't need the task once the activity has disappeared then you might as well go with my original approach which is cancel the task and throw it away when the activity is paused.
Also, one final note. If you keep a reference to the activity around even after the activity has stopped, you will have a memory leak because that activity still has a reference that cannot be cleaned up until the task has completed.
This article sounds similar to what you are doing. If you really want to keep the task around then this seems like a good solution. I also found Android AsyncTask Context Terminated that might help you.
I use AsyncTask in several activities.
i know how to Override onBackPress().
But when an AsyncTask is running i want to give the user the ability to go back even though the asynctask is still running.
How can I do this?
Also does an asynctask still run when activity that initiated it is closed before it finishes?
AsyncTasks run on until they complete or are cancelled. So in your case (the user presses Back and you end the main activity), the AsyncTask will carry on.
If you did want to end the AsyncTask from onBackPressed, the best you can do is to call
myAsyncTask.cancel(true);
but all that does is call the task's onCancelled() which can set a cancel flag that can be checked (usually) in doInBackground() - and if it is set the task can return.
Note that using cancel(true) does not stop the task. The docs talk about "interrupting" the task for the true case, but all they mean is that onCancelled will be called while doInBackground is running - at which point the task has an opportunity to set a flag that doInBackground can inspect.
For the true case, if the task does not implement onCancelled, and/or it does not set/inspect a flag, the task will complete doInBackground, but onPostExecute(Object) is never invoked - which is equivalent to the false case.
The async task will continue to run even if your application was closed. You have to be careful with that since your task will leak your context (keep it in memory) if it has a reference to it (so the activity) as long as your task is still running. You can avoid that by referencing your activity by a WeakReference.
You can stop a running task with cancel(true). A cancel will let the task finish its doInBackground but will never call onPostExecute. You could interrupt your background routine by checking isCanceled() and so return earlier since the task was killed.
I deleted one half of my post!
I did so confuse interrupt with a thread stop! Using true will of course NOT stop the actual thread. Thanks a lot to Torid and his answer which proved me wrong! Big lesson lernt, thanks for that!
I have a class that fetches data in response to button presses in the main activity. Unfortunately, I keep running into problems because this class is not an Activity or a Service. For example, without a Context I cannot translate a resource id into a string:
getString(R.string.example_string); // Doesn't work
Should I make this class into a Service and have the main Activity stop the class when it is closed? Should I pass the Context from the Activity into this class like this?
MyClass c = new MyClass(this);
Or is there some better way to handle this problem?
This issue also comes up when I try to send a Toast from this class.
Update: Erich and Janusz pointed me in the direction of the AsyncTask class which works perfectly, except that it creates a new thread and never kills that thread. This means that ever time the user presses a button, another thread is added and the old ones just sit there.
If you have a background action whose lifecycle is decoupled from your activity, I would use a Service. In that case, the Service will have its own Context, so you won't need to pass it in. If, however, you need to perform a background action in response to a UI event (and optionally post the results back into the UI thread), I would recommend you use an AsyncTask.
I agree with Erich, if you only have a something small like posting a change to a web backend or loading something from the phone memory to show it on screen use a Async Task. If the task will exit very "quick" (some seconds) you can make an anonymous class inside your activity. This will enable you to use a implicit reference to the outer activity inside the task and you can get your context from there.
If the task is running for a longer time you can pass down the context. If you are passing down the context try to not pass this from the activity use this.getApplicationContext() this will minimize the number of references to your activity and enable the garbage collector to clean up properly.