警告:此 AsyncTask class 应该是静态的,否则可能会发生泄漏

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

我在我的代码中收到一条警告:

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

完整的警告是:

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.

这是我的代码:

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

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

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

            return null;
        }
    }.execute();

我该如何纠正?

非静态内部 classes 持有对包含 class 的引用。当您将 AsyncTask 声明为内部 class 时,它可能比包含 Activity class 的寿命更长。这是因为对包含 class 的隐式引用。这将防止 activity 被垃圾收集,从而防止内存泄漏。

要解决您的问题,请使用静态嵌套 class 而不是匿名、本地和内部 class 或使用顶级 class.

如何使用静态内部 AsyncTask class

为防止泄漏,您可以使内部 class 静态化。但是,这样做的问题是您无法再访问 Activity 的 UI 视图或成员变量。您可以传递对 Context 的引用,但随后您 运行 有同样的内存泄漏风险。 (如果 AsyncTask class 对它有强引用,Android 无法在关闭后对 Activity 进行垃圾回收。)解决方案是对 [=51= 进行弱引用](或任何你需要的Context)。

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;
        }
    }
}

备注

  • 据我所知,这种类型的内存泄漏危险一直存在,但我只是在 Android Studio 3.0 中才开始看到警告。许多主要的 AsyncTask 教程仍然没有涉及它(参见 here, here, here, and here)。
  • 如果您的 AsyncTask 是顶级 class,您也会遵循类似的程序。静态内部 class 与 Java.
  • 中的顶级 class 基本相同
  • 如果不需要Activity本身,但又想要Context(比如显示一个Toast),可以传入对应用上下文。在这种情况下,AsyncTask 构造函数将如下所示:

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
  • 有一些关于忽略此警告并仅使用非静态 class 的争论。毕竟,AsyncTask 的生命期很短(最长几秒钟),并且无论如何它都会在完成时释放对 Activity 的引用。参见 this and
  • 优秀文章:How to Leak a Context: Handlers & Inner Classes

科特林

在 Kotlin 中,内部 class 仅 。这使其默认为静态。

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
        }
    }
}

这个AsyncTaskclass应该是静态的,否则可能会发生泄漏,因为

  • Activity被摧毁时,AsyncTaskstaticnon-static)仍然运行
  • 如果内部class是non-static(AsyncTask)class,它将引用外部class(Activity) .
  • 如果一个对象没有指向它的引用,Garbage Collected 将释放它。如果一个对象未​​被使用并且Garbage Collected 不能释放它=>泄漏内存

=> 如果 AsyncTasknon-staticActivity 不会释放它被销毁的事件 => leak

将 AsyncTask 设置为静态 class 无泄漏后更新 UI 的解决方案

1) 使用 WeakReference 就像@Suragch 回答
2) 发送和删除 Activity 对(来自)AsyncTask

的引用
public class NoLeakAsyncTaskActivity extends AppCompatActivity {
    private ExampleAsyncTask asyncTask;

    @Override 
    protected void onCreate(Bundle savedInstanceState) {
        ...

        // START AsyncTask
        asyncTask = new ExampleAsyncTask();
        asyncTask.setListener(new ExampleAsyncTask.ExampleAsyncTaskListener() {
            @Override
            public void onExampleAsyncTaskFinished(Integer value) {
                // update UI in Activity here
            }
        });
        asyncTask.execute();
    }

    @Override
    protected void onDestroy() {
        asyncTask.setListener(null); // PREVENT LEAK AFTER ACTIVITY DESTROYED
        super.onDestroy();
    }

    static class ExampleAsyncTask extends AsyncTask<Void, Void, Integer> {
        private ExampleAsyncTaskListener listener;

        @Override
        protected Integer doInBackground(Void... voids) {
            ...
            return null;
        }

        @Override
        protected void onPostExecute(Integer value) {
            super.onPostExecute(value);
            if (listener != null) {
                listener.onExampleAsyncTaskFinished(value);
            }
        }

        public void setListener(ExampleAsyncTaskListener listener) {
            this.listener = listener;
        }

        public interface ExampleAsyncTaskListener {
            void onExampleAsyncTaskFinished(Integer value);
        }
    }
}