別スレッドから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