Locked History Actions

Android/Activity

Activty

Activityのライフサイクルに関する間違い

http://developer.android.com/reference/android/app/Activity.html

に以下のような図があるが、

http://developer.android.com/images/activity_lifecycle.png

onPauseの前の「Another Activity comes in front of the activity」という部分は間違い、あるいは間違いで無いとしても非常に紛らわしい。 onPauseが呼び出される以前には、Another Activityオブジェクトは生成されてもいないのである。これは、後述のonPauseメソッドの説明からも明らかである。 翻訳:「AのonPauseから返ってこない限りBはcreateされないため、ここで長い処理は行ってはならない」。

実際にトレースをおこなってみると、説明の通り、BのonCreateが呼び出されるのは、AのonPauseの後である。つまり、以下の順序である。

  • A.onPause
  • B.onCreate
  • B.onStart
  • B.onResume
  • A.onStop

(A.onPause以前にBオブジェクト自体が作成されている可能性はあるかもしれないが)B.onCreateが呼び出されるのはA.onPauseの後であるため、 A.onPause以前にBが実行可能状態になることは有り得ない。

考えてみればこれは当たり前のとで、A.onPause以前にBが「実行」されてしまうと、同時に二つのActivityが動作することになってしまう。 これではシステム的にもアプリケーションプログラミング的にも複雑になってしまう。

これらをきちんと確認せずに「Activityのライフサイクル」を上の間違った図を元にして説明しているサイトが異常に多い(というよりほとんど)ので注意が必要である。

また、この図にはConfigurationChangeの場合の遷移が記述されていない。すなわち、端末を回転したりした場合には、 通常、表示中のアクティビティインスタンスが単純に破棄されて新たなインスタンスが生成される。 つまり、

  • A.onPause
  • A.onStop
  • A.onDestroy
  • A'.onCreate
  • A'.onStart
  • A'.onResume

となる。はずだが、なぜか

  • A.onPause
  • A.onStop
  • A.onDestroy
  • A'.onCreate
  • A'.onStart
  • A'.onResume
  • A'.onPause
  • A'.onStop
  • A'.onDestroy
  • A".onCreate
  • A".onStart
  • A".onResume

となってしまう(こちらのバグ?)。

これらのメソッドの意味

なぜこんなにたくさんのメソッドが用意されているか、それらの用途は何なのかが疑問である。

http://developer.android.com/guide/topics/fundamentals.html

によると、要するに

  • onStart ~ onStop:Activityが可視の状態。
  • onResume ~ onPause:Activityがユーザ操作を受けられる状態。

すなわち、onStart~onResume、onPause~onStopの期間は「表示されているけれども、ユーザの操作を受け付けない状態」である。 つまりこれは、通常のウインドウシステムにおけるモーダルダイアログが表示されている状態であると考えてよい。

通常のウインドウシステムの場合、これらの期間にウインドウコンポーネントの内容変更を行うと、それらはすぐに表示されるのだが、Androidの場合でも 同じなのだろうか?例えばこの期間にTextViewに対してsetTextを行うと、その表示は変更されるのであろうか?試験方法が不明なので試していない。

ただし、Activityの状態に関わらず、GUIスレッドとは別途に作成した独自のスレッドは動作し続けることができるようである。 もちろんこれはAndroidとは無関係なJava側の仕様であるので当然だろう。

ただし、onPauseの説明にあるように、onPauseメソッドが呼び出された後は、そのアプリケーションプロセス自体がいつでもkillされる可能性がある。 したがって、onStopやonDestroyで「何か」をしようと思うのは意味がない。これらのメソッドは無視されるべきである。 onPauseはアプリの終了と同様であると考えなくてはいけない。

つまり、onPause~onStopの期間にActivityの表示を変更するのは意味が無いということになる。 何かの表示を行っても、それをユーザが見逃したらこれもまた問題だろう。

また、Activityが作成されてから最初のonResumeに到達するまでのこれらのメソッドはほぼ意味がない。 なぜなら、Activityが作成されたにも関わらず、それがonResumeにまで到達しないというのは極めて稀なケースと思われるからだ。 つまり、新たなActivityに遷移しようとしてユーザの操作が可能になるまでの間に、電話がかかってきてメモリ不足になるとか、電源ボタンが押されてしまうなどのケースである。

マニュアルにはonStartでBroadcastReceiverの登録などを行えとあるが、そもそもこの機能自体が特別なアプリケーション向けのものであり、 通常はこのようなものは使わないだろう(電話着信やMail着信に反応するアプリがそんなに沢山あってははうざくて仕方がない)し、 そもそもユーザとの対話ができない状態なのに、それらの通知を受け取って何の意味があるのだろうか?

また、onRestart無しのonStartはアクティビティの一生の中でただの一度だけしかありえない。 したがって、onRestartなど不要であり、onStartかonResumeの引数にフラグでも付ければよい話である。

ふつうのアプリのためのライフサイクルメソッドの省略の仕方

以下のメソッドがあれば十分である。

  • onCreate:Activityのセットアップを行う。View等の中身は空でよい。データを設定する必要はない。
  • onResume:初めてなのかonRestart後であるのかのフラグを渡す。表示すべきデータがあれば、Viewに設定する。
  • onPause:プリファレンス等をセーブする。

ちなみに、onCreateはともかく、あとの二つの名称は極めて不適当。 onActivation,onActivated, beforeActivationなどにすべきだろう。 なぜわざわざこんなわかりにくい名称にするのだろうか?

Activityのライフサイクル翻訳

※以下はhttp://developer.android.com/reference/android/app/Activity.htmlの一部の翻訳

onCreate

protected void onCreate (Bundle savedInstanceState) Since: API Level 1

Called when the activity is starting. This is where most initialization should go: calling setContentView(int) to inflate the activity's UI, using findViewById(int) to programmatically interact with widgets in the UI, calling managedQuery(android.net.Uri, String[], String, String[], String) to retrieve cursors for data being displayed, etc.

You can call finish() from within this function, in which case onDestroy() will be immediately called without any of the rest of the activity lifecycle (onStart(), onResume(), onPause(), etc) executing.

Derived classes must call through to the super class's implementation of this method. If they do not, an exception will be thrown. Parameters savedInstanceState If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle). Note: Otherwise it is null. See Also

  • onStart()
  • onSaveInstanceState(Bundle)
  • onRestoreInstanceState(Bundle)
  • onPostCreate(Bundle)

onRestart

onStop()の呼び出し後、現在のアクティビティが再表示されたとき(ユーザがそこにバックしたとき)に呼び出される。 引き続き、onStart(), onResume()が呼び出される。

もしアクティビティがRaw Cursorオブジェクト(managedQuery(android.net.Uri, String[], String, String[], Stringの代わりに)を使用しているのであれば、 この場所が通常は再クエリを行うのにふさわしい場所である(なぜなら、onStop()にて非アクティブにしているであろうから)。

派生クラスはスーパークラスの同メソッドを呼び出さなければならない、さもないと例外が発生する。

onStart

onCreate(Bundle)あるいは、アクティビティがストップして、再度ユーザに表示されるときのonRestart()の後に呼び出される。 このメソッドの後にonResume()が呼び出される。

派生クラスはスーパークラスの同メソッドを呼び出さなければならない。さもないと、例外が発生する。

onResume

protected void onResume () Since: API Level 1

Called after onRestoreInstanceState(Bundle), onRestart(), or onPause(), for your activity to start interacting with the user. This is a good place to begin animations, open exclusive-access devices (such as the camera), etc.

Keep in mind that onResume is not the best indicator that your activity is visible to the user; a system window such as the keyguard may be in front. Use onWindowFocusChanged(boolean) to know for certain that your activity is visible to the user (for example, to resume a game).

Derived classes must call through to the super class's implementation of this method. If they do not, an exception will be thrown. See Also

  • onRestoreInstanceState(Bundle)
  • onRestart()
  • onPostResume()
  • onPause()

onPause

アクティビティがバックグラウンドに移行しようとするとき(ただし、まだkillされていない)、アクティビティのライフサイクルの一部として呼び出される。 onResume()の逆である。

アクティビティAの上にアクティビティBが起動されたとき、Aにおいてこのコールバックが起動される。 AのonPauseから返ってこない限りBはcreateされないため、ここで長い処理は行ってはならない。

This callback is mostly used for saving any persistent state the activity is editing, to present a "edit in place" model to the user and making sure nothing is lost if there are not enough resources to start the new activity without first killing this one.

アニメーションやその他のCPUを消費しがちな処理を停止するためのタイミングとしても良い場所である。 なぜなら、次のアクティビティにできる限り早く移行するため、あるいはカメラのような排他的リソースに対するアクセスをクローズする必要があるからである。

システムがメモリを必要とする場合には、pauseされたプロセスをkillする場合がある。 このため、このメソッドから戻る時には、必要なすべての状態がセーブされていなければならない。 一般的には、アクティビティ中の永続的状態をセーブするにはonSaveInstanceState(Bundle)が使用され、 このメソッドはグローバルな永続的データをContent Providerやファイル等保存するのに使用される。

この呼び出しを受けた後、通常はそれにひき続いてonStopの呼び出しを受けることになる (そのまえに次のアクティビティがresumeされて表示されるが)。 しかしながら、stop状態にならずにそのままonResumeが呼び出されることもある。

※訳注:どのような場合にこれが発生するのか何も説明が無い。

派生クラスはスーパークラスの同メソッドを呼び出す必要がある。そうしなければ例外が発生する。

onStop

非表示になったときに呼び出される。このメソッドの次はonRestart()あるいはonDestroy()が呼び出されるか、あるいは何も呼び出されない。 これは後のユーザの操作による。

このメソッドが全く呼び出されない場合もありうる。システムのメモリ不足状態でonPauseの呼び出し後にもはやアクティビティのプロセス状態を保持できなくなった場合である。

派生クラスはスーパークラスの同メソッドを呼び出さなければならない。そうしなければ例外が発生する。

onDestyroy

Activityが破棄されるときの最終的なクリーンアップを行う。 これは、(誰かがfinish()を呼び出したために)アクティビティが終了しようとしている場合か、システムがスペースを空けるためにアクティビティのインスタンスを破棄する場合である。いずれのシナリオであるかは、isFinishing()メソッドを呼び出すことによって区別できる。

※訳注:ユーザがバックボタンを押したときはfinish()になるのか?

注意:このメソッドがデータセーブのために呼び出されるとの想定をしてはならない! 例えば、アクティビティがコンテントプロバイダのデータを編集する場合、それらはonPause()あるいはonSaveInstanceState(Bundle)でコミットされるべきであって、 ここではない。 このメソッドは通常、スレッドなど、アクティビティにassociatedしているリソースを解放する場所である。 アプリケーションはその後も動作するのであるから、破棄されるアクティビティがこのようなものを残していってはいけないのだ。 システムは単純にアクティビティの持ち主であるプロセスを、このメソッド(あるいは他のメソッドも)を呼び出すこと無しに単純にkillすることがある。 したがって、プロセス終了後に何かを残す目的でこのメソッドを用いてはいけない。

派生クラスはスーパークラスのメソッド実装を呼び出すこと。さもなければ例外が発生する。

※訳注というか意見:スレッドをアクティビティに結びつけておくのはそもそも悪い設計である。なぜなら、アクティビティは(端末の回転などによって)いつ破棄・再生成されるかわからないのであり、アクティビティのライフサイクルとは全く別にスレッドすなわちバックグラウンド処理が設計されなければならない。したがってonDestroyメソッドには価値がない。