Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
AsyncTask アンチパターン 
2014-12-04 Android アンチパターン勉強会 
黒川 洋 / @hydrakecat
背景 
非同期処理で AsyncTask が使われているコードをよく見か 
けるが、けっこう気軽に使われている 
非同期処理の問題は起きるかどうかがタイミング次第なこ 
とが多いので、再現や解決が困難なバグを生む 
ネットに転がっている情報や解...
ネタ元参考文献 
Processes and Threads | Android Developers 
Handling Runtime Changes | Android Developers 
Efficient Android Thre...
事前知識(おさらい) 
Android では View の操作を UI スレッド(メインスレッ 
ド)からしかできない 
重い処理を UI スレッドで行うと、ユーザーからはアプリが 
反応しないように見える 
5秒以上応答がないと ANR (A...
Android の非同期処理 
Basic Thread 
HandlerThread 
Executor 
AsyncTask 
Services 
AsyncQueryHandler 
Loader
Android の非同期処理 
Basic Thread 
HandlerThread 
Executor 
AsyncTask 
Services 
AsyncQueryHandler 
Loader
AsyncTask とは 
http://developer.android.com/reference/android/os/AsyncTask.html 
バックグラウンド処理を行って結果を UI スレッドに渡すた 
めのクラス。 
Thr...
AsyncTask の罠 (その1) 
@Override 
public void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
......
IllegalArgumentException 
画面回転などで Configuration Chage が発生すると、Activity 
の再生成が行われる 
→ その際に Fragment が detach されるので 
mProgres...
IllegalArgumentException の 
とりあえずの対処 
onDestroy 時にダイアログを閉じて null にしておく 
@Override 
public void onCreate(Bundle savedInstan...
問題点 
Configuration change が起きて、Activity が再生成になる 
と、AsyncTask の結果は捨てられることになる 
それが嫌なら、Thread や AsyncTaskLoader などを使っ 
て、再生成さ...
AsyncTask の罠 (その2) 
Fragment#getActivity() が null になることがある 
画面回転などで Configuration Chage が発生すると、Fragment 
が detach されてしまう 
...
NullPointerException の対処 
Fragment#isAdded()で確認する 
あと、個人的には mAdapter はコールバックで作らない方 
が好き 
@Override 
public View onCreateVi...
AsyncTask の罠 (その3) 
長い処理を行うとメモリリークの可能性がある 
task = new AsyncTask<Void, String, Void>(){ 
... 
}.execute(); 
non-static な内部ク...
メモリリークの対処法 
non-static な内部クラスにせず、static な内部クラスにする 
か別クラスにする 
Activity#onDestory() 時に Activity への参照を破棄し 
て AsyncTask#cancel...
メモリリークの対処法(再びAsyncTask#cancel(false)は、isCanceledをtrueに 
セットして、実行中のタスクが終わるまで待機する。 
AsyncTask#cancel(true)にしても、即座にスレッドが 
終了す...
AsyncTask の罠 (その4) 
AsyncTask を複数実行したときに、逐次実行される 
(sequential)か、同時実行される(concurrent)かは、呼 
び出し方、APIレベルで変わる 
実行環境はアプリケーション全体で...
API レベルに関わらず処理を 
同じにするには 
逐次実行 
2.3系をサポートする限り無理です。AsyncTaskを諦めてく 
ださい。 
同時実行 
targetSdkVersion を < 13 にするか、API レベルによって処理 
...
まとめ 
オススメの AsyncTask の使い方 
軽い処理だけにしておく 
non-static な inner class にしない 
Activity#onDestroy()で、キャンセルと Activity の参 
照の解放を忘れずに...
Upcoming SlideShare
Loading in …5
×

AsyncTask アンチパターン

8,474 views

Published on

Slides at Android Anti-pattern Session held on 2014-12-04

Published in: Technology
  • Be the first to comment

AsyncTask アンチパターン

  1. 1. AsyncTask アンチパターン 2014-12-04 Android アンチパターン勉強会 黒川 洋 / @hydrakecat
  2. 2. 背景 非同期処理で AsyncTask が使われているコードをよく見か けるが、けっこう気軽に使われている 非同期処理の問題は起きるかどうかがタイミング次第なこ とが多いので、再現や解決が困難なバグを生む ネットに転がっている情報や解決方法は間違っていること が多い印象 非同期処理は、人間にはまだ早すぎる...
  3. 3. ネタ元参考文献 Processes and Threads | Android Developers Handling Runtime Changes | Android Developers Efficient Android Threading (Anders Goransson) Android の非同期処理についての解説。Thread や Looper か ら解説してあって勉強になる。
  4. 4. 事前知識(おさらい) Android では View の操作を UI スレッド(メインスレッ ド)からしかできない 重い処理を UI スレッドで行うと、ユーザーからはアプリが 反応しないように見える 5秒以上応答がないと ANR (Application Not Responding) エ ラーが表示される 描画に関係しない重い処理はバックグラウンドで処理する
  5. 5. Android の非同期処理 Basic Thread HandlerThread Executor AsyncTask Services AsyncQueryHandler Loader
  6. 6. Android の非同期処理 Basic Thread HandlerThread Executor AsyncTask Services AsyncQueryHandler Loader
  7. 7. AsyncTask とは http://developer.android.com/reference/android/os/AsyncTask.html バックグラウンド処理を行って結果を UI スレッドに渡すた めのクラス。 Thread や Handler のヘルパークラス。 基本的には数秒程度の処理を行うのに使うもので、それ以 上の処理を行いたい場合は、Executor などの使用が推奨さ れている。 非常によく使われている。というか、非同期処理は、一般的 に AsyncTask がむやみに使われていない印象。
  8. 8. AsyncTask の罠 (その1) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... new AsyncTask<Void, String, Void>(){ @Override protected void onPreExecute() { super.onPreExecute(); mProgressDialog.show(); } @Override protected String doInBackground(Void... value) { try { Thread.sleep(15000); } catch (InterruptedException e) { } return "AsyncTask Done"; } @Override protected void onPostExecute(String result) { mProgressDialog.dismiss(); mTextView.setText(result); } }.execute();
  9. 9. IllegalArgumentException 画面回転などで Configuration Chage が発生すると、Activity の再生成が行われる → その際に Fragment が detach されるので mProgressDialog.dismiss() で IllegalArgumentException が発生する。 ウェブの記事では、画面固定を推奨しているものもあるが、 画面回転以外(キーボードの表示とか)にも Configuration Change は起き得るので、これではダメ。
  10. 10. IllegalArgumentException の とりあえずの対処 onDestroy 時にダイアログを閉じて null にしておく @Override public void onCreate(Bundle savedInstanceState) { ... new AsyncTask<Void, Void, String>(){ ... @Override protected void onPostExecute(String result) { if (mProgressDialog != null) { mProgressDialog.dismiss(); } if (mTextView != null) { mTextView.setText(result); } } }.execute(); } @Override public void onDestroy() { if (mProgressDialog != null && mProgressDialog.isShowing()) { というか、mProgressDialog.ガイドライdismiss(); ン通り、ProgressDialog は使わない! } mProgressDialog = null; }
  11. 11. 問題点 Configuration change が起きて、Activity が再生成になる と、AsyncTask の結果は捨てられることになる それが嫌なら、Thread や AsyncTaskLoader などを使っ て、再生成された Activity にスレッドをアタッチする
  12. 12. AsyncTask の罠 (その2) Fragment#getActivity() が null になることがある 画面回転などで Configuration Chage が発生すると、Fragment が detach されてしまう @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ... new AsyncTask<Void, Void, String[]>(){ ... @Override protected void onPostExecute(String[] result) { mAdapter = new ArrayAdapter<String>(getActivity(), R.layout.listrow, result); mListView.setAdapter(mAdapter); } }.execute(); }
  13. 13. NullPointerException の対処 Fragment#isAdded()で確認する あと、個人的には mAdapter はコールバックで作らない方 が好き @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { ... new AsyncTask<Void, Void, String[]>(){ ... @Override protected void onPostExecute(String[] result) { if (isAdded() && mAdapter != null) { mAdapter.addAll(Arrays.asList(result)); mAdapter.notifyDatasetChanged(); } } }.execute(); }
  14. 14. AsyncTask の罠 (その3) 長い処理を行うとメモリリークの可能性がある task = new AsyncTask<Void, String, Void>(){ ... }.execute(); non-static な内部クラスは暗黙に親オブジェクト(Activity オブジェクト)への参照を持っている AsyncTask が内部的に利用しているスレッドが生きている 限り、このオブジェクトは GC されない 出典: Efficient Android Threading
  15. 15. メモリリークの対処法 non-static な内部クラスにせず、static な内部クラスにする か別クラスにする Activity#onDestory() 時に Activity への参照を破棄し て AsyncTask#cancel() を呼ぶ もしくは、Activity への参照を弱参照(Weak Reference) にする @Override public void onCreate(Bundle savedInstanceState) { ... mTask = new MyTask(this); mTask.execute(); } @Override protected void onDestroy () { super.onDestroy(); mTask.setActivity(null); mTask.cancel(true); } private static class MyTask extends AsyncTask<String, Bitmap, Void> { ... }
  16. 16. メモリリークの対処法(再びAsyncTask#cancel(false)は、isCanceledをtrueに セットして、実行中のタスクが終わるまで待機する。 AsyncTask#cancel(true)にしても、即座にスレッドが 終了することを保証しない。 private static class MyTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... params) { for (int i=0; i<NUM_TASKS; i++) { if (this.isCancelled()) { return null; } else { try { // Do any task } catch (InterruptedException iex) { // the blocking method throws an InterruptedException return null; } } } return "AsyncTask Done"; } }
  17. 17. AsyncTask の罠 (その4) AsyncTask を複数実行したときに、逐次実行される (sequential)か、同時実行される(concurrent)かは、呼 び出し方、APIレベルで変わる 実行環境はアプリケーション全体で同一(ある Service の AsyncTask が別 Activity の AsyncTask をブロックしうる) API targetSdkVersion execute executeOnExecutor 1-3 Any Sequential Not available 4-10 Any Concurrent Not available 11-12 Any Concurrent Sequential/Concurrent (customizable) 13+ <13 Concurrent Sequential/Concurrent (customizable) 13+ ≥13 Sequential Sequential/Concurrent (customizable)
  18. 18. API レベルに関わらず処理を 同じにするには 逐次実行 2.3系をサポートする限り無理です。AsyncTaskを諦めてく ださい。 同時実行 targetSdkVersion を < 13 にするか、API レベルによって処理 を変える必要があります。 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR1) { new MyAsyncTask().execute(); } else { new MyAsyncTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }
  19. 19. まとめ オススメの AsyncTask の使い方 軽い処理だけにしておく non-static な inner class にしない Activity#onDestroy()で、キャンセルと Activity の参 照の解放を忘れずに行う 場合に応じて AsyncTaskLoader 、 Executor 、 HandlerThread の利用も検討する

×