2011年2月16日水曜日

android 開発はマジ難しい

 だいぶ Android 開発の肝も心得てきました。現在販売されている Android 書籍を見ても、ネット上にある Android の開発情報を見ても、まともな Android のコードを書けているのは、10パーセントにも満たないような気がしています。
どこが難しいかは、やはり、イベント絡みでスレッドを意識しなくてはならない事にあります。同じイベントでも、カメラのコールバックと、GPSのイベント通知では機構が異なり、ライブラリの設計そのものが変です。
カメラのコールバック Camera.takePicture で登録したイベントは、推定、Android 実機の実装に依存しそうで、IS03 では、Activity のメインスレッド外で発生します。そのため、巷のコードはアプリケーション・エラーでハングするか、ハングする可能性が高いものが流通しています。 IS03で、カメラをオープンしているアプリがハングすると、どうなるか?電源を切って再起動するまで、カメラが開けません。
Camera.startPreview のコードは、Camera.takePicture の直後に置いては、いけません。スレッドの競合を起こします。 もし、Camera.startPreview が Activity のメインスレッドから呼ばれなければならないのであれば、Handler を使ってコールするように Runnable を post する必要があります。この辺の基準は、正直わかりません。何故なら、Android のライブラリ設計者も、よくわかっていない節があるからです。

Handler は特殊で、ハンドラを生成したメッセージループ Looper スレッドに作用します(実行時のカレントスレッドに作用します)。Handler を保持するクラスに作用するのではありません。従って、別スレッドから、メインスレッドで実行したいコードは、Runnable クラスとして、Handler に post させます。

宣言は、どこでも良いが、スコープには注意した方が良い(Javaは関係ない?)という意味で同一クラスに置く。
Handler handler_ = null;
 しかしそして、Looper.loop(); の実態があるメインスレッドにて
handler_ = new Handler();
する必要があります。Activity に限定すれば、@Override を施したメソッド内で new Handler() をすればよい。ただし、生成は1回のみで十分なので、自ずと onCreate() や、メンバ変数宣言で Handler handler_ = new Handler(); するのが妥当。
ルーパースレッドでは、
public void run() {
    Looper.prepare(); 
    handler_ = new Handler();
    Looper.loop();
  }
とするのが良いでしょう。

 これに対して、Callback 系のメソッドや、ワーカースレッドなどの別スレッドから、UI に関わるようなメインスレッドで実行しなければならないコードは、
public void postHoge() {
    handler_.post(
      new Runnable() {
        @Override
        public void run() {
           // メインスレッドで実行したいコード
           hoge();
        }
      }
    );
  }
というコードを用意しておいて、別スレッドから
// Activity のメソッドであれば
  postHoge();
  // ルーパースレッドのメソッドであれば
  looperThread_.postHoge();

というようにコールすれば良いようです。

ルーパースレッドを終了させるスマートな方法は、ルーパースレッドのメソッドとして
public void postExit() {
    handler_.post(
      new Runnable() {
        @Override
        public void run() {
          Looper.myLooper().quit();
        }
      }
    );
  }
定義したものを、looperThread_.postExit(); してやる事です。looperThread_.interrupt(); するよりは良いでしょう。
まだ、Handler の使いかたは、いろいろあるようですが、難しすぎです。

追記:
2011/02/16 修正 どこでもよい -> 同一クラスに置く,
2011/02/20 修正 どこでもよい -> OK
handler_.post() に相当する関数 runOnUiThread なるものがあるらしい。こいつを使うと
  Handler handler_ = new Handler();
を省略できて、かわりに
  public void postHoge() {
    runOnUiThread(
      new Runnable() {
        @Override
        public void run() {
           // メインスレッドで実行したいコード
           hoge();
        }
      }
    );
  }
と、できるようだ。

0 件のコメント: