別スレッドからGUI更新を行うサンプル
では、別スレッドからGUI更新を行うサンプルアプリケーションを紹介しましょう。
ProgressDialog
時間のかかる処理を行う際に、画面上に進捗状況を表示したい場合があります。そのようなときに便利に使えるAndroidのGUIコンポーネントが、ProgressDialogです。
今回のサンプルは、このProgressDialogクラスをつかったアプリケーションで、実行すると、次のような画面になります。
経過秒数を表示して、5秒後にダイアログが消去される、というものです。経過秒数を表示する処理のところが、メインのスレッドとは別のスレッドになっています。
ProgressDialogの表示
サンプル全体のコードは、次のようになります。
public class HelloAndroid extends Activity {
ProgressDialog progressDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// ProgressDialogのインスタンスを生成する
progressDialog = new ProgressDialog(this);
// ProgressDialogのスタイルを設定する
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
// メッセージを設定する
progressDialog.setMessage("処理を実行中です...");
// 表示
progressDialog.show();
// 経過秒数を表示するスレッド(1)
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 5; i++) {
updateProgress(i);
}
// ダイアログの消去
progressDialog.dismiss();
}
}).start();
}
// 経過秒数を更新する(2)
public void updateProgress(final int val) {
try {
// 1秒待機
Thread.sleep(1000);
} catch (InterruptedException e) {
}
// 経過秒数更新
progressDialog.setMessage( val + "秒経過");
}
}
まずProgressDialogの使い方を説明しましょう。特に難しいものではありません。インスタンス化した後は、各種設定を行います。setProgressStyleメソッドは、表示スタイルの設定です。ここでは定数STYLE_SPINNERを指定していますが、STYLE_HORIZONTALを指定すれば、進捗率をバーで表示するタイプになります。
setMessageメソッドは、ダイアログに表示するメッセージの設定で、showメソッドは、ダイアログを表示します。ダイアログを消去するには、dismissメソッドを実行します。
(1)の経過秒数を表示するスレッドでは、forループで、経過秒数の更新処理(updateProgressメソッド)をくりかえし呼び出し、その後、ProgressDialogを消去しています。
(2)の経過秒数を更新する箇所では、1秒待機後、setMessageメソッドで文字を更新しています。なお、通常のアプリケーションであれば、1秒待機する部分で、時間のかかる処理を行うことになるでしょう。
Handlerクラスを使う
このサンプルには問題がないように見えますが、じつはこれを実行すると、次のような表示となり例外が発生してしまいます。
これは、別スレッド(経過秒数を表示するスレッド)の中から直接GUIの更新(setMessageメソッドの実行)を行ったからなのです。
そこで、Handlerクラスを使って、GUI更新のメッセージをメッセージキューに投入するようにします。以下のように、Handler変数の追加と、(2)の経過秒数を更新するメソッドを少し変更します。
public class HelloAndroid extends Activity {
Handler handler = new Handler();
~ 中略 ~
// 経過秒数を更新する(2)
public void updateProgress(final int val) {
// 1秒待機
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
handler.post(new Runnable() {
public void run() {
// 経過秒数更新
progressDialog.setMessage(val + "秒経過");
}
});
}
}
まずHandlerクラスをインスタンス化して、メインスレッドのLooperオブジェクトに関連づけます。そして、postメソッドでメッセージをメッセージキューに投入しています。postメソッドの引数は、Runnableオブジェクトになっています。つまり、メッセージとしてRunnableオブジェクトを指定できるということです。ここでは、runメソッドに、progressDialog.setMessageの呼び出しをそのまま記述したオブジェクトを指定しています。
こうすることで、GUIスレッドがメッセージをとりだし、progressDialogのsetMessageメソッドを実行する、という処理になるわけです。
最後に
今回は、ジェネリクス、コレクションと、Androidの非同期処理の基本を説明しました。次回はストリーム関連のAPIをとりあげることにします。
参考資料
- 『Javaポケットリファレンス』 WINGSプロジェクト 高江賢 著、山田祥寛 監修、技術評論社、2011年3月
- Java Platform, Standard Edition 6 API 仕様
- Android Developers


