Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

2.08. 非同期処理

KeithYokoma edited this page May 4, 2013 · 20 revisions

この章では、Android での非同期処理について解説します。

参考:Services | Android Developers
参考:Loaders | Android Developers
参考:Processes and Threads | Android Developers

目次

Service

Service とは、バックグラウンドで動作する Android のコンポーネントです。

Service の状態

Serviceには、その呼び出し方によって 2 つのいずれかの状態をとります。

開始

Context#startService(Intent)によって呼び出された時の状態です。

バインド

Context#bindService(Intent)によって呼び出された時の状態です。

Service のライフサイクル

Serviceがどのように呼び出されたかによって、ライフサイクルが異なります。

Service Lifecycle

Context#startService(Intent)によって開始されたSerivceは、自身が終了を宣言するか、誰かが終了を命令するまで生存し続けます。

Context#bindService(Intent)によってバインドされたServiceは、バインドしている呼び出し元が全員バインドの解除を行うまで生存し続けます。

Service を構築するクラス

Service

一般的な Service を作成するひな形クラスです。
バックグラウンドで動作するものではありますが、その実動作しているスレッドは、Service を動かしているプロセスのメインスレッドです。
つまり、Service 内でブロックする処理を記述すると、メインスレッドの処理がブロックします。よって、独自のプロセスで動かしたい場合は、その旨 AndroidManifest で宣言する必要があり、また、ブロックする処理を実行する場合は、自分でスレッドを新しく立てる必要があります。

IntentService

Intent による呼び出しで開始される Service であり、かつ、Service の開始要求を 1 つずつ順に処理するワーカスレッド上で動作する特別な Service です。 一度に複数の処理を並列して行う必要がない場合は、IntentService を利用するほうが実装が簡単です。

Loader

非同期に処理を行うための新しいフレームワークです。特に、ネットワークやファイル I/O を介してデータを読み出すためのフレームワークとして設計されているものです。
Activity や Fragment のライフサイクルに合わせて動作するように作られています。

Honeycomb 以後から導入されました。
SupportPackage にも含まれているので、2.x 系の OS でも利用することができます。

非同期処理を行うためのフレームワークとして、後述するAsyncTaskもあります。
ActivityFragmentのライフサイクルに合わせて動作する点で、データの読み出しにはLoaderが適しています。

AsyncTaskLoader

ネットワーク通信やその他のファイル I/O などによってデータを読み出す場合は、このクラスを拡張して非同期処理を記述します。

CursorLoader

別の章で解説する、データベースからのデータの読み出しに特化したクラスです。

AsyncTask

名前の通り、非同期処理のためのクラスです。
非同期に実行したい処理と、処理前、処理後のメインスレッド上での処理を記述するため、このクラスを継承して使います。

AsyncTaskでは、内部でスレッドプールを持っています(Donut までは単一のスレッドで動作していた)。
デフォルトでは 5 つのスレッドがプールされており、内部で並列して複数の非同期処理を実行できるようになっています(Honeycomb 以降は、スレッドの並列実行における諸問題を回避するため単一スレッドで動作するように戻っている)。
最大でプールされるスレッド数は 128 です。

AsyncTaskオブジェクトは、使い回しができません。一度処理が完了すると、その後にもう一度非同期処理の実行を依頼した時点で、例外が投げられます。
よって、再度非同期処理を実行したい場合は、新たにAsyncTaskオブジェクトを作成する必要があります。

/**
 * 非同期処理を実行するためのネストクラス。
 *
 * ジェネリクスの仕組みを用いて、非同期処理に渡す引数の型、進捗を監視するコールバック用の型、非同期処理の結果を表す型を指定する。
 *
 * Activity や Fragment のライフサイクルに合わせて、自分で AsyncTask をコントロールする必要があり、これを行わないと
 * 特に {@link AsyncTask#onPostExecute()} で、参照するオブジェクトが既にメモリから破棄されていて NullPointerException となることが起こりえる。
 */
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    private Context mApplicationContext;

    public MyAsyncTask(Context applicationContext) {
        super();
        mApplicationContext = applicationContext;
    }

    /**
     * 非同期処理を実行する前に UI スレッドで実行する処理を書く
     */
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Toast.makeText(mContext, "onPreExecute", Toast.LENGTH_SHORT).show();
    }

    /**
     * 非同期処理の進捗を受け取るコールバック。
     */
    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
        Toast.makeText(mContext, "onProgressUpdate", Toast.LENGTH_SHORT).show();
    }

    /**
     * 非同期処理の本体
     * 引数は非同期処理内容に渡すためのパラメータ配列。
     */
    @Override
    protected Void doInBackground(Void... params) {
        // 2 秒おきに進捗を通知する
        try {
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
            Thread.sleep(2000L);
            publishProgress();
        } catch (InterruptedException e) {
            Log.e(MyAsyncTask.class.getSimpleName(), "thread interrupted: ", e);
        }
        return null;
    }

    /**
     * 非同期処理の実行後に、UI スレッドで実行する処理。
     * 引数は {@link AsyncTask#execute(Object...)} の返り値。
     */
    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        Toast.makeText(mContext, "onPostExecute", Toast.LENGTH_SHORT).show();
    }
}
public class MyActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // do something...

        // 非同期処理の開始
        new MyAsyncTask(getApplicationContext()).execute();
    }
}

GitHub Pagesへ移行しましたmixi-inc.github.ioへお願いします。

Clone this wiki locally