首页 文章

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

提问于
浏览
154

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

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

我该如何纠正?

3 回答

  • 37

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

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

  • 14

    如何使用静态内部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;
            }
        }
    }
    

    注意事项

    • 据我所知,这种类型的内存泄漏危险一直都是真的,但我才开始在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

  • 357

    This AsyncTask class should be static or leaks might occur because

    • Activity 被销毁时, AsyncTaskstaticnon-static )仍然在运行

    • 如果内部类是 non-staticAsyncTask )类,它将引用外部类( Activity ) .

    • 如果对象没有引用指向它, Garbage Collected 将释放它 . 如果一个对象未使用且 Garbage Collected can not 释放它=>泄漏内存

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

    Solution for update UI after make AsyncTask as static class without leak

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

相关问题