1. ホーム
  2. アンドロイド

[解決済み】警告。この AsyncTask クラスは静的であるべきで、さもなければリークが発生する可能性があります。

2022-03-31 09:04:14

質問

私のコードで以下のような警告が表示されます。

この AsyncTask クラスは static であるべきで、さもなければリークが発生する可能性があります (anonymous android.os.AsyncTask)

完全な警告は

この AsyncTask クラスは static であるべきで、さもなければリークが発生する可能性があります (anonymous android.os.AsyncTask) 静的なフィールドはコンテキストをリークします。 非静的な内部クラスは、その外部クラスへの暗黙の参照を持ちます。外側のクラスが例えばフラグメントやアクティビティである場合、この参照は、長く実行されるハンドラ/ローダ/タスクがアクティビティへの参照を保持することを意味し、ガベージコレクションを防ぐことができる。 同様に、アクティビティやフラグメントへの直接のフィールド参照は、これらの長く実行されるインスタンスから、リークを引き起こす可能性があります。 ViewModel クラスは、決して Views やアプリケーション以外の 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クラスがActivityを強く参照していると、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 チュートリアルはまだ対応していません( こちら , こちら , こちら そして こちら ).
  • また AsyncTask はトップレベルクラスです。静的な内部クラスは、Javaでは基本的にトップレベルクラスと同じです。
  • アクティビティそのものは必要ないが、コンテキストは欲しいという場合(たとえば、アクティビティに 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
        }
    }
}