https://developer.android.com/reference/android/os/AsyncTask
個人的な写経的なまとめ的なものです。誤記誤訳があったらごめんね。
[マルチスレッドまとめ的なのはここ]
(https://qiita.com/old_cat/items/a6d71f18a0789351c1a3)
public abstract class AsyncTask extends Object
android.os.AsyncTask<Param,Progress,Result>
非同期通信後の処理や、AsyncTaskの汎用化といか共通化?について
#AsyncTask概要
AsyncTaskは簡単かつ適切にUIスレッドを扱ます。ThreadやHandlerを使わずに、バックグラウンドで処理し、結果をUIスレッドに戻します。
AsyncTaskはThreadやHandlerなどを扱うヘルパークラス(インスタンス化して使う:staticメソッドはない)としてデザインされてます。一般的なスレッドフレームワークになるようには構成されてません。
AsyncTaskは数秒程度の処理に適してます。長い間スレッド処理させたい場合には、Exesutor,ThreadPoolExecuter,FutureTaskなど、java.util.concurrentパッケージで提供されるAPIsを使ってね。
非同期のタスクは、バックグラウンドスレッドで実行されてUIスレッドに渡される結果によって定義されます?。つまり、以下の引数のタイプと、4つのステップで定義します。
-
AsyncTaskの引数
- Param
- Progress
- Result
-
4つのステップ
- onPreExecute
- doInBackground
- onProgressUpdate
- onPostExecut
#使い方
AsyncTaskはサブクラス化して使い、少なくとも1つ以上のメソッドをオーバーライドします(doInBackground(Param...))、またほとんどの場合、2つ目もオーバーライドします(onPostExecute(Result))。
以下はサブクラス化の例
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
一度生成すれば、タスクの実行は実に簡単!
new DownloadFilesTask().execute(url1, url2, url3);
###AsyncTaskの引数<総称型タイプ>
AsyncTaskM<Param,Progress,Result>
1.Param
実行時にタスクに渡すパラメータタイプ
2.Progress
バックグランド処理の間に表示させる進捗ユニットのタイプ
3.Result
バックグラウンド処理の結果のタイプ
全てのタイプを使う必要はなく、voidで済ますこともできます。
こんな感じで
private class MyTask extends AsyncTask<Void,Void,Void>
###4つのステップ
非同期タスクが実行されるとき、タスクは4つのステップを踏みます。
-
onPreExecute()
タスクが処理される前にUIスレッドに呼び出す準備的なメソッド。例えば、Execute()の実行時に、プログレスバーを表示させたい時などに、onPreExecute()でプログレスバーの表示を準備させたりするって感じ。 -
doInBackground(Params...):オーバーライド必須
onPreExecute()処理後、すぐにバックグラウンドスレッドで呼び出されます。バックグラウンド処理に時間を要してもOKだよっていう処理に使うステップです。このステップで、非同期タスクのパラメータが引き渡されます。処理結果は必ずこのステップで返され、最後のステップに結果を渡します。このステップは、プログレスのユニットを表示するpublishProgress(Progress)にも使え、そのケースではonProgressUpdate(Progress...)のステップでUIスレッドに表示されます。 -
onProgressUpdate(Progress...)
publishProgress(Progress...)の実行後にUIスレッドに呼び出されます。実行のタイミングは定義されてません。このメソッドは色んなプログレスフォームを表示するのに使われます。プログレスのフォームは、バックグラウンド処理の間、UIに表示されます。例えばアニメーションプログレスバーとか、テキストフィールドでのログ表示とか。 -
onPostExecute(Result)
バックグラウンド処理の終了後にUIスレッドに呼ばれます。バックグラウンド処理の結果はこのステップにパラメーターとして返されます。
###タスクキャンセル
タスクキャンセルはcancel(boolea)で実行してね。cancel()を実行すると(成功すると)、後でisCancelled()が呼ばれてtrueを返します。通常ならdoInBackground(java.lang.Object[])を実行するとonPostExecute(java.lang.Object)が実行されますが、cannel()を実行すると、onPostExecute()の代わりにonCannelled(java.lang.Object)が実行されるって感じの流れ。タスクがちゃんとキャンセルされてるか確認するために、doInBackground(java.lang.Object[])からisCancelled()の戻り値(キャンセルされてればtrue!)が発行されるので、可能な限り素早くチェック!
###スレッドルール
スレッドを適切に使うために、幾つかのルールがあります。
- AsyncTaskはUIスレッド側で呼び出します。AndroidのバージョンがJELLY_BEAN以降は自動的にそうなります。
- タスクインスタンスはUIスレッド側で作成
- UI側でexecute(Params...)を実行
- 直接、onPreExesute(),onPostExecute(Result),doInBackground(Prams...),onProgressUpdate(Progress...)を呼び出さないでね。Androidシステムに任せてください!
- タスクは1回だけ実行できます。もし2回目が実行されると、例外を投げられます。
###メモリの確認
AsyncTaskは、明示的に同期するケースを除き、以下を確認するために、全てのコールバック関数の呼び出しが同期されることを保証します。
- onPreExecute()のメモリへの影響、execute(Params...)の呼び出し前に実行されたメソッド等(AsyncTaskオブジェクトの構築)のメモリへの影響は、doInBackground(Params...)に表示される
- doInBackground(Params...)
メモリへの影響は、onPostExecute(Result)に表示される - publishProgress(Progres...)を呼び出す前のdoInBackgrund(Params...)のメモリへの影響は、対応するonProgressUpdate(Progress..)で表示される。ただし、doInBackground(Params...)は引き続き実行されるため、doInBackground(Params...)の更新が、進行中のonProgressUpdata(Progress...)
の呼び出しに干渉しないように注意が必要。 - cancel(boolean)のメモリへの影響は、calcek(boolean)に先行して呼び出されるisCancelled()(実行結果でtrueを返す)の呼び出しの後とか、onCancelled()の呼び出し中や実行後に表示されます。
#AsyncTaskの生い立ち
当初、AsyncTasksはバックグラウンドのシングルスレッドで順次処理されてました。AndroidのバージョンがBuild.VERSION_CODES.DONUTからは、並行処理するマルチタスクを扱えるように、スレッドプールに変化してきた。AndroidバージョンがBuild.VERSION_CODES.HONEYCOMBからは、並行処理が原因で発生するアプリケーションエラーを避けるため、タスクはシングルスレッドで実行するようになっています。
もし、並行処理をするのなら、THREAD_POOL_EXECUTORとともにexecuteOnExecutor(java.util.concurrent.Executor,java.lang.Object[])を実行しよう!。
#概要
#####■ Nested classes
enum |
AsiyncTask.Status |
タスクの現在のステータスを示す |
#####■ Fields
public static final Executor
SERIAL_EXECUTOR |
タスクを一つずつ順次処理させるExecutor |
HREAD_POOL_EXECUTOR |
並列処理時に使われるExecutor |
#####■ Public constructors
AsyncTask<> |
新しい同期タスクを生成 |
#####■ Public methods
final boolean |
cancel(boolean mayInterruptIfRunning) タスクをキャンセル処理< /td> |
final AsyncTask |
execute(Params... param) パラメーターを指定してタスクを処理 |
static void |
execute(Runnable runnable) シンプルなRunnableオブジェクトを、手軽に処理するためのexecute(java.lang.Object) |
final AsyncTask |
executeOnExecutor(Executor exec,Params... param> パラメータを指定してタスクを処理 |
final Result |
get(long timeout,TimeUnit unit) 処理を完遂するのに時間がかかる場合に待機して結果を取得する |
final Result |
get() 処理を完遂するのに必要なら待機して結果を取得する |
final AsyncTask.Status |
getStatus タスクの現在の状態を返す |
final boolean |
isCancelled() タスクが正常に終了する前にキャンセルが成功したらtrueを返す |
#####Protected methods
abstract Result |
doInBackground(Params... params) バックグラウンドスレッドで処理を実行するのにオバーライドするメソッド |
void |
onCancelled() できればonCancelled(java.lang.Object)をオーバーライドしよう! |
void |
onCancelled(Result result) cancel(boolean)を実行し、doInBackground(java.lang.Object[])が終了した後で、UIスレッドで稼働する |
void |
onPostExecute(Result result) doInBackground(Params...)の後でUIスレッドで稼働する |
void |
onPreExecute() doInBackground(Params...)の前にUIスレッドで稼働する |
void |
onProgressUpdate(Progress... values) publishProgress(Progress...)が呼び出された後に、UIスレッドで稼働する |
final void |
publishProgress(Progress... values) このメソッドは、バックグラウンドで処理中にUIスレッドでアップデートするためにdoInBackground(Params...)から呼び出される |
#####継承されたメソッド
From class java.lang.Object |
#AsyncTaskまとめ的な
AsyncTaskはサブクラス化して使う
AsyncTask<Params,Progress,Result>
- Params
doInBackground()で使う引数型を指定 - Progress
実行中の情報をユーザーに表示するメソッドの型
publishProgress(),onProgressUpdate()で使う引数型を指定 - Result
結果表示のメソッド
onPostExecute()で使う引数型を指定
AsyncTaskが引数不要なら、AsyncTask<void,void,void>
###主なメソッド
-
execute()
-
publishProgress()
-
@Overrideメソッド(軌道はAndroidシステムに任せてね)
- onPreExecute()
- doInBackground()
- onProgressUpdate()
- onPostExecute()
#####・execute(Params... params)
UIスレッド側で実行するメソッド。オーバーライドメソッドのdoInBackground()に引数を渡してdoInBackgroundを実行するよ!。
引数の型はAsyncTaskの引数型を参照してね
引数はdoInBackground()を実行するに使います。
#####・onPreExecute
Execute()実行前にしたい処理があればここで!
#####・doInBackground()
オーバーライド必須のメソッド!
execute()から引数を受け取って、バックグラウンドでタスク処理。
doInBackground()処理後にonPostExecute()を呼び、doInBackground()処理の結果をonPostExecute()に渡します。もしcancel()を使うとonPostExecute()の代わりに、onCannelled()が呼ばれるよ。
doInBackground()はタスク処理専用。UIスレッドの更新は他のメソッドで!
#####・onPostExecute()
doInBackground()内で使って、doInBackground()の結果を、UIスレッド側で表示するメソッド。AsyncTaskの引数Resultを使うのです。
#####・publishProgress()
進捗表示を使いたいときにどうぞ!
doInBackground()でpublishProgress()を実行すると、Androidシステムは、UIスレッドでonProgressUpdate()を実行します。
#####・onProgressUpdate()
doInBackground()でpublishProgress()を実行すると、Androidシステムは、UIスレッドでonProgressUpdate()を実行
引数はAsyncTaskの引数を参照
###サンプルコード:execute()とdoInBackground()
- MainActivity.java
AsyncTask<>をサブクラス化し、execute()を実行する - AsyncTask<>クラスを拡張して、MyAsyncTaskクラスを生成
doInBackground()は、MainActivity.javaのexecute()によって処理を実行する(todo)。doInBackground()はexecute()から引数を受け取るって処理に使うよ。
public class MainActivity extends AppCompatActivity {
public static TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// タスクインスタンスを生成。execute()で引数をonInBackground()に渡す
// new MyAsyncTask().execute(); でもOK
MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
}
}
class MyAsyncTask extends AsyncTask<Void,Void,Void> {
@Override
protected Void doInBackground(Void... aVoid) {
// todo
Log.d("msg","In MyAsyncTask");
return null;
}
}
###サンプルコード:幾つかメソッドを入れてみる
3秒毎にtextViewの表示を変えるコード
-
MyActivity.onCreate()
myAsncTask.excute("In doInBackground") -
class MyAsyncTask
-
onPreExecute()
textView.setText("onPreExecute") -
doInBackground()
sleep(10003) -> publishProgress(string[0]) -> sleep(10003) -> return "End doInBackground" -
onProgressUpdate()
textView.setText(string[0]) -
onPostExecute()
textView.setText(s)
-
onPreExecute()
public class MainActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
// タスクインスタンスを生成。execute()で引数をonInBackground()に渡す
// new MyAsyncTask<>.execute(); でもOK
MyAsyncTask myAsyncTask = new MyAsyncTask();
// doInBackground()に文字列("In doInBackground")を渡して
// onPreExecute()の処理後に、doInBackground()を実行
myAsyncTask.execute("In doInBackground");
}
class MyAsyncTask extends AsyncTask<String,String, String> {
// execute("In doInBackground")処理前に実行。UIスレッドのtextViewに表示
@Override
protected void onPreExecute(){
textView.setText("onPreExecute");
}
// execute()から文字列"In doInBackground"を受け取る
@Override
protected String doInBackground(String... string) {
// 3秒待機
try {
sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
// doInBackground処理中に、textViewを表示させるonPublishUpdate()を呼ぶ
publishProgress(string[0]);
// 3秒待機
try {
sleep(1000*3);
} catch (InterruptedException e) {
e.printStackTrace();
}
// onPostExecute()に文字列"End doInBackground"を渡す
return "End doInBackground";
}
// doInBackground内のpublishProgress()から文字列を受け取り、UIスレッドのtextViewに表示
protected void onProgressUpdate(String... string){
textView.setText(string[0]);
}
// doInBackground()からreturn値("End doInBackground")を受け取り、UIスレッドのtextViewに表示
@Override
protected void onPostExecute(String s){
textView.setText(s);
}
}
}
#ActivityからAsyncTaskにパラメータを渡す
複数のパラメータを一つ一つ渡す
...
new MyAsyncTask().execute("param1","param2");
...
@Override
protected String doInBackground(String... string){
String param1 = string[0];
String param2 = string[1];
return null
}
複数のパラメータを配列にして渡す
Map<String,String> map = new HashMap<String,String>();
map.put("param1","data1");
map.put("param2","data2");
map.put("param3","data3");
new MyAsyncTask().execute(map);
public class MyAsyncTask extends AsyncTask<Map<String,String>,Void,Map<String,String>>{
@Override
protected Map<String,String> doInBackground(Map<String,String>... map){
Map<String,String> mp = map[0];
// mpをJsonにするのなら
// JSONObject jsonMap = new JSONObject(mp);
return mp;
}
@Override
protected void onPostExecute(Map<String,String> mp){
super.onPostExecute(mp);
String val1 = mp.get("param1"); // val1にdata1が格納された
String val2 = mp.get("param1"); // val2にdata2が格納された
String val3 = mp.get("param3"); // val3にdata3が格納された
}
}
ちょっと時間かかったね。いつも通り亀の歩み。