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

时间:2017-06-01 13:42:55

标签: 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();

我该如何纠正?

3 个答案:

答案 0 :(得分:459)

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

科特林

在Kotlin只为don't include the inner keyword内部班级。这使其默认为静态。

我对Kotlin还不太擅长,所以如果可以改进我的代码,请更正下面的代码:

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

答案 1 :(得分:43)

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

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

答案 2 :(得分:19)

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

  • Activity被销毁时,AsyncTaskstaticnon-static}仍在运行
  • 如果内部类是non-staticAsyncTask)类,则它将引用外部类(Activity)。
  • 如果对象没有引用指向它,Garbage Collected将释放它。如果对象未使用且Garbage Collected 无法释放它=>泄漏记忆

=&GT;如果AsyncTasknon-static,则Activity将不会发布已销毁的事件=&gt;泄漏

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

1)使用WeakReference之类的@Suragch回答
2)发送并删除Activity <{1}}

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