コードに次のような警告が表示されます。
このAsyncTaskクラスは静的にする必要があります。そうしないとリークが発生する可能性があります(匿名Android.os.AsyncTask)
完全な警告は次のとおりです。
このAsyncTaskクラスは静的にする必要があります。そうしないとリークが発生する可能性があります(anonymous Android.os.AsyncTask)静的フィールドはコンテキストをリークします。非静的内部クラスは、それらの外部クラスへの暗黙の参照を持ちます。その外部クラスがたとえばFragmentまたはActivityの場合、この参照は、長時間実行されるハンドラ/ローダ/タスクがアクティビティへの参照を保持し、それによってガベージコレクションが行われないようにします。同様に、これらの長時間実行されているインスタンスからのアクティビティおよびフラグメントへの直接フィールド参照は、リークを引き起こす可能性があります。 ViewModelクラスは、ビューまたはアプリケーション以外のコンテキストを指すべきではありません。
これは私のコードです:
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は閉じた後にアクティビティをガベージコレクションできません。)解決策は、アクティビティ(または必要な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;
}
}
}
AsyncTask
チュートリアルの多くはまだ対処していません( here 、 here 、 here 、および ここ )。AsyncTask
が最上位クラスの場合も、同様の手順に従います。静的内部クラスは、基本的にJavaのトップレベルクラスと同じです。アクティビティ自体は必要ないが、コンテキストが必要な場合(たとえば、Toast
を表示する場合)、アプリコンテキストへの参照を渡すことができます。この場合、AsyncTask
コンストラクターは次のようになります。
private WeakReference<Application> appReference;
MyTask(Application context) {
appReference = new WeakReference<>(context);
}
Kotlinでは、内部クラスに対して inner
キーワードを含めないでください だけです。これにより、デフォルトで静的になります。
私はまだKotlinが得意ではないので、改善できる場合は以下のコードを修正してください。
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
クラスは静的であるべきで、そうでないとリークが発生する可能性があります
Activity
が破壊されても、AsyncTask
(static
またはnon-static
の両方)はまだ実行中ですnon-static
(AsyncTask
)クラスの場合、それは外部クラス(Activity
)への参照を持ちます。Garbage Collected
はそれを解放します。オブジェクトが未使用でGarbage Collected
できない場合 release =>>メモリリーク=> AsyncTask
がnon-static
の場合、Activity
は破棄されたイベントを解放しません=> leak
AsyncTaskをリークのない静的クラスにした後のUIの更新のためのソリューション
1)@Suragch answerのようにWeakReference
を使用
2)Activity
への(からの)AsyncTask
参照の送信および削除
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);
}
}
}