I read some questions here, some articles in Internet, but the question about memory leaks in AsyncTask isn't clear for me. Please, can you give me an advice?
Let's consider some situations:
1) AsyncTask is an inner class
I write MyAsyncTask for downloading small data from the server (<1 KB) in MyActivity code (not as static class). It will store an implicit reference to MyActivity instance. And if i'll start MyAsyncTask.execute(), then MyActivity instance cannot be Garbage Collected, until this AsyncTask will finish. So, if I'll rotate the screen during AsyncTask executing, then old MyActivity instance will be in memory - and it is memory leak.
What I decided to do: because of size of my data for downloading, I will cancel my AsyncTask in onDestroy() method in MyActivity. In this way, I have such code of MyActivity:
public class MyActivity extends Activity {
//views and constants
private MyAsyncTask air;
private ProgressDialog progressDialog;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.account_info_layout);
progressDialog = new ProgressDialog(this);
//findViewById, etc.
}
@Override
protected void onStart() {
super.onStart();
air = new MyAsyncTask();
air.execute();
}
@Override
protected void onDestroy() {
if (air.getStatus() == AsyncTask.Status.RUNNING) {
air.cancel(true);
}
air = null;
super.onDestroy();
}
class MyAsyncTask extends AsyncTask<Void, Void, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
UserData.refreshTimer();
if (!progressDialog.isShowing())
progressDialog.show();
}
@Override
protected String doInBackground(Void... params) {
//GET request
return result;
}
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
//handle results
progressDialog.dismiss();
}
}
}
So, if my activity instance is destroyed, I cancel my async task, and create new instance in onStart(). Will it produce memory leaks, or can it produce IllegalArgumentException/NullPointerException because of progressDialog instance? I suppose, it will not produce any exceptions, because if I cancel the AsyncTask, onPostExecute() will not be called.
2) AsyncTask in own file
The next case is when I write MyAsyncTask in other file, and pass in constructor Context instance. Will such approach produce memory leaks, if I'll store Context as WeakReference? And is it correct idea to cancel AsyncTask in onDestroy() method in calling Activity to avoid IllegalArgumentException/NullPointerException during onPostExecute() method? Or, other way to avoid these exceptions is to check my Context variable for null.
Other approaches: I've heard about Otto library, about using retained Fragments, but now I want to understand these questions. If somebody knows - please, answer.
Cancelling is a good way to solve your memory leak. You might want to consider cancelling in onStop though, since you set up a new task in onStart. You might want to combine this with dismissing the progressDialog in onStop, since you're cancelling the task.
If you cancel the task, you will not cause a memory leak. If you don't, you might cause a temporary memory leak. You could for example solve that by constructing the new Java file with a context.getApplicationContext() instead of normal getContext / this (Activity). Then it will not be tied to the activity but to the application (the application survives orientation change). You however won't be able to access the dialog in onPostExecute(). Instead you could use a callback to a listener if you want. Make the activity implement the listener (and detach it onStop). But cancelling is a fine approach as well.
By the way cancelling an async task doesn't mean that it will immediately be cancelled. I recommend you to switch to Intent Services, Handlers or if possible to RXJava.
Unlike async tasks where you may need to nest async tasks to execute multiple executions, with RXJava you could chain those executions, apply filters and different transformations and you can run that code on a worker thread.