My activity is finished while AsynchTask is inside doinBackground() method.
When it reaches postExecute() the activity used to update the UI is already destroyed,but its TextView is still accessible in onPostExecute().
Can any one explain how this is happening?
Android Activities have a specific lifecyle. When they go past their Destroy lifecycle state, they are no longer usable by Android and will be eventually garbage collected.
However, when you pass an Activity reference, or any widget reference for that matter, to an AsyncTask, this prevents the GC from dispossing the activity and its related objects (e.g. view and widgets). Furthermore, your AsyncTask, which is executed in a separate thread with its own state, will hold a reference one or more objects that are invalid. And since you hold a reference in your AsyncTask, they cannot be freed by the GC.
That is why you must be very careful with using AsyncTasks with Activity reference. Also, refrain from using AsyncTasks for updating your UI, there are other ways of doing this.
Before starting new activity call cancel on AsyncTask
asynTask.cancel(true)
and in your asynctasks doInBackground method check if AsyncTask is cancelled or not
#Override
protected String doInBackground(String... urls) {
for( !isCancelled()) {
//do some stuff
}
}
Your activity can get destroyed for several reasons such as
System purging your activity due to memory needs
You rotate the phone. Then the Activity gets killed and recreated
You explicitly navigate back from the Activity
Your logic needs to handle such scenarios when executing an AsyncTask. Once your Activity starts an AsyncTask, it gets executed in another thread and delivers the results back to your activity once it gets finished. Your activity might get killed while the background AsyncTask is doing the work. A good practice is to call the cancel(boolean) method once your activity gets killed (onDestroy) and if you don't wish to continue with the results of the AsyncTask.
The question expects an explaination on how it happens.
Think of this simple java code.
public class myClass{
public static void main(String[] args) {
int n=2;
MyThread t=new MyThread(n);
t.start();
n=4;
System.out.println("n from main "+n);
}
}
class MyThread extends Thread{
int n;
public MyThread(int n){
this.n=n;
}
#Override
public void run(){
try{
Thread.sleep(2000);
}catch(Throwable e){
System.out.println("error :"+e);
}
System.out.println("n from thread "+n);
}
}
And the output would be,
n from main 4
n from thread 2
This happens because the instance of the integer n you pass to the thread holds its value. This is the same thing that happens in your AsyncTask. This behavior will be changed if you make n a static variable and made a reference to it inside the new thread.
AsyncTask (same as standard java thread) doesn't respect lifecycle of the host Activity. It's up to the developer to implement proper cancelling (as #mrDroid showed).
You may use AsyncTaskLoader, so you don't have to write code that handles activity configuration changes.
Be aware that even without direct reference to the host Activity (as mentioned #George Metaxas) a non-static inner AsyncTask may lead to memory leaks (because inner class holds implicit reference to the outer class).
Related
I have some fragments loaded in a ViewPager, where each "page" is loaded from a row in a cursor. Each fragment shows an image (JPEG) on the device. When the user dismisses the fragment (i.e swipe/page change, hits back/up, or just closes the app entirely) I want to invoke a method which opens the JPEG file for writing and does an update of its metadata. The actual work is eventually handled by the Apache Commons Imaging library.
I've implemented this by invoking my saveToFile() method from each fragment's life cycle onStop() handler. Does this mean the entire file operation ends up running on the UI thread? Should I definitely set up an AsyncTask for this?
Say the file write for some reason suddenly (for some jpeg) should take a long time, eg 2 minutes. What would then happen? Would the UI just wait (freeze) at this page/fragment before resuming? Or would the process (write to file) carry on "in the background" somehow? Or would the process just be killed, stopped short mid-process?
The way I have this wired up currently (onStop invoking saveToFile(), which calls up the imaging library and then updates the file) seems to work as it should. Even if I end the app, I still see my Toast text popping up, saying "Writing to file..." Seemingly, the process is never disturbed, and I can't say I'm experiencing any UI lag.
onStop() handler. Does this mean the entire file operation ends up
running on the UI thread? Should I definitely set up an AsyncTask for
this?
YES
An AsyncTask has several parts: a doInBackground method that does, in fact, run on a separate thread and the onPostExecute method that runs on the UI thread.
You can also use some sort of observer pattern such as EventBus to run async and post results to the UI.
Say the file write for some reason suddenly (for some jpeg) should
take a long time, eg 2 minutes. What would then happen? Would the UI
just wait (freeze)
The application will crash because Android will forcefully close it due to ANR (Application Not Responding).
Refer to the official documentation for details on this: https://developer.android.com/training/articles/perf-anr.html
Android applications normally run entirely on a single thread by
default the "UI thread" or "main thread"). This means anything your
application is doing in the UI thread that takes a long time to
complete can trigger the ANR dialog because your application is not
giving itself a chance to handle the input event or intent broadcasts.
Therefore, any method that runs in the UI thread should do as little
work as possible on that thread. In particular, activities should do
as little as possible to set up in key life-cycle methods such as
onCreate() and onResume(). Potentially long running operations such as
network or database operations, or computationally expensive
calculations such as resizing bitmaps should be done in a worker
thread (or in the case of databases operations, via an asynchronous
request).
The most effective way to create a worker thread for longer operations
is with the AsyncTask class.
Here is what I recommend though. Use the above mentioned, EventBus and create a BaseActivity which will automatically save the data for you onClose() by firing an event that runs Async. You then extend that base activity in all the places where you need autosave capabilities.
Here's what I mean with an example that uses EventBus.
public abstract class BaseActivity extends Activity{
#Override
protected void onResume(){
if(!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
super.onResume();
}
#Override
protected void onDestroy() {
if(EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
super.onDestroy();
}
#Override
protected void onStop() {
super.onStop();
//We fire event and pass the current parent class that inherited this base.
EventBus.getDefault().post(new EventBusProcessMySaveData(this.getClass()));
}
}
//Your model class to use with EventBus
public final class EventBusProcessMySaveData{
private final Class className;
public EventBusProcessMySaveData(final Class className){
this.className = className;
}
public Class getClassName(){
return this.className;
}
}
public class MyMainActivity extends BaseActivity{
//Do you standard setup here onCreate() and such...
//Handle Event for Saving Operation, async.
//This will fire everytime theres an onClose() IN ANY activity that
//extends BaseActivity, but will only process if the class names match.
#Subscribe(threadMode = ThreadMode.ASYNC)
public void methodNameDoesNotReallyMatterHere(final EventBusProcessMySaveData model){
//We make sure this is the intended receiving end by comparing current class name
//with received class name.
if(model.getClassName().equals(this.getClass())){
//Do whatever you need to do that's CPUintensive here.
}
}
}
I have an app which makes rest calls and represents the data in a GridView.
The main activity uses two fragements, a UI Fragment and a retained worker fragment. The worker fragment contains an inner AsyncTask that performs the REST calls.
Everything was working great I had no crashes etc, until I tried to do an update on a regular interval.
In order to perform the interval I added a handler. The handler is a member of the worker fragment. Within the worker fragment I have the LoadAPI method which calls the asynctask.
public void loadAPI(){
final String myURL = "http://x.com/"
handler.post(new Runnable() {
public void run(){
new APITask().execute(myURL);
handler.postDelayed(this,10000);
}
});
}
The problem is when there is a config change, my activity is destroyed, and onPostExecute crashes when it references the main activities listener. But I have implemented onAttach in my worker fragment. onCancel seems an ugly option, as multiple asynctasks can get called, and I don't have a named instance. I suppose I could keep a list of asynctasks in the worker fragment and cancel them onDestroy (It's ok to lose the latest update) but I think I am doing something wrong here. What is frustrating is the worker frag and asynctask were working fine until I did a continuous polling and I can't figure out why the introduction of the handler is causing this behavior.
My api tasks will take anywhere from 50 milisecond to 5 seconds.
Retained fragments will not get recreated during config changes like rotations, but they will still get destroyed and recreated when system will kill your app because it is in background for example.
so to be safe you should at least:
Never put your async task inside fragment as inner class, if you want to have it inside your fragment class body, then make it static. Otherwise AsyncTask will keep internal reference to your fragment and will prevent it from being garbage collected, and whats more bad is that in onPostExecute you will access your destroyed fragment.
When creating your asynctask, pass a reference to fragment to it, and store this reference inside WeakReference<>, ex:
private WeakReference<DataFragment> fragmentRef;
then in onPostExecute, before using fragment check if fragmentRef.get() returns non-null.
If you need continuous data updates, then consider using IntentService, or even WakefulIntentService. It will be slightly more difficult to report data updates progress from service to activity - but it can be managed with broadcasts. Also, if you want to do data updates from background then you will have to use service, together with alarms - then WakeFullIntentService (or regular service) is the way to go: https://github.com/commonsguy/cwac-wakeful for further reading.
I know that AsyncTask are not preferred for long process. Their main objective is to take the load off from UI thread and do stuff in background. Later on completion update the respective UI thread.
I am aware of the memory leaks i.e when UI needs to be updated after doInBackground is done and there's a possibility that the activity is destroyed.
My question is can I use AsyncTask just like a simple Thread?
What happens to AsyncTask if Activity or Application dies, which started it?
Requirments
I dont need to update any UI.
I dont want my task to be associated with Activity(which starts it).
First Question :
Yes you can.Its totally depends on your logic.
Second Question :
The thread will be in the background though the application is killed by the user or by the system.
To resolve the second scenario use the following technique
Just make sure that you are finishing your AsyncTask before application or Activity closes
AsyncTask yourAsyncTask
#Override
public void onDestroy(){
//you may call the cancel() method but if it is not handled in doInBackground() method
if(yourAsyncTask!=null)
if (yourAsyncTask != null && yourAsyncTask.getStatus() != AsyncTask.Status.FINISHED)
yourAsyncTask.cancel(true);
super.onDestroy();
}
If you only need the 'doInBackground' just use a normal thread.
new Thread("threadName", new Runnable(){ #Override run(){ } }).start();
The whole reason to use an AsyncTask is to have the facilities of preExecute and postExecute, so you don't need to mess with handlers.
It remain started in background even the application is killed or crash.
First, a general note, as stated by the Android Docs:
AsyncTasks should ideally be used for short operations (a few seconds at the most). If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.
To answer your questions:
Yes - you can use Async task as if it were just a background thread - an Async task is merely a wrapper of Thread and Handler that allows the thread to seamlessly communicate with the UI thread. Warning! If you plan to update the UI thread, or otherwise reference an activity or fragment in the callbacks that reference the UI thread (i.e. onProgressUpdated and/or onPostExecute) you should explicitly check that the activity or fragment is still in a state from which it can be referenced and used. For example - here's the right and wrong way to do it when launching an AsyncTask from a fragment:
Create your task with a ref to the activity so you can do something when it's done:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
Fragment mFragment;
public DownloadFilesTask(Fragment fragment){
mFragment = fragment;
}
WRONG:
protected void onPostExecute(Long result) {
// if the fragment has been detached, this will crash
mFragment.getView().findView...
}
RIGHT:
protected void onPostExecute(Long result) {
if (mFragment !=null && mFragment.isResumed())
... do something on the UI thread ...
}
}
If the Activity dies while an AsyncTask is executed, it will continue to run. Using the techniques listed above, you can avoid crashes by checking the lifecycle of the context that started the task.
Finally, if you have a very long-running operation that does not require the UI thread at all, you should look into using a Service. Here's a blurb:
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use
My question is can I use AsyncTask just like a simple Thread?
Yes AsyncTask is android background thread , the task will be done in the background .
AsyncTask automatically creates a new Thread for you, so everything you do in doInBackground() is on another thread.
What happens to AsyncTask if Activity or Application dies, which
started it?
The AsyncTask is related to application , if application destroy or finish then all related AsyncTask of that application will be terminated .
After an AsyncTask finishes, I usually do one of the following;
Call a method on a callback object/interface that I define in the activity and pass to My AsyncTask subclass constructor.
Provide the AsyncTask with a Handler and call myHandler.sendMessage() when the task is complete.
What I want to ask is what is considered best practice after the AsyncTask is complete. If the user has pressed the home button while the task is processing, the activity is no longer in the foregreound. As soon as the app tries some UI operation, as a response to the task beeing completed, the The OS throws a WindowManager$BadTokenException.
I can of course surround my UI code with a catch for BadTokenException. That seems hacky. Is there any way to tell if the activity is in front?
Maybe you will not like my answer, but I consider AsyncTask broken (for reasons like this one).
Edit: My initial answer recommended to use an IntentService and broadcast the result. This is as inefficient as sending a letter to yourself.
You can use an AsyncTaskLoader which works around the problems of AsyncTask, but the API of AsyncTaskLoader is far from perfect as well. In particular, you must ensure that loader id is unique, and be aware that results are cached for the id, not for the arguments. Also, the propagation of exception is as broken as with AsyncTask.
A more modern and safer way to approach the problem is to use Guava future.
It means you are using some where the context that is not appropriate. To clear you doubt about the exception see this Link.
Bad Token Exception
You can check if the activity is active or not. I usually make my AsyncTask subclass as static (to avoid memory leak) so I pass a reference of the activity (wrapped on a WeakReference, again to avoid memory leaks).
When onPostExecute is executing I do the necessary checks when using WeakReferences plus call Activity.isFinishing() for the activity, so I can check the activity is not in process of being destroy, to avoid execute UI changes on a dying Activity.
Define an object of your activity in your onStart() as a static member -
private static MyActivity mActivity = null;
public void onStart() {
mActivity = this;
}
public void onDestroy() {
mActivity = null;
}
Then on your AsyncTask method, do something like:
if (mActivity != null) {
mActivity.doSomething(); //make sure to use static member
}
say for example I have this code in my activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Thread cThread = new Thread(new Runner());
cThread.start();
}
private NotifyMe(){
//do something here
}
and this is my Runner class:
public class TCPClient implements Runnable {
public void run(){
//call NotifyMe() [THIS IS MY QUESTION]
}
}
I have a thread on my activity that runs the Runner Class.
Once the thread start, I would like to call the NotifyMe() function
that is located at the activity. Is this possible?
Please let me know if you don't understand my question.
You can add a Constructor to the TCPClient that takes a reference to the activity, change the notifyMe method to public and then call the notifyMe method on the activity object that is stored in the thread.
The problem you would get with this is that activities may be closed, paused, destroyed while your thread is running. To check if the activity is still active use the isFinishing() method from the activity.
This solution is somewhat dangerous if your activity uses a lot of memory because the reference to the activity in the thread will let the garbage collector not reclaim the memory used by the drawables of the UI in the activity etc. until the thread is executed and can be garbage collected as well. If your activity is not that heavy in memory that should be ok. If it is or if you want to access the data from the thread from multiple activities have a look at this question.
A more or less unrelated note if you have a very small thread that won't run the whole time your app is running use a AsyncTask. This will allow you to simply put a single operation into the background.