SlideShare a Scribd company logo
1 of 19
Download to read offline
AsyncTask アンチパターン 
2014-12-04 Android アンチパターン勉強会 
黒川 洋 / @hydrakecat
背景 
非同期処理で AsyncTask が使われているコードをよく見か 
けるが、けっこう気軽に使われている 
非同期処理の問題は起きるかどうかがタイミング次第なこ 
とが多いので、再現や解決が困難なバグを生む 
ネットに転がっている情報や解決方法は間違っていること 
が多い印象 
非同期処理は、人間にはまだ早すぎる...
ネタ元参考文献 
Processes and Threads | Android Developers 
Handling Runtime Changes | Android Developers 
Efficient Android Threading (Anders Goransson) 
Android の非同期処理についての解説。Thread や Looper か 
ら解説してあって勉強になる。
事前知識(おさらい) 
Android では View の操作を UI スレッド(メインスレッ 
ド)からしかできない 
重い処理を UI スレッドで行うと、ユーザーからはアプリが 
反応しないように見える 
5秒以上応答がないと ANR (Application Not Responding) エ 
ラーが表示される 
描画に関係しない重い処理はバックグラウンドで処理する
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 スレッドに渡すた 
めのクラス。 
Thread や Handler のヘルパークラス。 
基本的には数秒程度の処理を行うのに使うもので、それ以 
上の処理を行いたい場合は、Executor などの使用が推奨さ 
れている。 
非常によく使われている。というか、非同期処理は、一般的 
に AsyncTask がむやみに使われていない印象。
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();
IllegalArgumentException 
画面回転などで Configuration Chage が発生すると、Activity 
の再生成が行われる 
→ その際に Fragment が detach されるので 
mProgressDialog.dismiss() で 
IllegalArgumentException が発生する。 
ウェブの記事では、画面固定を推奨しているものもあるが、 
画面回転以外(キーボードの表示とか)にも Configuration 
Change は起き得るので、これではダメ。
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; 
}
問題点 
Configuration change が起きて、Activity が再生成になる 
と、AsyncTask の結果は捨てられることになる 
それが嫌なら、Thread や AsyncTaskLoader などを使っ 
て、再生成された Activity にスレッドをアタッチする
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(); 
}
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(); 
}
AsyncTask の罠 (その3) 
長い処理を行うとメモリリークの可能性がある 
task = new AsyncTask<Void, String, Void>(){ 
... 
}.execute(); 
non-static な内部クラスは暗黙に親オブジェクト(Activity 
オブジェクト)への参照を持っている 
AsyncTask が内部的に利用しているスレッドが生きている 
限り、このオブジェクトは GC されない 
出典: Efficient Android Threading
メモリリークの対処法 
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> { 
... 
}
メモリリークの対処法(再び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"; 
} 
}
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)
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); 
}
まとめ 
オススメの AsyncTask の使い方 
軽い処理だけにしておく 
non-static な inner class にしない 
Activity#onDestroy()で、キャンセルと Activity の参 
照の解放を忘れずに行う 
場合に応じて AsyncTaskLoader 、 Executor 、 
HandlerThread の利用も検討する

More Related Content

What's hot

オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
Moriharu Ohzu
 
Audit operationnel de la fonction commercial hicham alaoui encgo
Audit operationnel de la fonction commercial hicham alaoui encgoAudit operationnel de la fonction commercial hicham alaoui encgo
Audit operationnel de la fonction commercial hicham alaoui encgo
hicham alaoui
 
サーバーサイドでの非同期処理で色々やったよ
サーバーサイドでの非同期処理で色々やったよサーバーサイドでの非同期処理で色々やったよ
サーバーサイドでの非同期処理で色々やったよ
koji lin
 
Loadrunner presentation
Loadrunner presentationLoadrunner presentation
Loadrunner presentation
medsherb
 

What's hot (20)

Reactive programming with RxSwift
Reactive programming with RxSwiftReactive programming with RxSwift
Reactive programming with RxSwift
 
絶対落ちないアプリの作り方
絶対落ちないアプリの作り方絶対落ちないアプリの作り方
絶対落ちないアプリの作り方
 
What’s new in cloud run 2021 後期
What’s new in cloud run 2021 後期What’s new in cloud run 2021 後期
What’s new in cloud run 2021 後期
 
オブジェクト指向できていますか?
オブジェクト指向できていますか?オブジェクト指向できていますか?
オブジェクト指向できていますか?
 
Spring bootでweb バリデート編
Spring bootでweb バリデート編Spring bootでweb バリデート編
Spring bootでweb バリデート編
 
GitとCIとかチャットとかをオンプレで運用する話
GitとCIとかチャットとかをオンプレで運用する話GitとCIとかチャットとかをオンプレで運用する話
GitとCIとかチャットとかをオンプレで運用する話
 
View customize plugin for Redmineの紹介 (2019年版)
View customize plugin for Redmineの紹介 (2019年版)View customize plugin for Redmineの紹介 (2019年版)
View customize plugin for Redmineの紹介 (2019年版)
 
Audit operationnel de la fonction commercial hicham alaoui encgo
Audit operationnel de la fonction commercial hicham alaoui encgoAudit operationnel de la fonction commercial hicham alaoui encgo
Audit operationnel de la fonction commercial hicham alaoui encgo
 
オブジェクト指向アンチパターンを考えてみた
オブジェクト指向アンチパターンを考えてみたオブジェクト指向アンチパターンを考えてみた
オブジェクト指向アンチパターンを考えてみた
 
5分で出来る!イケてるconfluenceページ
5分で出来る!イケてるconfluenceページ5分で出来る!イケてるconfluenceページ
5分で出来る!イケてるconfluenceページ
 
Introduce Katalon tool
Introduce Katalon toolIntroduce Katalon tool
Introduce Katalon tool
 
サーバーサイドでの非同期処理で色々やったよ
サーバーサイドでの非同期処理で色々やったよサーバーサイドでの非同期処理で色々やったよ
サーバーサイドでの非同期処理で色々やったよ
 
Loadrunner presentation
Loadrunner presentationLoadrunner presentation
Loadrunner presentation
 
App013 ここはあえて紙と
App013 ここはあえて紙とApp013 ここはあえて紙と
App013 ここはあえて紙と
 
やはりお前らのMVCは間違っている
やはりお前らのMVCは間違っているやはりお前らのMVCは間違っている
やはりお前らのMVCは間違っている
 
LA CARTOGRAPHIE DES PROCESSUS.pdf
LA CARTOGRAPHIE DES PROCESSUS.pdfLA CARTOGRAPHIE DES PROCESSUS.pdf
LA CARTOGRAPHIE DES PROCESSUS.pdf
 
Prince2 Synthèse
Prince2 SynthèsePrince2 Synthèse
Prince2 Synthèse
 
La Gestion de Projet Agile
La Gestion de Projet AgileLa Gestion de Projet Agile
La Gestion de Projet Agile
 
Burp Suite 2.0触ってみた
Burp Suite 2.0触ってみたBurp Suite 2.0触ってみた
Burp Suite 2.0触ってみた
 
C#メタプログラミング概略 in 2021
C#メタプログラミング概略 in 2021C#メタプログラミング概略 in 2021
C#メタプログラミング概略 in 2021
 

Similar to AsyncTask アンチパターン

おいしいFragment #antama_ws
おいしいFragment #antama_wsおいしいFragment #antama_ws
おいしいFragment #antama_ws
Takahiro Yoshimura
 
Androidの通信周りのコーディングについて
Androidの通信周りのコーディングについてAndroidの通信周りのコーディングについて
Androidの通信周りのコーディングについて
Shoichi Takagi
 
ちょっと詳しくJavaScript 特別編【悪霊の神々】
ちょっと詳しくJavaScript 特別編【悪霊の神々】ちょっと詳しくJavaScript 特別編【悪霊の神々】
ちょっと詳しくJavaScript 特別編【悪霊の神々】
株式会社ランチェスター
 
ありえるえりあ勉強会@五反田~テスト編~ Part3
ありえるえりあ勉強会@五反田~テスト編~ Part3ありえるえりあ勉強会@五反田~テスト編~ Part3
ありえるえりあ勉強会@五反田~テスト編~ Part3
Tomoyuki Sato
 

Similar to AsyncTask アンチパターン (20)

ASP.NETを利用したAJAX開発の応用
ASP.NETを利用したAJAX開発の応用ASP.NETを利用したAJAX開発の応用
ASP.NETを利用したAJAX開発の応用
 
おいしいFragment #antama_ws
おいしいFragment #antama_wsおいしいFragment #antama_ws
おいしいFragment #antama_ws
 
Weeyble async 181009_tukky
Weeyble async 181009_tukkyWeeyble async 181009_tukky
Weeyble async 181009_tukky
 
Androidの通信周りのコーディングについて
Androidの通信周りのコーディングについてAndroidの通信周りのコーディングについて
Androidの通信周りのコーディングについて
 
Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.Android Lecture #03 @PRO&BSC Inc.
Android Lecture #03 @PRO&BSC Inc.
 
ちょっと詳しくJavaScript 特別編【悪霊の神々】
ちょっと詳しくJavaScript 特別編【悪霊の神々】ちょっと詳しくJavaScript 特別編【悪霊の神々】
ちょっと詳しくJavaScript 特別編【悪霊の神々】
 
Sencha ug3 siesta_share
Sencha ug3 siesta_shareSencha ug3 siesta_share
Sencha ug3 siesta_share
 
GroovyなAndroidテスト #atest_hack
GroovyなAndroidテスト #atest_hackGroovyなAndroidテスト #atest_hack
GroovyなAndroidテスト #atest_hack
 
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。
APASEC 2013 - ROP/JIT を使わずに DEP/ASLR を回避する手法を見てみた。
 
About Jobs
About JobsAbout Jobs
About Jobs
 
20130924 Picomon CRH勉強会
20130924 Picomon CRH勉強会20130924 Picomon CRH勉強会
20130924 Picomon CRH勉強会
 
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_cccJEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
 
React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門
 
Rx java x retrofit
Rx java x retrofitRx java x retrofit
Rx java x retrofit
 
C++ マルチスレッドプログラミング
C++ マルチスレッドプログラミングC++ マルチスレッドプログラミング
C++ マルチスレッドプログラミング
 
Unit testing JavaScript with JUnit/JavaFX
Unit testing JavaScript with JUnit/JavaFXUnit testing JavaScript with JUnit/JavaFX
Unit testing JavaScript with JUnit/JavaFX
 
C#次世代非同期処理概観 - Task vs Reactive Extensions
C#次世代非同期処理概観 - Task vs Reactive ExtensionsC#次世代非同期処理概観 - Task vs Reactive Extensions
C#次世代非同期処理概観 - Task vs Reactive Extensions
 
ありえるえりあ勉強会@五反田~テスト編~ Part3
ありえるえりあ勉強会@五反田~テスト編~ Part3ありえるえりあ勉強会@五反田~テスト編~ Part3
ありえるえりあ勉強会@五反田~テスト編~ Part3
 
Project Loom - 限定継続と軽量スレッド -
Project Loom - 限定継続と軽量スレッド - Project Loom - 限定継続と軽量スレッド -
Project Loom - 限定継続と軽量スレッド -
 
マーブル図で怖くないRxJS
マーブル図で怖くないRxJSマーブル図で怖くないRxJS
マーブル図で怖くないRxJS
 

AsyncTask アンチパターン

  • 1. AsyncTask アンチパターン 2014-12-04 Android アンチパターン勉強会 黒川 洋 / @hydrakecat
  • 2. 背景 非同期処理で AsyncTask が使われているコードをよく見か けるが、けっこう気軽に使われている 非同期処理の問題は起きるかどうかがタイミング次第なこ とが多いので、再現や解決が困難なバグを生む ネットに転がっている情報や解決方法は間違っていること が多い印象 非同期処理は、人間にはまだ早すぎる...
  • 3. ネタ元参考文献 Processes and Threads | Android Developers Handling Runtime Changes | Android Developers Efficient Android Threading (Anders Goransson) Android の非同期処理についての解説。Thread や Looper か ら解説してあって勉強になる。
  • 4. 事前知識(おさらい) Android では View の操作を UI スレッド(メインスレッ ド)からしかできない 重い処理を UI スレッドで行うと、ユーザーからはアプリが 反応しないように見える 5秒以上応答がないと ANR (Application Not Responding) エ ラーが表示される 描画に関係しない重い処理はバックグラウンドで処理する
  • 5. Android の非同期処理 Basic Thread HandlerThread Executor AsyncTask Services AsyncQueryHandler Loader
  • 6. Android の非同期処理 Basic Thread HandlerThread Executor AsyncTask Services AsyncQueryHandler Loader
  • 7. AsyncTask とは http://developer.android.com/reference/android/os/AsyncTask.html バックグラウンド処理を行って結果を UI スレッドに渡すた めのクラス。 Thread や Handler のヘルパークラス。 基本的には数秒程度の処理を行うのに使うもので、それ以 上の処理を行いたい場合は、Executor などの使用が推奨さ れている。 非常によく使われている。というか、非同期処理は、一般的 に AsyncTask がむやみに使われていない印象。
  • 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. IllegalArgumentException 画面回転などで Configuration Chage が発生すると、Activity の再生成が行われる → その際に Fragment が detach されるので mProgressDialog.dismiss() で IllegalArgumentException が発生する。 ウェブの記事では、画面固定を推奨しているものもあるが、 画面回転以外(キーボードの表示とか)にも Configuration Change は起き得るので、これではダメ。
  • 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. 問題点 Configuration change が起きて、Activity が再生成になる と、AsyncTask の結果は捨てられることになる それが嫌なら、Thread や AsyncTaskLoader などを使っ て、再生成された Activity にスレッドをアタッチする
  • 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. 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. AsyncTask の罠 (その3) 長い処理を行うとメモリリークの可能性がある task = new AsyncTask<Void, String, Void>(){ ... }.execute(); non-static な内部クラスは暗黙に親オブジェクト(Activity オブジェクト)への参照を持っている AsyncTask が内部的に利用しているスレッドが生きている 限り、このオブジェクトは GC されない 出典: Efficient Android Threading
  • 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. メモリリークの対処法(再び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. 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. 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. まとめ オススメの AsyncTask の使い方 軽い処理だけにしておく non-static な inner class にしない Activity#onDestroy()で、キャンセルと Activity の参 照の解放を忘れずに行う 場合に応じて AsyncTaskLoader 、 Executor 、 HandlerThread の利用も検討する