警告:这个 AsyncTask 类应该是静态的,否则可能会发生泄漏 [英] Warning: This AsyncTask class should be static or leaks might occur

查看:36
本文介绍了警告:这个 AsyncTask 类应该是静态的,否则可能会发生泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的代码中收到一条警告,指出:

I am getting a warning in my code that states:

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

This AsyncTask class should be static or leaks might occur (anonymous android.os.AsyncTask)

完整的警告是:

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

This AsyncTask class should be static or leaks might occur (anonymous 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();

我该如何纠正?

推荐答案

如何使用静态内部 AsyncTask 类

为了防止泄漏,您可以将内部类设为静态.但是,问题在于您无法再访问 Activity 的 UI 视图或成员变量.您可以传入对 Context 的引用,但是您会面临相同的内存泄漏风险.(如果 AsyncTask 类对它有强引用,Android 不能在 Activity 关闭后对其进行垃圾收集.)解决方案是对 Activity(或您需要的任何 Context)进行弱引用.

How to use a static inner AsyncTask class

To prevent leaks, you can make the inner class static. The problem with that, though, is that you no longer have access to the Activity's UI views or member variables. You can pass in a reference to the Context but then you run the same risk of a memory leak. (Android can't garbage collect the Activity after it closes if the AsyncTask class has a strong reference to it.) The solution is to make a weak reference to the Activity (or whatever Context you need).

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 教程仍然没有处理它(参见 这里这里此处此处).
  • 如果您的 AsyncTask 是顶级类,您也将遵循类似的过程.静态内部类与 Java 中的顶级类基本相同.
  • 如果您不需要 Activity 本身但仍需要 Context(例如,显示 Toast),您可以传入对应用程序上下文的引用.在这种情况下,AsyncTask 构造函数看起来像这样:

    Notes

    • As far as I know, this type of memory leak danger has always been true, but I only started seeing the warning in Android Studio 3.0. A lot of the main AsyncTask tutorials out there still don't deal with it (see here, here, here, and here).
    • You would also follow a similar procedure if your AsyncTask were a top-level class. A static inner class is basically the same as a top-level class in Java.
    • If you don't need the Activity itself but still want the Context (for example, to display a Toast), you can pass in a reference to the app context. In this case the AsyncTask constructor would look like this:

      private WeakReference<Application> appReference;
      
      MyTask(Application context) {
          appReference = new WeakReference<>(context);
      }
      

    • 有一些理由可以忽略此警告并仅使用非静态类.毕竟,AsyncTask 的生命周期很短(最长几秒钟),无论如何它都会在完成时释放对 Activity 的引用.请参阅这个.
    • 优秀文章:如何泄漏上下文:处理程序&内部类
    • 在 Kotlin 中,只需 不要为内部类包含 inner 关键字.这使其默认为静态.

      In Kotlin just don't include the inner keyword for the inner class. This makes it static by default.

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

      这篇关于警告:这个 AsyncTask 类应该是静态的,否则可能会发生泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆