Android プログラミングを初めて学ぶ人のための学習サイトです。実用的なコード例でわかりやすく解説しています。
Android 開発入門Android 開発入門
Android 開発
Androidプログラミング基礎
Android 携帯・アプリの話題
関連サイト
サイト情報
ホーム > 非同期処理の基礎 > AsyncTask を利用した非同期処理

AsyncTask を利用した非同期処理

スポンサーリンク

非同期処理と UI の更新 ではワーカースレッドを利用した非同期処理と UI を更新する時には Handler を利用して UI スレッドに対して処理をポストするという点について説明しました。

ここでは、そうしたバックグラウンドでの処理と UI とのやりとりをひっくるめて簡単に操作するための、 AsyncTask クラスの利用方法を説明します。

さらに、ついでに ProgressDialog のキャンセル処理とプログレスバーの表示についても説明します。

ProgressDialog と非同期処理のキャンセル

ここで作るサンプルプログラムは次のようなものです。まずはボタンが一つだけあります。

AsyncTask と ProgressDialog

このボタンをクリックすると、下のスクリーンショットのようにプログレスバー付きの ProgressDialog が表示されます。

AsyncTask と ProgressDialog

この進捗が100%になったところで、ダイアログは消えます。また、 BACK ボタンを押すことで処理をキャンセルすることもできることにします。

処理が進んだときの UI の更新とキャンセルの確認方法はどのようにしたらよいでしょうか。ここでは AsyncTask を利用してその辺の処理を簡易化します。

非同期処理の開始

まずは、メインのアクティビティでボタンをクリックした時の処理を書いてしまいます。ボタンをクリックしたときに非同期処理を開始しています。

package com.keicode.android.test;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class AsyncTest2 extends Activity 
  implements OnClickListener {
  
  final String TAG = "AsyncTest2";
  
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    ((Button)findViewById(R.id.button1))
      .setOnClickListener(this);
  }
  
  @Override
  public void onClick(View v) {
    if(v.getId() == R.id.button1){
      new MyAsyncTask(this).execute("Param1");
    }
  }
  
}

みてわかるように、クリックした時は MyAsyncTask というクラスのインスタンスを作って、それの execute メソッドを呼んでいます。 この execute には "Param1" という、サンプルプログラムらしい引数を渡しています(笑)。

この MyAsyncTask というのが、 後述の AsyncTask クラスを派生したユーザー定義のクラスです。さっそくその MyAsyncTask のコードをみてみましょう。

package com.keicode.android.test;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.os.AsyncTask;
import android.util.Log;

public class MyAsyncTask 
  extends AsyncTask<String, Integer, Long> 
  implements OnCancelListener{

  final String TAG = "MyAsyncTask";
  ProgressDialog dialog;
  Context context;
  
  public MyAsyncTask(Context context){
    this.context = context;
  }
  
  @Override
  protected void onPreExecute() {
    Log.d(TAG, "onPreExecute");
    dialog = new ProgressDialog(context);
    dialog.setTitle("Please wait");
    dialog.setMessage("Loading data...");
    dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    dialog.setCancelable(true);
    dialog.setOnCancelListener(this);
    dialog.setMax(100);
    dialog.setProgress(0);
    dialog.show();
  }

  @Override
  protected Long doInBackground(String... params) {
    Log.d(TAG, "doInBackground - " + params[0]);
    
    try {
      for(int i=0; i<10; i++){
        if(isCancelled()){
          Log.d(TAG, "Cancelled!");
          break;
        }
        Thread.sleep(1000);
        publishProgress((i+1) * 10);
      }
    } catch (InterruptedException e) {
      Log.d(TAG, "InterruptedException in doInBackground");
    }  
    return 123L;
  }
  
  @Override
  protected void onProgressUpdate(Integer... values) {
    Log.d(TAG, "onProgressUpdate - " + values[0]);
    dialog.setProgress(values[0]);
  }
  
  @Override
  protected void onCancelled() {
    Log.d(TAG, "onCancelled");
    dialog.dismiss();
  }

  @Override
  protected void onPostExecute(Long result) {
    Log.d(TAG, "onPostExecute - " + result);
    dialog.dismiss();
  }

  @Override
  public void onCancel(DialogInterface dialog) {
    Log.d(TAG, "Dialog onCancell... calling cancel(true)");
    this.cancel(true);
  }
}

進捗のダイアログ (すなわち UI スレッドとの通信が含まれるはず)、キャンセル処理、及び肝心の非同期処理など盛りだくさんのはずなのですが、コードはこの通り非常に簡潔に済んでいます。

メソッドが呼ばれる順番とタイミング

AsyncTask クラスのメソッドは次のように呼ばれます。

  1. まずはコンストラクタ。これはいいですよね。ここではメインのアクティビティのコンテキストを受け取っています。
  2. 次に onPreExecute が最初に UI スレッドで呼び出されます。 したがってここで、 UI に関わる処理をします。ここでは ProgressDialog の設定を行っています。
  3. 次に doInBackground がワーカースレッド上で実行されます。 ここに渡されるパラメータの型は AsyncTask を extends するときのひとつめのパラメータです (AsyncTask<T, ... の T です)。

    非同期で行いたい時間のかかる処理はこの doInBackground で実行します。後で説明しますが、時々 isCancelled を呼び出してキャンセルの要求が行われていないか確認します。

    また進捗をアップデートするには、この中で publishProgress を呼ぶことで行います。
  4. doInBackground 内で publishProgress が呼ばれると、 UI スレッド上で onProgressUpdate が呼ばれます。 onProgress の引数の型は AsyncTask の extends 時のふたつめのパラメータです。
  5. doInBackground が終わると、これの戻り値をパラメータとして渡して onPostExecute が呼ばれます。 この値の型は AsyncTask を extends するときの三つめのパラメータです。

ではこのプログラムを動かして、デバッグログをみてみましょう。

AsyncTask と ProgressDialog

このとおり、確かに doInBackground には execute に渡したパラメータが渡されていること、 publishProgress に渡した値が onUpdateProgress で受け取れていること、 そして、 doInBackground の戻り値が onPostExecute に渡っていることが分かります。

doInBackground 以外は UI スレッド上で実行されるので、 UI に関わる処理が問題なくそのまま行えています。

処理のキャンセル

処理をキャンセルする場合は、 AsyncTask の cancel メソッドを呼びます。

すると isCancelled が true になるとか、今回の例のように sleep が InterruptedException となるので、 適切な終了処理をした後で doInBackground を抜けることができます。

キャンセルした状態で doInBackground を抜けると onPostExecute ではなく、 onCancelled が呼ばれます。

実行結果は次の通り。

AsyncTask と ProgressDialog

ProgressDialog がキャンセルイベントを捕まえたところで、 AsyncTask の cancel を呼び出す。すると、doInBackground は InterruptedException がおきて終了して、その後 onPostExecute ではなく onCancelled が呼ばれました。

ちなみに、 onCancelled をオーバーライドした時は super.onCancelled は呼ばないように気をつけてください。

記事を気に入っていただけましたら、ソーシャルメディアでシェアをお願いします。記事を書く活力になります!

スポンサーリンク
ホーム > 非同期処理の基礎 > AsyncTask を利用した非同期処理
サイト内検索
当サイト及び姉妹サイト内 (*.keicode.com) の情報を検索します。
小山圭介 著者
小山 圭介
4/15/1973生
米国在住のソフトウェアエンジニア。ITコンサルタント
»プロフィール
スポンサーリンク

© 2010-2014 Keisuke Oyama Android 開発入門