警告: この AsyncTask クラスは静的である必要があります。そうでないと、リークが発生する可能性があります。質問する

警告: この AsyncTask クラスは静的である必要があります。そうでないと、リークが発生する可能性があります。質問する

コード内に次のような警告が表示されます:

この 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();

これを修正するにはどうすればいいでしょうか?

ベストアンサー1

静的な内部 AsyncTask クラスの使用方法

リークを防ぐために、内部クラスを静的にすることができます。ただし、その場合の問題は、アクティビティの UI ビューやメンバー変数にアクセスできなくなることです。 への参照を渡すことはできますContextが、メモリ リークの同じリスクが発生します。(Android は、AsyncTask クラスに強い参照がある場合、アクティビティが閉じた後にアクティビティをガベージ コレクションできません。) 解決策は、アクティビティ (または必要なもの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チュートリアルでは、まだこの問題を扱っていません(ここここここ、 そしてここ)。
  • トップレベル クラスの場合も同様の手順に従いますAsyncTask。静的内部クラスは、基本的に Java のトップレベル クラスと同じです。
  • Activity 自体は必要ないが、Context が必要な場合 (たとえば、 を表示する場合Toast)、アプリ コンテキストへの参照を渡すことができます。この場合、AsyncTaskコンストラクターは次のようになります。

    private WeakReference<Application> appReference;
    
    MyTask(Application context) {
        appReference = new WeakReference<>(context);
    }
    
  • この警告を無視して非静的クラスを使用するという議論もあります。結局のところ、AsyncTask は非常に短命 (長くても数秒) であり、終了すると Activity への参照を解放します。これそしてこれ
  • 素晴らしい記事です:コンテキストをリークする方法: ハンドラーと内部クラス

コトリン

Kotlinではinnerキーワードを含めないでください内部クラスの場合。これにより、デフォルトで静的になります。

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

おすすめ記事