Activityはライフサイクルを持っています。ライフサイクルは「画面の作成~表示~破棄」という一連の流れのことです。
アプリケーションはこの流れに沿ってプログラミングします。流れに逆らったプログラムは作れません。
アプリケーションの開発者にとって、ライフサイクルの理解は最重要です。
Activityのライフサイクルについてまとめます。
目次
Activityライフサイクル
ライフサイクルとは
Activityはアプリケーションコンポーネント(アプリの部品)の1つで、主な役割は一枚の画面表示をすることです。
Activityはアンドロイドシステムにより制御される「画面の作成~表示~破棄」という一連の流れを持っています。そして、画面が切り替わる毎に繰り返されています。
「画面が切り替わる」とは、例えば次のような場面です。
アプリを起動・終了・再開
アプリ内で画面を移動(新規画面へ移ったり・元仮面へ戻ったり)
他のアプリを起動、元のアプリへ復帰
etc …
この「一連の流れ」のことを、生物の一生に見立てて「ライフサイクル」と呼びます。
ライフサイクルの状態遷移
アプリ内のActivityインスタンスはライフサイクルのさまざまな状態を遷移します。この状態遷移をActivityが認識できるように、コールバックが設けられています。
その様子を表したのが下図です。矢印上に遷移の条件を記載しています。

ケース別の状態遷移
ライフサイクルの状態遷移が「どのような経路を通るか?」は、画面が切り替わる場面で異なります。
それを整理すると表のようになります。数字は遷移の順番です。
| ケース | ![]() |
|||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Activityの起動 | 1 | 2 | 3 | |||||||||
| Activityの終了 | 1 | 2 | 3 | |||||||||
| startActivity( )で Sub Activityの起動(Bgへ) | 1 | 2 | 3 | |||||||||
| Sub Activityの終了(Fgへ) | 1 | 2 | 3 | |||||||||
| startActivityForResult( )で Sub Activityの起動(Bgへ) | 1 | 2 | 3 | |||||||||
| Sub Activityの終了(Fgへ) | 1 | 2 | 3 | 4 | ||||||||
| 他のアプリ起動(Bgへ) | 1 | 2 | 3 | |||||||||
| 履歴(Recent)から復帰(Fgへ) | 1 | 2 | 3 | |||||||||
| 端末の回転 | 5 | 6 | 7 | 8 | 1 | 2 | 3 | 4 | ||||
| 端末の電源Off | 1 | 2 | 3 | |||||||||
| 端末のSeeep | 1 | 2 | 3 | |||||||||
| 端末のWakeup | 1 | 2 | 3 | |||||||||
| Activityの起動(SINGLE_TOP) | 2 | 3 | 1 | |||||||||
| ※Fg:フォアグラウンド、Bg:バックグラウンド ※API<28の場合、onSaveInstanceState ⇒ onStopの順番 |
||||||||||||
| ケース | ![]() |
|||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Activityの起動 | 1 | 2 | 3 | |||||||||
| Activityの終了 | 1 | 2 | 3 | |||||||||
| startActivity( )で Sub Activityの起動(Bgへ) | 1 | 3 | 2 | |||||||||
| Sub Activityの終了(Fgへ) | 1 | 2 | 3 | |||||||||
| startActivityForResult( )で Sub Activityの起動(Bgへ) | 1 | 3 | 2 | |||||||||
| Sub Activityの終了(Fgへ) | 1 | 2 | 3 | 4 | ||||||||
| 他のアプリ起動(Bgへ) | 1 | 3 | 2 | |||||||||
| 履歴(Recent)から復帰(Fgへ) | 1 | 2 | 3 | |||||||||
| 端末の回転 | 5 | 6 | 7 | 8 | 1 | 3 | 2 | 4 | ||||
| 端末の電源Off | 1 | 3 | 2 | |||||||||
| 端末のSeeep | 1 | 3 | 2 | |||||||||
| 端末のWakeup | 1 | 2 | 3 | |||||||||
| Activityの起動(SINGLE_TOP) | 2 | 3 | 1 | |||||||||
| ※Fg:フォアグラウンド、Bg:バックグラウンド ※API≧28の場合、onStop ⇒ onSaveInstanceStateの順番 |
||||||||||||
API≧28において、onSaveInstanceStateの順番がonStopと入れ替わっています。ドキュメントに次の説明があります。
——
onSaveInstanceStateが呼び出された場合、このメソッドはBuild.VERSION_CODES.P以降を対象とするアプリでonStopの後に発生します。以前を対象とするアプリケーションでは、このメソッドはonStopの前に発生し、…
特殊な状態1:onActivityResult
新しく起動したActivity(図のSub)が終了する時に、結果を起動元のActivity(図のMain)へ返すことが出来ます。

その時、Activityの起動はActivity#startActivityForResult( )を用います。Activityの結果はActivity#onActivityResult( )コールバックの引数に返ります。
companion object {
const val REQUEST_SUB = 111
const val REQUEST_KEY = "RequestKey"
}
...
override fun onCreate(savedInstanceState: Bundle?) {
...
findViewById<Button>(R.id.btnSubReturn).setOnClickListener {
val _intent = Intent(this, SubActivity::class.java).apply {
putExtra(REQUEST_KEY, "1234")
}
startActivityForResult(_intent, REQUEST_SUB)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode) {
REQUEST_SUB -> { // SubActivityからの結果
if(resultCode == RESULT_OK)
data?.getStringExtra(SubActivity.RESULT_KEY)?.let {
txtMesg.text = "Subから\"${it}\"を受取りました!"
}
else
txtMesg.text = "Subの処理はキャンセルされました!"
}
else -> {}
}
}
companion object {
const val RESULT_KEY = "ResultKey"
}
...
override fun onCreate(savedInstanceState: Bundle?) {
...
findViewById<Button>(R.id.btnCancelSub).setOnClickListener {
setResult(RESULT_CANCELED, intent) // 結果を設定
finish()
}
findViewById<Button>(R.id.btnFinishSub).setOnClickListener {
intent.putExtra(RESULT_KEY, "ABCDE")
setResult(RESULT_OK, intent) // 結果を設定
finish()
}
intent.getStringExtra(MainActivity.REQUEST_KEY)?.let {
txtMesg.text = "Mainから\"${it}\"を受取りました!"
}
}
※startActivity( )は非推奨になっていません。
特殊な状態2:onSaveInstanceState
端末の回転が行われる時、表示中のActivityは終了され、端末の向きに合わせた画面を構築するためにActivityが再起動されます。
これが原因で、画面の表示内容やユーザの操作内容が再起動されたActivith側へ反映されないという問題が起こります。

onRestoreInstanceStateで復元
この問題を回避するために端末の回転では、Activity#onSaveInstanceState( )で表示内容や操作内容を保存し、Activity#onRestoreInstanceState( )で復元できます。
保存・復元は引数savedInstatnceState(Bundle型)を介して行うようになっています。

class MainActivity : AppCompatActivity() {
companion object {
const val COUNT_KEY = "CountKey"
}
private var count = 0
private lateinit var txtMesg: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
txtMesg = findViewById(R.id.txtMesg)
findViewById<Button>(R.id.btnPush).setOnClickListener {
txtMesg.text = makeMesg(++count)
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
count = savedInstanceState.getInt(COUNT_KEY) // 復元
txtMesg.text = makeMesg(count)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(COUNT_KEY, count) // 保存
}
fun makeMesg(count: Int) =
if(count > 0) "ボタンは${count}回押されました!"
else "ボタンは押されていません!"
}
onCreateで復元
復元側はActivity#onRestoreInstanceState( )の代わりにActivity#onCreate( )を使って同様なことができます。ただし、Activityの初回起動(アプリの起動)では、savedInstanceStateがnullになるため、対応が必要です。
class MainActivity : AppCompatActivity() {
companion object {
const val COUNT_KEY = "CountKey"
}
private var count = 0
private lateinit var txtMesg: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
txtMesg = findViewById(R.id.txtMesg)
// savedInstanceState == null:Activity新規作成、アプリ起動
// savedInstanceState != null:Activity再作成、 端末回転
count = savedInstanceState?.let{ it.getInt(COUNT_KEY) }?:0 // 復元
txtMesg.text = makeMesg(count)
findViewById<Button>(R.id.btnPush).setOnClickListener {
txtMesg.text = makeMesg(++count)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(COUNT_KEY, count) // 保存
}
fun makeMesg(count: Int) =
if(count > 0) "ボタンは${count}回押されました!"
else "ボタンは押されていません!"
}
特殊な状態3:onNewInstance
実行中のActivityからActivity#startActivity( )を使って新たにActivityの起動をする時、Intent#flagsにIntent.FLAG_ACTIVITY_SINGLE_TOPが指定されていると、インスタンスをスタックへ重複して配置しない動作になります。
詳細は「ActivityのLaunchModeによる起動の違い」を参照してください。

この時、インスタンスを配置しない代わりに、実行中のActivityインスタンスのActivity#onNewInstance( )が呼ばれます。
例えば次のような場合です。
val _intent = Intent(this, Sub3Activity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
}
startActivity(_intent)
関連記事:

