android - 警告 : This AsyncTask class should be static or leaks might occur

标签 android android-asynctask android-runonuithread

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

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();

我该如何纠正这个问题?

最佳答案

如何使用静态内部 AsyncTask 类

为防止泄漏,您可以将内部类设为静态。但是,问题在于您不再有权访问 Activity 的 UI View 或成员变量。您可以传入对 Context 的引用,但同样会面临内存泄漏的风险。 (如果 AsyncTask 类对它有强引用,Android 不能在 Activity 关闭后对它进行垃圾收集。)解决方案是对 Activity(或您需要的任何 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 教程仍然没有处理它(参见 herehereherehere )。
  • 如果您的 AsyncTask 是顶级类,您也将遵循类似的过程。静态内部类与 Java 中的顶级类基本相同。
  • 如果您不需要 Activity 本身但仍需要 Context(例如,显示 Toast),则可以传入对应用上下文的引用。在这种情况下,AsyncTask 构造函数如下所示:

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
  • 有一些争论可以忽略这个警告而只使用非静态类。毕竟,AsyncTask 的生命周期很短(最长几秒钟),无论如何它都会在完成时释放对 Activity 的引用。见 thisthis .
  • 优秀文章:How to Leak a Context: Handlers & Inner Classes

Kotlin

在 Kotlin 中只需 don't include the inner keyword对于内部类。这使其默认为静态。

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

关于android - 警告 : This AsyncTask class should be static or leaks might occur,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44309241/

相关文章:

java - 当用户离线时如何使用自定义无互联网连接可用布局?

java - 无法解决 xml 布局中的符号错误(Udacity Sunshine 第 2 课)

java - Android - 可运行对象解析为 runOnUiThread() 参数

android - 使用UI-Thread更新ImageView的位图

java - Android:确保 UI 操作在 UI 线程上完成的最佳实践

android - 未调用扩展 ImageView onDraw 方法

Android:如何每 3 分钟和 5 分钟将数据从服务器同步到我的应用程序?

android - 几次运行后,AsyncTask 在 onPreExecute 后停止

android - AsyncTask中如何处理isCancelled

android - android 中的 Webview 仅在 Samsung galaxy tab 2 中挂起