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

Key*_*vat 232 android android-asynctask android-runonuithread

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

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

完整的警告是:

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

我该如何纠正?

Sur*_*gch 513

如何使用静态内部AsyncTask类

为了防止泄漏,可以使内部类静态.但问题是,您无法再访问Activity的UI视图或成员变量.你可以传入一个引用,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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

笔记

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

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 有一些参数可以忽略这个警告,只使用非静态类.毕竟,AsyncTask旨在非常短暂(最长几秒钟),并且无论如何它将在活动结束时释放对Activity的引用.看到这个这个.
  • 优秀的文章:如何泄漏上下文:处理程序和内部类

  • +1.这是我见过的最好,最干净的解决方案!只是,如果你想在onPostExecute方法中修改UI,你还应该检查Activity是否被销毁:activity.isFinishing() (5认同)
  • @Sunny,传入对Fragment的引用而不是Activity.您可以取出`activity.isFinishing()`检查,并可能用`fragment.isRemoving()`检查替换它.不过,我最近没有对片段做过多少工作. (2认同)

Ana*_*and 56

非静态内部类包含对包含类的引用.当您声明AsyncTask为内部类时,它可能比包含Activity类更长寿.这是因为对包含类的隐式引用.这将防止活动被垃圾收集,从而导致内存泄漏.

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

  • 那么使用AsyncTask有什么意义呢?如果在Thread的run方法中最后运行新的Thread和handler.post或view.post(更新UI)更容易.如果AsyncTask是静态或顶级类,则很难从中访问所需的变量/方法 (41认同)
  • @Anand请删除此答案,以便/sf/answers/3231635641/上更有用的答案可以在顶部. (15认同)
  • 没有提供使用它的正确方法的代码.我曾尝试过将静态放在那里,但会出现更多警告和错误 (7认同)
  • @KeyurNimavat我想你可以传递一个弱的参考你的活动 (3认同)
  • 解决方案在于警告本身。使用静态嵌套类或顶级类。 (2认同)

Pha*_*inh 21

此类AsyncTask应该是静态的,否则可能会发生泄漏

  • 什么时候Activity被摧毁,AsyncTask(两个staticnon-static)仍在运行
  • 如果内部类是non-static(AsyncTask)类,它将引用外部类(Activity).
  • 如果一个对象没有引用指向它,Garbage Collected将释放它.如果一个对象未​​使用且Garbage Collected 无法释放它=>泄漏内存

=>如果AsyncTasknon-static,Activity将不会释放它被销毁的事件=>泄漏

将AsyncTask作为静态类而不泄漏后的更新UI解决方案

1)使用WeakReference@Suragch回答
2)发送和删除Activity对(from)的引用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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Suragch你的链接声明,虽然不能保证调用onDestroy,但唯一的情况是系统终止进程,所以无论如何都会释放所有资源.所以,不要在这里做保存,但你可以在这里做资源发布. (5认同)
  • [`onDestroy()`不保证每次都被调用](https://developer.android.com/reference/android/app/Activity#onDestroy%28%29) (2认同)
  • @AngeloFuchs,感谢您指出。 (2认同)
  • 在非静态 AsyncTask 用例的情况下,为什么我们不能将 AsyncTask 实例变量设置为 NULL,类似于此。尽管 AsyncTask 正在运行,这不会告诉 GC 释放 Activity 吗? (2认同)