How to use a static inner AsyncTask class
ref:https://stackoverflow.com/a/46166223
To prevent leaks, you can make the the inner class static. The problem with that, though, is that you no longer have access to the Activity's UI views or member variables. You can pass in a reference to the
Context
but then you run the same risk of a memory leak. (Android can't garbage collect the Activity after it closes if the AsyncTask class has a strong reference to it.) The solution is to make a weak reference to the Activity (or whatever Context
you need).public class MyActivity extends AppCompatActivity {
int mSomeMemberVariable = 123;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// start the AsyncTask, passing the Activity context
// in to a custom constructor
new MyTask(this).execute();
}
private static class MyTask extends AsyncTask<Void, Void, String> {
private WeakReference<MyActivity> activityReference;
// only retain a weak reference to the activity
MyTask(MyActivity context) {
activityReference = new WeakReference<>(context);
}
@Override
protected String doInBackground(Void... params) {
// do some long running task...
return "task finished";
}
@Override
protected void onPostExecute(String result) {
// get a reference to the activity if it is still there
MyActivity activity = activityReference.get();
if (activity == null) return;
// modify the activity's UI
TextView textView = activity.findViewById(R.id.textview);
textView.setText(result);
// access Activity member variables
activity.mSomeMemberVariable = 321;
}
}
}
Notes
- As far as I know, this type of memory leak danger has always been true, but I only started seeing the warning in Android Studio 3.0. A lot of the main
AsyncTask
tutorials out there still don't deal with it (see here, here, here, and here). - You would also follow a similar procedure if your
AsyncTask
were a top-level class. A static inner class is basically the same as a top-level class in Java. - If you don't need the Activity itself but still want the Context (for example, to display a
Toast
), you can pass in a reference to the app context. In this case theAsyncTask
constructor would look like this:private WeakReference<Application> appReference; MyTask(Application context) { appReference = new WeakReference<>(context); }
- There are some arguments out there for ignoring this warning and just using the non-static class. After all, the AsyncTask is intended to be very short lived (a couple seconds at the longest), and it will release its reference to the Activity when it finishes anyway. See this and this.
- Excellent article: How to Leak a Context: Handlers & Inner Classes
沒有留言:
張貼留言