プロセスはその重要度に応じて、次の5つに分類されます。重要度の高いものから順に示すと、
となります。
Androidでは、スレッドに関連するクラスとして
の3つが用意されています。
実行中のコードが実行されているスレッドは、Eclipseならばデバッグビューで確認できます。
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 handler = new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// TODO
}
};
handler.sendMessage(handler.obtainMessage());
Looperクラスは、スレッドのメッセージループを処理するメソッドを提供します。
メソッド | 説明 | |
---|---|---|
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() | スレッドのメッセージキューを処理する |
HandlerThreadクラスはThreadのサブクラスで、Looperを保持する新しいスレッドを開始するのに便利な機能を提供します。
HandlerThread | Android DevelopersAndroidのUIはスレッドセーフではないため、UIの操作はUIのスレッドから行う必要があります。そのUIのスレッドについて、次の2つの規則があります。
他のスレッドからUIスレッドにアクセスする方法として、いくつかの方法が提供されています。
しかし、これらのメソッドよりAsyncTaskクラスを使用すべきです。
ちなみにAndroid 3.0 (Honeycomb) 以降では、UIのスレッド以外から操作すると「android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.」のように例外が発生します。
AsyncTaskクラスはThreadクラスなどのヘルパークラスであり、ジェネリックであるためこれを継承して使用します。そのとき必ずdoInBackground()をオーバーライドし、そこでバックグラウンド スレッドでの処理を記述します。またonPostExecute()をオーバーライドすると、その引数でdoInBackground()の戻り値をUIスレッドで受け取れます。
AsyncTaskは、継承時に3つの型変数を指定する必要があります。
処理の実行は、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に表示するサンプルを例に、スレッドを用いて実際に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);