Androidで非同期処理【Android、Java】
AsyncTaskを使って非同期処理
適当なメソッドからAsyncTaskを直接呼び出して、非同期処理を実行しています。これだとThis AsyncTask class should be static or leaks might occur
というWarningが出てしまいました。
public class MainActivity extends AppCompatActivity { /* 非同期処理の呼び出し */ public void asyncTaskSample() { new AsyncTask<Integer, Integer, Integer>() { @Override protected Integer doInBackground(Integer ...params) { System.out.println("Hello World!!"); return 0; } }.execute(0); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); asyncTaskSample(); } }
このエラーの原因を他のサイトから引用します。
- 非staticな内部クラスは暗黙に親オブジェクト(Activityオブジェクト)への参照を持っている。
- AsyncTaskが内部的に利用しているスレッドが生きている限りこのオブジェクトがガベージコレクションの対象にならない。
うーん、よくわからない。
この記事にあるように、「非static内部クラスインスタンスが存在する = 外部クラスインスタンスが存在する」という状態のため、非static内部クラスは外部クラスを参照することができるようです。そして参照を持つオブジェクトはガベージコレクションの対象にならないため、非同期処理が終わってもメモリから解放されない。これがメモリリークの原因になってしまうようです。わかったようなわからないような…。
で、原因がわかったところで上のコードをどう直したらいいかと言うと、Warningの通りにAsyncTask
をstatic
なクラスにする、もしくは、別クラスにすればいいです。以下はstatic
なクラスにした例。
public class MainActivity extends AppCompatActivity { /* 非同期処理の呼び出し */ public void asyncTaskSample() { MyTask task = new MyTask(); task.execute(0); } private static class MyTask extends AsyncTask<Integer, Integer, Integer> { @Override protected Integer doInBackground(Integer ...params) { System.out.println("Hello World!!"); return 0; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); asyncTaskSample(); } }
ただこれでもまだ問題があって、上のコードだとActivityオブジェクトの非staticな変数にアクセスできないです。上のコードでは実装してないけど、ViewとかをMyTask
からいじりたかったりします。そこらへんをどうやってActivityオブジェクトから非同期処理に渡すのが最適かいまいちわかりません。
この記事では、AsyncTask
にImageView
を渡すのに、コンストラクタで渡しているのですが、AsyncTask
がImageView
への参照を持たないように、WeakReference
というのを使っています。参考までに、コードを引用させてもらいます。
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> { private final WeakReference<ImageView> mImageViewReference; int mWidth; int mHeight; String mFilePath; public BitmapWorkerTask(ImageView imageView) { mImageViewReference = new WeakReference<ImageView>(imageView); mWidth = imageView.getWidth(); mHeight = imageView.getHeight(); } // バックグラウンドで画像をデコード @Override protected Bitmap doInBackground(String... params) { mFilePath = params[0]; return decodeSampledBitmapFromFile(mFilePath, mWidth, mHeight); } // ImageView に Bitmap をセット @Override protected void onPostExecute(Bitmap bitmap) { if (mImageViewReference != null && bitmap != null) { final ImageView imageView = mImageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } }
まとめ
結局、方法はいろいろあってどれが最適か正直わかりません。ただ明らかに間違っているコードもネットには散見されるので、注意が必要だなと感じました。(そう言いつつも自分の書いたコードが間違ってるかも...)AsyncTask奥深すぎる...