android this - 警告:此AsyncTask類應該是靜態的或可能發生洩漏




class should (4)

我在我的代碼中發出警告,指出:

這個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();

我該如何糾正?


Answers

非靜態內部類擁有對包含類的引用。 當您將AsyncTask聲明為內部類時,它可能會比包含的Activity類生存得更長。 這是因為對含有類的隱式引用。 這將阻止活動被垃圾收集,從而導致內存洩漏。

要解決您的問題,請使用靜態嵌套類而不是匿名,本地和內部類或使用頂級類。


如何使用靜態內部AsyncTask類

為了防止洩漏,您可以使內部類為靜態。 但是,問題在於你不再有權訪問活動的UI視圖或成員變量。 你可以傳入一個對Context的引用,但是你運行內存洩漏的風險相同。 (如果AsyncTask類有強引用,那麼Android在關閉後無法收集活動。)解決方案是對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教程仍然沒有涉及它(參見herehere ,以及here )。
  • 如果你的AsyncTask是一個頂級的類,你也會遵循一個類似的過程。 靜態內部類與Java中的頂級類基本相同。
  • 如果您不需要活動本身,但仍需要上下文(例如,顯示Toast ),則可以傳遞對應用上下文的引用。 在這種情況下, AsyncTask構造函數看起來像這樣:

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
  • 有一些觀點忽略了這個警告,只是使用非靜態類。 畢竟,AsyncTask的使用壽命非常短(最長時間為幾秒),並且無論如何都會釋放對Activity的引用。 看到thisthis
  • 優秀的文章: androiddesignpatterns.com/2013/01/…

這個AsyncTask類應該是靜態的,否則可能會發生洩漏,因為

  • Activity被銷毀時, AsyncTaskstatic non-static )仍在運行
  • 如果內部類non-staticAsyncTask )類,它將引用外部類( Activity )。
  • 如果一個對像沒有引用指向它, Garbage Collected將釋放它。 如果一個對像沒有被使用, Garbage Collected 就不能釋放它,>洩漏內存

=>如果AsyncTask non-static ,則Activity不會釋放它被銷毀=>洩漏的​​事件

將AsyncTask作為靜態類而不洩漏之後更新UI的解決方案

1)使用WeakReference像@Suragch的答案
2)發送並從AsyncTask移除Activity引用

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

以下是使用弱引用和靜態處理程序類來解決問題的一般示例(如Lint文檔中所推薦的):

public class MyClass{

  //static inner class doesn't hold an implicit reference to the outer class
  private static class MyHandler extends Handler {
    //Using a weak reference means you won't prevent garbage collection
    private final WeakReference<MyClass> myClassWeakReference; 

    public MyHandler(MyClass myClassInstance) {
      myClassWeakReference = new WeakReference<MyClass>(myClassInstance);
    }

    @Override
    public void handleMessage(Message msg) {
      MyClass myClass = myClassWeakReference.get();
      if (myClass != null) {
        ...do work here...
      }
    }
  }

  /**
   * An example getter to provide it to some external class
   * or just use 'new MyHandler(this)' if you are using it internally.
   * If you only use it internally you might even want it as final member:
   * private final MyHandler mHandler = new MyHandler(this);
   */
  public Handler getHandler() {
    return new MyHandler(this);
  }
}




android android-asynctask android-runonuithread