非同期な AsyncTask をわかりやすく

なんかよく分かった感がない。なんとなくやってるような気がする。
とりあえず人気関連記事を。
AsyncTaskでバックグラウンド処理を行う - Android Wiki* 
AsyncTaskでユーザビリティを向上させる | Android Techfirm Lab 
Developer Blog 〜開発メモ書き系〜:AndroidでAsyncTaskを使ったバックグラウンド処理 
いろんな場合があるので,なかなか定型化しづらいように思うので,オフィシャルなリファレンス。
AsyncTask | Android Developers 
じっくり和訳しながら読んでみる。

使い方

AsyncTaskを使用することをサブクラス化する必要があります。サブクラスでは、少なくとも1つのメソッド(doInBackground(Params...),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));
         }
         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の一般的な型

非同期のタスクによって使用される3つのタイプは次のとおりです:

  1. Params: 実行時にはそのタスクに送信されるパラメータの型。
  2. Progress: 背景の計算中に発行されたプログレスユニットのタイプ。
  3. Result: バックグラウンドの計算の結果の型。

すべてのタイプは常に非同期タスクによって使用されます。
未使用のような型を設定するには、単に、Void型を使用します。

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

4つのステップ

非同期のタスクが実行されると、タスクは4つのステップを通過します:

  1. onPreExecute(): タスクが実行された直後に、UIスレッドで呼び出される。このステップは通常、ユーザーインターフェイスプログレスバーを表示することにより例えば、タスクを設定するために使用されます。
  2. doInBackground(Params...): onPreExecute()の実行が終了後すぐにバックグランドスレッド上に呼び出されます。このステップは長い時間がかかるバックグラウンドで計算を行うために使用されます。非同期タスクのパラメータは、このステップに渡されます。計算の結果は、このステップによって返されなければならず、最後のステップに戻って渡されます。このステップはまた、進行のいずれかまたは複数のユニットを公開する publishProgress(Progress...) を使用することができます。これらの値は、onProgressUpdate(Progress...)のステップで、UIスレッド上で公開されます。
  3. onProgressUpdate(Progress...): publishProgress(Progress...) の呼び出し後に、UIスレッドで呼び出されます。 実行のタイミングは不定です。このメソッドは、バックグラウンドの計算がまだ実行中のユーザインタフェースの進歩の任意のフォームを表示するために使用されます。例えば、それは、プログレスバーをアニメーション化したり、テキストフ​​ィールドにログを表示するために使用することができます。
  4. onPostExecute(Result): バックグラウンドの計算が終了した後に、UIスレッドで呼び出されます。バックグラウンドの計算の結果がパラメータとして、このステップに渡されます。

スレッドのルール

正しく動作させるには、このクラスのために従う必要のあるいくつかのスレッドのルールがあります:

  • タスクインスタンスは、UIスレッド上に作成する必要があります。
  • execute(Params...) は,UIスレッドで呼び出される必要があります。
  • onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) を直接手動でコールしてはならない。
  • タスクは、1回だけ実行することができます(2回目の実行が試行された場合は、例外がスローされます。)

メモリの可観測性

AsyncTaskは、すべてのコールバックの呼び出しは次の操作は、明示的に同期することなく安全であるような方法で同期されていることを保証します。

  • コンストラクタまたは onPreExecute() でメンバーフィールドを設定し、 doInBackground(Params...) でそれらを参照してください。
  • doInBackground(Params...) でメンバーフィールドを設定し, onProgressUpdate(Progress...) や onPostExecute(Result) でそれらを参照してください。

このサンプルがわかりやすいような

http://developer.android.com/intl/ja/guide/topics/fundamentals/processes-and-threads.html

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }
    
    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}
  1. ボタンをクリックでバックグランド処理をURLに対して開始する。(UIスレッド -> バックグランドスレッド)
  2. URLにアクセスしてビットマップデータを取得して, onPostExecute() に渡す。(バックグランドスレッド内メソッド移動)
  3. doInBackground() から受け取ったビットマップデータを「UIスレッド上」のViewにセットする。(バックグランドスレッド -> UIスレッド)

UIスレッドとバックグランドスレッド間のデータの受け渡しは,
バックグランドな処理の実行の doInBackground() 以外で行う。