別スレッドで処理して
メインスレッドではダイアログを表示する
このため、ProgressDialogを使うためには、時間のかかる処理を別スレッドで実行させ、メインスレッドでは、ProgressDialogを表示させるといった構造を作る必要があります。ここだけを見ると、ダイアログを表示させるのに別スレッドを作るのは、本末転倒な感じもしますが、ANRを起動させないためにも別スレッド化が必要だし、GUI側で画面を更新(ProgressDialogはアニメーションが動作する)させるためにも必要な構造なのです。
以下のリストは、タイムゾーンのリストを都市名順に並び変える部分のコードです。EditItem.javaの464行目からに相当します。タイムゾーンのリストはかなりの数が定義されており、並び変えには少し時間がかかるため、別スレッド(DoSort)で実行させ、その間にProgressDialogを表示しています。
private void imsort(){
dialog = new ProgressDialog(this);
dialog.setIndeterminate(true);
dialog.setMessage(myS(R.string.sort_wait));
dialog.show();
tmpsort = new DoSort(handler,this);
tmpsort.start();
}
private class DoSort extends Thread {
private Handler handler;
private final Runnable listener;
public DoSort(Handler _handler, Runnable _listener){
this.handler = _handler;
this.listener = _listener;
}
@Override
public void run()
{
WorldClock.myTimeZoneData.sort_by_city();
handler.post(listener);
}
}
スレッドはjavaのスレッドです。Threadクラスを継承するDoSortクラスを作り、その中のrunメソッドに処理を記述します。
このコードが別スレッドで実行されているとき、メインスレッドは、ProgressDialogを呼び出している状態です。これを止めなければなりません。このためのコードが「handler.post(listener);」です。前述のように別スレッドからは、GUI操作を行なうことはできません。ということは、ProgressDialogを止めることもできないのです。サブスレッド自体は、処理が終われば停止しますが、何らかの方法で、GUIを表示させているメインスレッドを実行させないと、ダイアログを止めることができないのです。ところが、メインスレッドはProgressDialogのshow()を実行した状態なので、アプリケーションには戻ってきていない状態になっています。
そこでHandlerを使って、ProgressDialog.show()を実行中のメインスレッドに割り込みをかけて、ダイアログを止めるような処理を行なわせているのです。実際には、EditItem.javaの454行目にあるrunメソッドが呼び出されます。
public void run(){
MyListAdapter arrayAdapter = new MyListAdapter(this,WorldClock.myTimeZoneData.getTimeZoneDb());
arrayAdapter.setCityNameMode(true);
tzlist.setAdapter(arrayAdapter);
tzlist.invalidate();
tzlist.setSelection(WorldClock.myTimeZoneData.blute_search(getTimeZone()));
dialog.dismiss();
}
というのは、listnerには、自分自身(this)が指定してあるからです。実はEditItemは、ActivityであるとともにRunnableでもあります。51行目の宣言では、
public class EditItem extends Activity implements Runnable
としてあります。
Runnableであるthisに対して、Handler.post(listener)を実行すると、EditItemクラスのrunメソッド(DoSortクラスにもrunメソッドがあることに注意)が実行されるわけです。
本当は、thisを使わずに別途Runnableクラスを作り、そのなかにrunメソッドと同等な処理を記述しておいて、これをスレッドに引数として渡し、スレッド内でHandler.postメソッドを使って起動するというのが、正しいやり方なのでしょうが、スレッドはここ1つだけでもあり、記述を簡単にするためにEditItemをRunnableにしてしまいました。
Handlerクラスは、Androidのオブジェクトです。内部にキューを持っていて、キューに入ったRunnableなオブジェクトを順番にメインスレッドで実行するものです。これが用意されているのは、前述のようにメインスレッドだけがGUI操作が可能なため、今回のProgressDialogのような操作を別スレッドから行なうためなのです。
では、上のリストからEditItemクラスのrunメソッドを見てみましょう。ここでは、EditItemにあるリストを更新して、最後にdialog.dismiss()を実行しています。dialogは、ProgressDialogなので、これで表示されているProgressDialogが止まり、メインスレッドに戻ってきます。
さて、次回はAndroidのGUIパーツの中でも一番複雑なListViewについて解説する予定です。
この連載の記事
-
第11回
スマホ
アプリケーションをAndroidマーケットに登録する -
第10回
スマホ
ブロードキャストへの応答とタイマ割り込み -
第9回
スマホ
Androidアプリで複数の項目を表示するリストビューを使う -
第7回
スマホ
Androidアプリの設定画面を作成する -
第6回
スマホ
Androidアプリ内で表示されるメニューを作成する -
第5回
スマホ
インテントによるアプリケーションとアクティビティの呼出し -
第4回
スマホ
Androidアプリを構成する「アクティビティ」を実際に作る -
第3回
スマホ
アプリケーションの基本となる「アクティビティ」 -
第2回
スマホ
開発したアプリをエミュレーターやデバッガ上でテストする -
第1回
スマホ
Androidアプリの開発環境であるEclipseの使い方を知る - この連載の一覧へ