Furudateのブログ

プログラミングやネットワーク系の知識・技術がメインのブログ。技術メモ帳的な感じになるかと。岩手から発信していきます。

【Android】AsyncTaskでバックグラウンド処理とキャンセル対応

こんばんは。

今回は、AndroidJava)でバックグラウンド処理(非同期処理)をする方法について、メモします。
また、バックグラウンド処理中にプログレスダイアログを表示させ、キャンセルにも対応するようにしました。

Androidでバックグラウンド処理をしようとすると、UI Threadを使いメインスレッドとは別スレッドとして処理をさせるようにする必要があります。
また、処理中にUI更新を行うためにはHandlerを利用しなければなりません。

ただ、それだと少しめんどくさいので、今回は別スレッドでの処理と処理中や処理完了後のUI更新を簡単にしてくれるAsyncTaskというものを使って書いていきます。
(なお、UI ThreadやHandlerを利用した方法はこちらのサイトに詳しく説明してあります。一度見ておくとAsyncTaskについての理解の助けになるかもしれません。)

基本的にこういう処理の場合はプログレスバーで何%処理が完了しているかみたいなのを表示するのですが、今回は前の記事でも書いた、グルグルするプログレスダイアログを表示することにします。
プログレスバーでのやり方については参考サイトにたくさん載っているので。

今回作成するのは、ボタンを押したらダイアログが表示され、バックグラウンドで3秒Sleepする、というものです。
処理完了後はToastが表示され、またキャンセルされたときもToastを表示させます。

さて、それでは実装していきましょう。
作成しなければならないクラスファイルは以下の3つです。

  1. メインとなるActivity
  2. プログレスダイアログのDialogFragment
  3. AsyncTaskのクラス

以下でそれぞれについて見ていきます。

1. メインとなるActivity

まずはActivityです。
ここは単純にAsyncTaskを実行するだけですね。

public class MainActivity extends Activity implements OnClickListener{
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btn = (Button)findViewById(R.id.button1);
        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // AsyncTask呼び出し
        new MyAsyncTask(this).execute("params");
    }
}

はい。MyAsyncTaskという独自クラスにThisを渡して、executeしているだけです。
ここで"params"という引数を渡していますが、これはAsyncTaskのdoInBackgroundの引数として受け取ります。

2. プログレスダイアログのDialogFragment

これはこちらで書いてあるDialogFragmentの内容そのままですので、今回は割愛します。

3. AsyncTaskのクラス

いよいよメインとなるAsyncTaskのクラスを実装していきます。

import java.io.Serializable;

import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

/**
 * AsyncTaskの引数は、デフォルトでは AsyncTask<Params, Progress, Result>です。これはそれぞれ <入力パラメータ、進行度、結果のデータ型> を表しています。
 * 入力パラメータはActivityのexecute()で渡した引数の型です。
 * 今回は、入力パラメータをStringで渡し、進行度をIntegerで指定してUIを更新し、最終結果をLongで返すようにしています。
 */
public class MyAsyncTask extends AsyncTask<String, Integer, Long> {

    Context context;
    MyProgressDialogFragment progressDialog = null;    // ロード中画面のプログレスダイアログ作成
    
    /**
     * コンストラクタ
     */
    public MyAsyncTask(Context context){
        this.context = context;
    }
    
    /**
     * バッググラウンド処理の前処理(準備)
     * UI Thread処理
     */
    @Override
    protected void onPreExecute(){
        @SuppressWarnings({ "serial" })
        Serializable Cancel_Listener = new MyProgressDialogFragment.CancelListener() {
            @Override
            public void canceled(DialogInterface _interface) {
                cancel(true); // これをTrueにすることでキャンセルされ、onCancelledが呼び出される。
            }
        };
        progressDialog = MyProgressDialogFragment.newInstance("処理中", "しばらくお待ちください", true, Cancel_Listener);
        progressDialog.show(((Activity) context).getFragmentManager(), "progress");
    }

    /**
     * バックグラウンド処理
     */
    @Override
    protected Long doInBackground(String... params) {
        try{
            Thread.sleep(3000);
             // Cancelされたとき
            if (isCancelled()){
                return 0L;
            }
        } catch (InterruptedException e) {
            Log.d("test", "Error");
        }  
        return 123L;
    }
    
    /**
     * バックグラウンド処理が終わった後の処理(表示の更新)
     */
    @Override
    protected void onPostExecute(Long result){
        if (progressDialog.getShowsDialog())
            progressDialog.dismiss();
        
        if (result != null){
            Toast.makeText(context, result.toString(), Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(context, "NG", Toast.LENGTH_SHORT).show();
        }
    }
    
    /**
     * 中止された際の処理
     */
    @Override
    protected void onCancelled(){
        if (progressDialog.getShowsDialog()){
            progressDialog.dismiss();
        }
        Toast.makeText(context, "Canceled", Toast.LENGTH_SHORT).show();
    }
}

こんな感じです。
注意点はだいたいソースコードに書いてあります。
AsyncTaskの実行の流れとしては以下のようになります。

  • onPreExecute(前処理)
  • doInBackground(メイン処理)
  • onPostExecute(後処理)
  • onCancelled(中止されたときの処理)

また、プログレスバーを付けると、doInBackgroundでpublishProgressを使ってプログレスバーの値を変更します。
publishProgressを呼び出すと、onProgressUpdate(進み具合を更新)する関数が呼び出されますので、これもオーバーライドして実装します。ここの引数の型はAsyncTaskの2つ目のパラメータです。

以上でバックグラウンド処理をすることが出来ました!
最初はちょっと戸惑いましたが、流れが分かるとそこまで難しくないのかなーと思います。

今回は以上です。
それでは。

追記

こちらのエントリーでは上記のものをリスナーを使って汎用的にし、Activity側にイベントを返しています。
こんな感じでやったほうが便利ですね。今後私もこのようにやっていきたいと思います。
ぜひ参考にしてみてくださいー。

参考にさせていただいたサイト

AsyncTaskと仲良くなろう:バックグラウンド処理とキャンセル
AsyncTask を利用した非同期処理
AndroidでAsyncTaskを使ったバックグラウンド処理
Androidの汎用的な非同期通信クラスできたよー!