アクティビティは、Android の 4 つの主要コンポーネントの 1 つであり、Android の最も基本的な部分であり、非常に重要な部分です。アクティビティのライフ サイクルと起動モードを理解すると、よりスムーズなプログラムを設計できます。
この記事では主に、Activity のライフ サイクルと起動モードに関する私の調査を記録し、実践から理論を習得します。
1. 基礎知識
1.スタックを返す
Android は、リターン スタックを通じて Activity を管理します。アクティビティが開始されるたびに、アクティビティはスタックに置かれ、スタックの一番上に置かれます。アクティビティが破棄されると、アクティビティはスタックの一番上から削除されます。
な
な
したがって、インターフェイスのフォアグラウンドにあり、ユーザーとやり取りできるアクティビティは、リターン スタックの一番上にある必要があります。return または finish() を押して Activity を終了すると、前の Activity が表示されます。
2.ライフサイクル
アクティビティには 4 つの状態があります。
-
稼働状況:
リターン スタックの上部にあるアクティビティが実行されています。
-
中断状態:
アクティビティがスタックの一番上になく、まだ表示されている場合は、一時停止状態です。
-
停止状態
Activity がスタックの一番上になく、表示されていない、停止状態です。この時点で、システムはまだアクティビティの一部の変数をメモリに保持している場合があります。
-
破壊された状態
Activity がリターン スタックから削除されると、破棄された状態になります。
サスペンド状態とストップ状態の違いに注意してください。スタックの一番上にないアクティビティがまだ表示されているのはなぜですか? これについては後で詳しく説明します。
アクティビティには、ライフ サイクルを管理するための合計 7 つのコールバック メソッドがあります。
-
onCreate():
Activity の作成時に呼び出されます。この時点で、レイアウト バインディング イベントの読み込みなど、いくつかの初期化操作が完了します。
-
onStart():
Activity が非表示から表示に変わるときに呼び出されます。
-
onResume()
Activity がユーザーと対話する準備ができたときに呼び出されます。この時点で Activity は stack の一番上にある必要があります。
-
onPause()
システムが別のアクティビティを開始または再開しようとしているときに呼び出されます。この時点で、システムは一部のリソースを解放し、一部の重要なデータのみを保持します。
-
onStop()
Activity が非表示になると呼び出されます。
-
onDestroy()
Activity がバック スタックから削除されるときに呼び出されます。
-
onRestart()
Activity が停止状態から実行状態に変化したときに呼び出されます。
3. スタートモード
アクティビティには 4 つの起動モードがあります
-
標準
これは、アクティビティを開始するデフォルトの方法です。このモードでは、アクティビティが開始されるたびに、アクティビティが以前のリターン スタックに存在するかどうかに関係なく、新しいアクティビティが作成され、スタックの一番上に配置されます。
-
シングルトップ
アクティビティを開始すると、返されたスタックの一番上が開始するアクティビティであるかどうかがシステムによって判断されます。はいの場合、新しいものを作成する必要はもうありません。そうでない場合は、スタックの上に新しいアクティビティを作成します。
-
シングルタスク
このモードでは、システムはスタックに戻り、Activity のインスタンスがあるかどうかを確認します。ステーションでインスタンスが見つかった場合は、スタックからインスタンスの前にあるすべてのアクティビティをポップして、インスタンスがスタックの一番上にくるようにします。
-
単一インスタンス
アプリケーションはリターンスタックに対応するためです。アクティビティが複数のアプリケーションから呼び出される場合、以前のメソッドは共有できません。singleInstance モードでは、singleInstance モードのアクティビティを管理するための独立したスタックがあります。
以前の基本的な知識は、ぎくしゃくしたテキストの説明です。理解するのは難しいので、以下は実践を通じてこの知識を理解することから始まります。
2.実用部分
ライフ サイクル コールバック関数がいつ呼び出されたかを明確に確認できるようにするために、まず独自の BaseActivity を記述し、ライフ サイクル コールバック関数にログ出力を追加します。次のアクティビティは、自分で作成したアクティビティを継承できます。
open class BaseActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(this.toString(), "onCreate()")
}
override fun onStart() {
super.onStart()
Log.d(this.toString(), "onStart()")
}
override fun onResume() {
super.onResume()
Log.d(this.toString(), "onResume()")
}
override fun onPause() {
super.onPause()
Log.d(this.toString(), "onPause()")
}
override fun onStop() {
super.onStop()
Log.d(this.toString(), "onStop()")
}
override fun onDestroy() {
super.onDestroy()
Log.d(this.toString(), "onDestroy()")
}
override fun onRestart() {
super.onRestart()
Log.d(this.toString(), "onRestart()")
}
}
1.ライフサイクル
3 つのアクティビティを用意しました。
-
ライフアクティビティ
-
通常のアクティビティ
-
ダイアログ アクティビティ
次に、LifeActivity にボタンを追加して、他の 2 つのアクティビティにジャンプできるようにします。
btn_startNormalActivity.setOnClickListener {
val intent = Intent(this, NormalActivity::class.java)
startActivity(intent)
}
btn_startDialogActivity.setOnClickListener {
val intent = Intent(this, DialogActivity::class.java)
startActivity(intent)
}
その中の DialogActivity はダイアログ アクティビティです。AndroidManifest.xml に指定
<activity
android:name=".activityStudy.DialogActivity"
android:theme="@style/Theme.AppCompat.Dialog"
android:label="dialogActivity"/>
android:theme="@style/Theme.AppCompat.Dialog" は、アクティビティがダイアログ スタイルのアクティビティであることを指定します。
1.1 LifeActivity の起動と破棄
最初にプログラムを起動して見てみると、最初の 3 つのコールバック関数が見つかりました。これが Activity の作成三部作です。
リターン キーが押されると、完全なライフ サイクルが発生します。
1.2 通常のアクティビティを開始する
LifeActivity から NormalActivity を開始します。
ここで、まず LifeActivity が onPause() メソッドを呼び出していくつかのリソースを解放することがわかります。しかし、現時点では LifeActivity はまだ表示されています。LifeActivity は、NormalActivity が三部作の作成を完了すると、onDestroy() メソッドを呼び出します。
それでは、NormalActivity からもう一度試してみましょう。
まだスタックの一番上にある NormalActivity が最初に onPause() メソッドを呼び出し、次に LifeActivity がスタックの一番上になった後で onStop() と onDestroy() を呼び出すことがわかります。
ここでの違いは、LifeActivity は以前に破棄されたことがなく、停止された状態であるため、この時点でスタックのトップを復元するための呼び出しシーケンスは onRestart() -> onStart() -> onResume() であるということです。
1.3 ダイアログ スタイルのアクティビティを開始する
それでは、起動ダイアログを試してみましょう。起動後はこんな感じ
この DialogActivity は画面全体をカバーしておらず、LifeActivity は引き続き表示されます。ただし、現時点では LifeActivity はユーザーと対話できません。つまり、スタックの一番上にありません。この時点で、LifeActivity は停止状態です。
コールバック関数を見ると、現時点では LifeActivity が onStop() を呼び出していないことがわかります。
これがサスペンド状態とストップ状態の違いです。
リターン後の関数呼び出し
LifeActivity がスタックの一番上に戻る必要がある場合、onResume() 関数を呼び出すだけで済みます。
1.4 まとめ
アクティビティの作成三部作は onCreate() onStart() onResume() です
アクティビティ A からアクティビティ B を開始する場合、最初に A の onPause() を呼び出し、次に B が作成されるのを待ちます。
B が画面全体を埋めて A を完全に非表示にする場合、A の onStop() が呼び出されて A が停止します。このとき、A を復元する場合は、onRestart() -> onStart() -> onResume() の手順に従います。
B が単なるダイアログ スタイルのアクティビティである場合、A は一時停止状態になります。A が復元される場合、 onResume() のみが呼び出されます
2.スタートモード
ここでは引き続き LifeActivity を使用し、LifeActivity を開始するボタンを追加します。
val intent = Intent(this, LifeActivity::class.java)
startActivity(intent)
2.1 標準モード
この時点で、LifeActivity はデフォルトの標準モードになっています。試してみましょう。
ここで 2 つの LifeActivity は同じインスタンスではなく、新しい LifeActivity が作成されていることがわかります。
独自の操作を開始することはまれですが、このモードでは多くのリソースを消費し、システム内に複数のインスタンスが存在します。
2.2 singleTop モード
次に、LifeActivity の起動モードを変更し、singleTop モードに変更します。
AndroidManifest.xml を変更します。
<activity android:name=".activityStudy.LifeActivity"
android:label="lifeActivity"
android:launchMode="singleTop"/>
もう一回やってみよう。
何度クリックしても、onPause() および onResume() メソッドだけが呼び出されることがわかります。ロゴも同じで、同じインスタンスであることを示しています。
次に、何か新しいことをしなければなりません。
LifeActivity から MiddleActivity にジャンプできる新しい MiddleActivity を追加します。MiddleActivity は、LifeActivity とそれ自体にジャンプすることもできます。
次に、これを行いましょう: LifeActivity -> MiddleActivity -> LifeActivity
C:\Users\Joker\AppData\Roaming\Typora\typora-user-images\image-20211227225906078.png
ログによると、同じインスタンスではなく、新しい LifeActivity が実際に作成されています。
この時点で、リターン スタックは次のようになります。
この時点で、スタックの一番上にある LifeActivity は元の Activity ではありません。これは singleTop モードの制限です。
このときリターンキーを押せばMiddleActivityに戻り、再度リターンキーを押すと元のLifeActivityに戻ります。上記のリターン スタックの図に従って理解できます。
2.3 singleTask モード
次に、LifeActivity の起動モードを変更してみましょう。singleTask で試してみてください。
ここで、LifeActivity が同じインスタンスであることがわかります。そして真ん中のMiddelActivityは破棄されています。
この時点でのリターン スタックは次のようになります。
したがって、この時点で戻るボタンを押すと、MiddleActivity には戻らず、LifeActivity を直接破棄します。
他のアクティビティがいくつあっても、最終的にはスタックから取り除かれます。
ここでは、6 つの MiddleActivities を MiddleActivity で開始します。それらがすべて破壊されていることがわかります。
このモードでは、スタック全体にインスタンスが 1 つしかないことを保証できますが、すべてのアクティビティがスタックより上にポップされるため、特定のリスクがあります。
2.4 シングルインスタンス モード
これは最も特殊な起動モードです。アプリケーションはリターン スタックに対応し、singleInstance モードのアクティビティは追加のリターン スタックによって一元管理されます。
アクティビティが複数のアプリケーションで使用される場合。
ここでは、3 つの新しいアクティビティを使用して練習します。
-
最初のアクティビティ
-
SecondActivity の起動モードは singleInstance です
-
サードアクティビティ
次に、それらを順番に開始し、ログに taskId を出力します
Log.d(this.toString(), "task id is $taskId")
FirstActivity と ThirdActivity は同じリターン スタックに属し、SecondActivity は別のリターン スタックに属していることがログからわかります。
また、FirstActivity から SecondActivity へ、および SecondActivity から ThirdActivity へジャンプすると、以前のように同じアプリケーション内でジャンプするのではなく、アプリケーションの切り替えに似た明らかなアニメーションが表示されます。
そして、ThirdActivity からクリックして戻ると、最初に FirstActivity に戻り、次に SecondActivity に戻ります。予想される返品注文ではありません。この時点で、リターン スタックは次のようになります。
したがって、ThirdActivity から戻るときは、task54 に従って戻ります。task54 が空になると、task55 のスタックの一番上に到達します。
したがって、このモードでは、FirstActivity の下に多くのアクティビティがある場合、これらのアクティビティがすべて破棄された後に SecondActivity に戻ります。したがって、このモードは最初の 3 つのモードとは大きく異なります。
3. 質問する
1. 停止状態から再開するアクティビティが onRestart() を呼び出すのはなぜですか?
前のプラクティスから、Activity が中断状態から実行状態に再開するときは、onResume() のみを呼び出す必要があると結論付けることができます。停止状態から再開する場合、onStart() と onRestart() を呼び出す必要があるだけでなく、onRestart() も最初に呼び出されます。何故ですか?この関数は何をしますか?
2. singleInstance モードの独立スタックは、singleTop モードまたは singleTask モードを使用しますか?
singleInstanceモードのActivityは統合スタックで管理されているので、スタックの管理方法はsingleTopかsingleTaskかそれ以外か?
ここでの推測は、singleTask モードである必要があります。結局のところ、インスタンスがグローバルに 1 つだけであることを確認する必要があります。
4. 疑問を探る
1.onRestart() 呼び出し
この部分にはソース コードの分析が含まれます。これについては、別のメモを使用して詳しく説明します。
2. singleInstance でのスタックの管理モード
ここでは、前のものに基づいて別の ForthActivity を導入し、起動モードを singleInstance に設定しました。
FirstActivity の起動モードは singleTop に設定されています。
FirstActivity から SecondActivity と ForthActivity にジャンプできるようになり、SecondActivity と ForthActivity にも FirstActivity にジャンプするボタンがあります。
ということで一連の作戦が始まりました。FirstActivity から SecondActivity にジャンプしてから FirstActivity に戻り、FirstActivity から ForthActivity にジャンプします。当初、この操作によって形成される戻りスタックの状況は次のようになると考えていました。
その結果、ログを確認したところ、それぞれのスタックで互いに干渉していないことがわかりました。
オリジナルの singleInstance 管理方法は、このアクティビティに独立したスタックを割り当てることです。この起動モードに複数のアクティビティがある場合、それぞれのスタックが複数存在します。これにより、グローバルな一意性も保証され、singleTop か singleTask かに関係なく、スタックにはこの Activity が 1 つしかありません。