警告:这个AsyncTask类应该是静态的,否则可能发生泄漏

我在我的代码中得到一个警告,指出:

这个AsyncTask类应该是静态的或者可能发生泄漏(anonymous android.os.AsyncTask)

完整的警告是:

这个AsyncTask类应该是静态的或可能发生泄漏(anonymous android.os.AsyncTask)一个静态字段会泄漏上下文。 非静态内部类对其外部类具有隐式引用。 如果该外部类是例如片段或活动,则该引用意味着长时间运行的处理程序/加载程序/任务将持有对该活动的引用,从而防止垃圾收集。 同样,从这些较长运行的实例直接引用活动和片段可能会导致泄漏。 ViewModel类不应该指向视图或非应用程序上下文。

这是我的代码:

new AsyncTask<Void,Void,Void>(){ @Override protected Void doInBackground(Void... params) { runOnUiThread(new Runnable() { @Override public void run() { mAdapter.notifyDataSetChanged(); } }); return null; } }.execute(); 

我该如何纠正?

Solutions Collecting From Web of "警告:这个AsyncTask类应该是静态的,否则可能发生泄漏"

非静态内部类拥有对包含类的引用。 当您将AsyncTask声明为内部类时,它可能比包含的Activity类生存得更长。 这是因为隐式引用了包含类。 这将防止活动被垃圾收集,因此内存泄漏。

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

如何使用静态内部AsyncTask类

为了防止泄漏,你可以使内部类是静态的。 但是,问题在于,您不再有权访问活动的UI视图或成员variables。 您可以传递对Context的引用,但是运行内存泄漏的风险相同。 (如果AsyncTask类有强引用,那么Android在closures后不能垃圾收集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) return; // modify the activity's UI TextView textView = activity.findViewById(R.id.textview); textView.setText(result); // access Activity member variables activity.mSomeMemberVariable = 321; } } } 

笔记

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

     private WeakReference<Application> appReference; MyTask(Application context) { appReference = new WeakReference<>(context); } 
  • 有一些观点忽略这个警告,只是使用非静态类。 毕竟,AsyncTask意图是非常短暂的(最长几秒钟),当它完成时它将释放它对Activity的引用。 看到这个和这个 。
  • 优秀的文章: 如何泄漏上下文:处理程序和内部类

每当你发现自己在AsyncTask中做了<Void, Void, Void> ,那么你可能就没有按照它被使用的方式来使用它。 如果你执行正确的钩子,你可以一起避免这个问题。 你的例子调用runOnUiThread ,这是你在这个例子中看起来唯一的Activity依赖。 AsyncTask已经提供了在UI线程上运行动作的方法。 下面是你应该做的一个简短的概要:

  1. 创build一个静态AsyncTask,或创build一个活动外的具体版本。
  2. 传递使用task.execute(Params...)执行作业所需的task.execute(Params...)
  3. 使用参数在doInBackground(Params...) ,返回结果而不是返回null
  4. 使用你的结果并在onPostExecute(Result) (或onProgressUpdate(Progress...) )上执行UI更新,因为这些都在UI线程上运行(所以你不需要调用runOnUiThread )。

Android文档有一个很好的例子,说明如何使用AsyncTask框架。

在旁边注意:如果你的mAdapter是一个适配器连接到某种AdapterView(即ListView等),并且你正在使用这个任务来获取视图的数据,那么我强烈推荐以下模式来避免竞争条件:

  1. doInBackground获取并返回数据
  2. onPostExecute (记住,这是在你的UI线程上运行)更新UI使用你的结果数据的数据集,然后调用notifyDataSetChanged