... class FooActivity extends Activity { public static Hoge hoge_ = null; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); hoge_ = new Hoge(); } @Override protected void onDestroy() { hoge_.close(); super.onDestroy(); } ... }みたいな事をやってるんです。フッ、static な global 変数ですか?って笑われるかもしれませんが、正直、あちこちの Activity が関連を持って利用しなければならないとなると、こうするより他無いんです。こんなんで共有できてしまうのは、将来的に地雷を踏む可能性もあります。だって、アプリケーション全体でデータを共有するためには、どうしたらいいですか?に対する回答は、「ApplicationBean を使いましょう」だからです。データは、Parcelable で受け渡しすべきです。さて、この android.app.Application ですが、onDestroy に相当するメソッドがありません。正直言って使えません。また、画面の回転でレイアウトが変わる度に onDestroy と onCreate が呼ばれるので、うざくてしょうがありません。だから、私はダミーの BaseActivity を作成して、そこで共通リソースの初期化と破棄を行なってます。そうすれば、MainActivity の onDestroy と onCreate が何回呼ばれようが気にしなくて済みます。 話を巻き戻して、SingleTask なのに一時的に2重起動してもプロセスが別なら問題ないんじゃないですか?と思うやないですか?ところが、違うんです。この2つのアプリケーションから hoge_ が共有できてしまっており、先に終了したアプリの onDestroy よりも先に、後から起動したアプリの onCreate がコールされる状況があるんです。いや、もうビックリですね?しょうがないので、
... class FooActivity extends Activity { public static Hoge hoge_ = null; private static int process_count_ = 0; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if( 0 == process_count_ ) { ++process_count_; hoge_ = new Hoge(); } else { ++process_count_; } } @Override protected void onDestroy() { --process_count_; if( 0 == process_count_ ) { hoge_.close(); } super.onDestroy(); } ... }という感じで、プロセス?カウントをとって、対処してます。ちなみにタイミングがシビアじゃ無いので、カウントの保護は一切行なっていません。 追記:static 変数の記述場所を間違えてたので修正しました。やっぱりカウンタは同期保護した方が良いかもしれません。synchronized(this)ではダメっぽい気がしますが、突っ込んでません。やるとすれば private static Object sync_ = new Object(); しておいて、synchronized(sync_) でしょうか?
訂正: final 修飾子が余計だったのを取った。
2012/01/15 追記: どうも、Activity 再開時の挙動など、singleTask 等で細かく異なるようである。参考になるサイトを見つけた。こちらも目を通した方が良いです。一旦 HOME ボタンを押した後に再開すると、singleTask では BaseActivity の onDestroy がコールされるので思わしくない。
2012/01/17 追記: BaseActivity に Manifest で android:screenOrientation="landscape" と指定するようにした。 BaseActivity -> MainActivity と表示している時にレイアウトの縦横が変更された時、MainActivity だけが変更の影響を受けるわけではなく、BaseActivity にまで変更の影響を受けている事がわかった。そのせいで、BaseActivity の onDestroy と onCreate がコールされる事になる。この辺の挙動は、かなりトリッキーだ。BaseActivity の onCreate 時に MainActivity を起動しているのだが、MainActivity が縦横変更時にどんどん増殖するという恐ろしい事態を引き起こしていた。現在は、singleInstance で実装している。
2012/08/29 追記:Android SDK における sqlite3 のコードが、データベースを開いたプロセスと閉じたプロセスが同一でないと、エラーで動作しないようなチェックが入っております。なので、最終的には、2重起動を自前で禁止するために、process_count_ >= 2 ならば、アプリケーションを終了するようにしました。 一方のプロセスがデータベースを閉じるまで待ってからオープンしようと synchronized メソッドでカウントを検知したら、そこから抜けてThread.sleep(200); とか入れてみたんですが、プロセス|スレッドのコンテキストが全く切り替わりません。このため、2重起動を自前で防ぐコードを選択せざるを得ませんでした。こういうのは、singleInstance を指定しているんだから、アプリがやる仕事じゃ無いと思います。
0 件のコメント:
コメントを投稿