プロセスとスレッド

プロセス

プロセス ライフサイクル (Process Lifecycle)

プロセスはその重要度に応じて、次の5つに分類されます。重要度の高いものから順に示すと、

  1. フォアグラウンド プロセス (Foreground process)
  2. ビジブル プロセス (Visible process)
  3. サービス プロセス (Service process)
  4. バックグラウンド プロセス (Background process)
  5. エンプティ プロセス (Empty process)

となります。

スレッド

Androidでは、スレッドに関連するクラスとして

の3つが用意されています。

ワーカースレッド (Worker threads / background threads)

ワーカースレッド

Worker threads - Processes and Threads | Android Developers

実行スレッドの確認

実行中のコードが実行されているスレッドは、Eclipseならばデバッグビューで確認できます。

Handlerクラス

Handlerクラスは、メッセージとメッセージキューに関連付けられたオブジェクトの、送信と処理を行うメソッドを提供します。

コンストラクタ 説明
Handler()
Handler - Handler | Android Developers
現在のスレッドのキューとハンドラを関連付ける
Handler(Handler.Callback callback)
現在のスレッドのキューとハンドラを関連付けて、メッセージをハンドルできるcallbackを登録する
Handler(Looper looper)
looperのキューとハンドラを関連付ける
Handler(Looper looper, Handler.Callback callback)
 
  メソッド 説明
 
post(Runnable r)
rをメッセージキューに追加する
postAtTime(Runnable r, long uptimeMillis)
uptimeMillis時間に実行されるように、rをメッセージキューに追加する
postDelayed(Runnable r, long delayMillis)
delayMillis経過後に実行されるように、rをメッセージキューに追加する
 
sendEmptyMessage(int what)
whatの値だけを伴いメッセージを送る
sendMessage(Message msg)
msgをメッセージキューの最後に追加する
sendMessageAtTime(Message msg, long uptimeMillis)
uptimeMillis時間の前に、msgをメッセージキューに追加する
sendMessageDelayed(Message msg, long delayMillis)
delayMillis経過前に、msgをメッセージキューに追加する
Handler | Android Developers

サンプルコード

Handler handler = new Handler(Looper.getMainLooper()){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // TODO
    }
};
handler.sendMessage(handler.obtainMessage());

Looperクラス

Looperクラスは、スレッドのメッセージループを処理するメソッドを提供します。

staticメソッド
メソッド 説明
synchronized static Looper getMainLooper() アプリケーションのメインスレッドで動いている主要なLooperを返す
static Looper myLooper() 現在のスレッドに関連付けられたLooperを返す
static MessageQueue myQueue() 現在のスレッドに関連付けられたMessageQueueを返す
static void prepare() Looperとして現在のスレッドを初期化する
static void prepareMainLooper() Looperとして現在のスレッドを初期化して、それをアプリケーションの主要なLooperとしてマークする
static void loop() スレッドのメッセージキューを処理する
Looper | Android Developers

HandlerThreadクラス

HandlerThreadクラスはThreadのサブクラスで、Looperを保持する新しいスレッドを開始するのに便利な機能を提供します。

HandlerThread | Android Developers

UIスレッド (UI thread)

AndroidのUIはスレッドセーフではないため、UIの操作はUIのスレッドから行う必要があります。そのUIのスレッドについて、次の2つの規則があります。

  1. UIスレッドをブロックしない。
  2. UIスレッドの外部から、Android UIツールキットへアクセスしない。

他のスレッドからUIスレッドにアクセスする方法として、いくつかの方法が提供されています。

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

しかし、これらのメソッドよりAsyncTaskクラスを使用すべきです。

ちなみにAndroid 3.0 (Honeycomb) 以降では、UIのスレッド以外から操作すると「android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.」のように例外が発生します。

C#における、スレッド セーフなコントロールの呼び出し

AsyncTaskクラス

AsyncTaskクラスはThreadクラスなどのヘルパークラスであり、ジェネリックであるためこれを継承して使用します。そのとき必ずdoInBackground()をオーバーライドし、そこでバックグラウンド スレッドでの処理を記述します。またonPostExecute()をオーバーライドすると、その引数でdoInBackground()の戻り値をUIスレッドで受け取れます。

AsyncTaskは、継承時に3つの型変数を指定する必要があります。

  1. Params … バックグラウンド スレッドを処理する、doInBackground()に渡される引数の型
  2. Progress … 進捗を伝える、onProgressUpdate()に渡される引数の型
  3. Result … 処理結果を受けとる、onPostExecute()に渡される引数の型

処理の実行は、AsyncTaskを継承したクラスのexecute()で行います。このメソッドの引数は可変引数であるため、任意の個数のパラメータを渡せます。

public void onClick(View v) {

        Test test = new Test();

        // タスクを実行する
        test.execute(10L, 20L, 30L);

        // 処理を途中で中止する
//      test.cancel(true);
}

class Test extends AsyncTask<Long, Integer, Double> {

    // [バックグラウンド スレッド]
    protected Double doInBackground(Long... params) {

        Double total = 0.0;
        for (int i = 0; i < params.length; i++) {
            total += params[i];

            // onProgressUpdate()の呼び出し
            publishProgress(i);

            // cancel()が呼ばれたならば、処理を中止する
            if (isCancelled()) break;
        }
        return total;
    }

    // [UIスレッド]
    protected void onProgressUpdate(Integer... progress) {

        // 処理の進捗を出力
        System.out.println(progress[0]);
    }

    // [UIスレッド]
    protected void onPostExecute(Double result) {

        // 処理の結果を出力
        System.out.println(result);
    }
}
ネットワーク上の画像をViewに表示

ネットワーク上の画像をViewに表示するサンプルを例に、スレッドを用いて実際にUIを操作する方法を以下に示します。

URL url = null;
try {
    url = new URL("http://example.com/sample.bmp");
} catch (MalformedURLException e) {
    e.printStackTrace();
}

// AsyncTaskを継承して、無名内部クラスとして定義
new AsyncTask<URL, Void, Drawable>() {

    @Override
    protected Drawable doInBackground(URL... params) {

        InputStream inputStream = null;
        try {
            inputStream = params[0].openStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // バックグラウンド スレッドで、ストリームから画像データを読み込む
        Drawable drawable = Drawable.createFromStream(inputStream, null);
        return drawable;
    }

    @Override
    protected void onPostExecute(Drawable image) {
        ImageView imageView = (ImageView) findViewById(R.id.imageView1);

        // UIスレッドで、Viewに画像を設定する
        imageView.setImageDrawable(image);
    }
}.execute(url);
Androidの情報サイトから、まとめて検索