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

此AsyncTask类应该是静态的,否则可能会发生泄漏(匿名android.os.AsyncTask)

完整的警告是:

此AsyncTask类应该是静态的,否则可能会发生泄漏(匿名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();

我该如何纠正这一点?

推荐答案

How to use a static inner AsyncTask class

为了防止泄漏,可以将内部类设置为静态.但问题是,您不再有权访问活动的UI视图或成员变量.您可以传入对Context的引用,但随后您将面临同样的内存泄漏风险.(如果AsyncTask类对活动有强引用,则Android无法在活动关闭后对其进行垃圾收集.)解决方法是对活动进行弱引用(或任何你需要的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;
        }
    }
}

Notes

  • 据我所知,这种内存泄漏的危险一直是真实的,但我只是在Android Studio 3.0中才开始看到警告.有很多主要的AsyncTask个教程仍然没有涉及到这一点(参见herehereherehere).
  • 如果你的AsyncTask名学生是顶级学生,你也会遵循类似的程序.静态内部类基本上与Java中的顶级类相同.
  • 如果您不需要活动本身,但仍然需要上下文(例如,显示Toast),则可以传入对应用程序上下文的引用.在本例中,AsyncTask构造函数将如下所示:

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
  • 有一些理由认为应该忽略这个警告,只使用非静电类.毕竟,AsyncTask的目的是非常短暂的(最长几秒钟),而且无论如何它都会在完成时释放对该活动的引用.请参见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相关问答推荐

如何允许我的应用程序在Android 10上运行,同时目标是API 33

Android深度链接配置中的URL片段匹配'

在Android Studio中陷入了不兼容的Gradle版本的循环

道查询注释部分房间表名称

无法安装后重新编译android代码'

有人能帮我在应用程序上使用模拟位置时避免被发现吗?我已经反编译并粘贴了一个代码,S小文件

如何使用Jetpack Compose实现此底表?

安卓Azure通讯聊天UI库导入?

布局可组合项如何具有可测量和约束参数?

使用 async 向网络发出并行请求并在supervisorScope中处理它们

单击按钮时不显示 Toast 消息

[Android][Room] 将密封类存储到 Room 数据库中

如何在组件之间导航

Jetpack Compose Arc 进度条动画(如何重启动画)

如何在 Android 应用程序未激活/未聚焦时显示视图?

无法 HEAD 'https://jcenter.bintray.com/com/facebook/react/react-native/maven-metadata.xml'

jetpack compose 中的可点击指示是什么?

Android 应用程序在启动时自动启动

使用协程访问数据库

未使用的内容填充参数