コード内に次のような警告が表示されます:
この 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
}
}
}