androidandroid-asynctaskandroid-runonuithread

Warning: This AsyncTask class should be static or leaks might occur


I am getting a warning in my code that states:

This AsyncTask class should be static or leaks might occur (anonymous android.os.AsyncTask)

The complete warning is:

This AsyncTask class should be static or leaks might occur (anonymous android.os.AsyncTask) A static field will leak contexts. Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected. Similarly, direct field references to activities and fragments from these longer running instances can cause leaks. ViewModel classes should never point to Views or non-application Contexts.

This is my code:

 new AsyncTask<Void,Void,Void>(){

        @Override
        protected Void doInBackground(Void... params) {
            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    mAdapter.notifyDataSetChanged();
                }
            });

            return null;
        }
    }.execute();

How do I correct this?


Solution

  • How to use a static inner AsyncTask class

    To prevent leaks, you can make 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 || activity.isFinishing()) return;
    
                // modify the activity's UI
                TextView textView = activity.findViewById(R.id.textview);
                textView.setText(result);
    
                // access Activity member variables
                activity.mSomeMemberVariable = 321;
            }
        }
    }
    

    Notes

    Kotlin

    In Kotlin just don't include the inner keyword for the inner class. This makes it static by default.

    class MyActivity : AppCompatActivity() {
    
        internal var mSomeMemberVariable = 123
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            // start the AsyncTask, passing the Activity context
            // in to a custom constructor
            MyTask(this).execute()
        }
    
        private class MyTask
        internal constructor(context: MyActivity) : AsyncTask<Void, Void, String>() {
    
            private val activityReference: WeakReference<MyActivity> = WeakReference(context)
    
            override fun doInBackground(vararg params: Void): String {
    
                // do some long running task...
    
                return "task finished"
            }
    
            override fun onPostExecute(result: String) {
    
                // get a reference to the activity if it is still there
                val activity = activityReference.get()
                if (activity == null || activity.isFinishing) return
    
                // modify the activity's UI
                val textView = activity.findViewById(R.id.textview)
                textView.setText(result)
    
                // access Activity member variables
                activity.mSomeMemberVariable = 321
            }
        }
    }