Androidのライフサイクルについてまとめたもの
今回はActivityのライフサイクルについてのみ
Activityの画面遷移
Andoridは画面遷移の際に、遷移前の画面の上に載るように遷移後の画面が生成される。
バックキーを押すと、今度は上の画面(遷移後の画面)が消え、下の画面が現れる。
Webでの画面遷移とは違い、「遷移」というよりも「積み込む」ような挙動をしている。
例えるならば、紙芝居のようなもの。
ライフサイクルとは
Andoridは画面遷移やバックグラウンドへの移動、アプリキルなどの様々な動作により、Activityの生成と消滅を繰り返している。
このような1つ1つのActivityが生成されてから破棄されるまでの工程をライフサイクルという。
ライフサイクルのコールバック
Activityのライフサイクルの各段階を移動するために、Activityクラスから提供されているコールバックが6つある。
onCreate()
onStart()
onResume()
onPause()
onStop()
onRestart()
onDestroy()
各コールバックは呼ばれるタイミングが異なり、適切な処理も異なる。
適切な処理をすれば、次のような状況を回避することもできる。
- 別アプリに切り替えたらクラッシュする
- 使用していない時にバックグラウンドでリソースを消費する
- 別アプリから戻ったらユーザの入力が消える
- 画面回転によってクラッシュしたり、ユーザの入力が消える
つまりライフサイクルに沿って開発をすることにより、アプリ動作を安定させることができるようになる。
ライフサイクル図
Activityのライフサイクルの各段階を遷移する様子を表した図。
こちらのサイトを参照。
各コールバックと処理について
onCreate()
Activityが生成される時に最初に呼ばれる。
逆に最初にしか呼ばれないため、ライフサイクルの中で一度のみ実行する必要がある処理を実行するのが良い。
例えば、
- Activityの基本的な設定をするような処理
- XMLレイアウトファイルを取得し、View(画面インタフェース)を作成する
- データの初期化処理
- パラメータ
savedInstanceState
を受け取る- このパラメータには、Activity生成以前、つまり前のライフサイクルで消滅したActivityの状態が保存してあり、ライフサイクル間でのデータ保持をすることができる。
- これがnullの時は以前にActivityが存在していない = 今が最初のActivity生成と判定することもできる。
などがある。
onCreate()
が呼ばれ、処理が終わるとすぐにonStart
とonResume
を呼び出す。
onStart()
Activityの状態が「開始」になると呼び出される。
Activityがフォアグラウンドに移動し、操作可能な状態になるまでの間に呼ばれる。Activityの遷移で元に戻った時も呼ばれる。
ここではonCreate()
で取得したViewへのイベント登録や初期化を行う。
よくサンプルコードでonCreate()
にViewへのイベント登録や初期化処理を入れているものもあるが、それは簡略化のため。
別画面から戻ってきた時などに初期化されず、想定した挙動と違うことが起きる可能性があるため、onCreate()
ではViewの取得、onStart()
ではViewへの初期化と分けてやる。
onStart()
が呼ばれると、すぐにonResume()
が呼ばれる。
onResume()
Activityが前面に表示され、ユーザとやりとりが可能になる直前に呼び出される。
そのため、Activityをユーザに表示可能になるために必要な処理や初期化をここで処理する。
例えば、
- データベースとの接続
- データベースから必要な情報を取り出す
- 必要な情報を画面にセットする
などである。
割り込み処理の発生(アプリ表示中に電話がくるなど)により、Avtivityが一時停止すると、onStop()
が呼ばれ、再開すると再びonResume()
が呼ばれる。
onPause()
アプリがバックグラウンドに移動したことを示すために呼び出される。この段階ではアプリキル = Activityが消滅するとは限らない。
Activityの一時停止状態であるため、ここでは
- 続行しない操作を停止する
- 続行する操作はバックグラウンドにあることを想定して調整を行う
などの処理を行う。
ただし、onPause()
の実行は非常に短時間で終了し、状態の保存できない場合があるため、
- アプリデータ、ユーザデータの保存
- ネットワークの呼び出し
- データベース トランザクションの実行
には使用してはいけない。高負荷で時間がかかることが想定される処理は次のonStop()
で行う。
onStop()
アプリが完全にバックグラウンドに移動した時に呼び出される。Activityは非表示になり、停止している。
高負荷の状態保存はここで行う。
アプリがユーザに表示されていないため、
- 不要なリソースの解放/調節
- アニメーションの一時停止
- 位置情報アップデートを高精度から低精度へ切り替え
を行う。
Activityが再度表示される時にはonRestart
->onStart
->onResume()
が呼び出され、Activityが破棄される時にはonDestroy()
を呼び出す。
onRestart()
名前そのままActivityの再表示の際に呼び出される。
これが呼び出された後にはonStart()
->onResume()
と続いて呼び出される。
アプリキルされる時には呼ばれることはない。
onDestroy()
Activityが破棄される前に呼び出される。
次の理由で呼び出される。
- アプリキルするなどして、Activityが終了した時
- 画面の構成が変更された時
- 画面の回転、テーマ変更、マルチウィンドウモードによりActivityが一時的に破棄される
以前のコールバックで解放されていないリソースはここで解放される。
ここまでくるとActivityが再開することはないため、新たなActivity生成の準備が始まる。
ライフサイクルの動きをアプリで確認
package com.android.activitylifecyclesample
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d(TAG, "onCreate()")
}
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart()")
}
override fun onResume() {
super.onResume()
Log.d(TAG, "onResume()")
}
override fun onPause() {
super.onPause()
Log.d(TAG, "onPause()")
}
override fun onStop() {
super.onStop()
Log.d(TAG, "onStop()")
}
override fun onRestart() {
super.onRestart()
Log.d(TAG, "onRestart()")
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy()")
}
companion object {
private const val TAG = "MainActivity"
}
}
各コールバックを通った時にログが表示される。
色々な操作をしてみて、コールバックがどのように呼ばれるのかを体験する。
起動
D/MainActivity: onCreate()
D/MainActivity: onStart()
D/MainActivity: onResume()
起動はここまで。
バックグラウンドからフォアグラウンドに移動
// バックグラウンドへ移動
D/MainActivity: onPause()
D/MainActivity: onStop()
// フォアグラウンドへ移動
D/MainActivity: onRestart()
D/MainActivity: onStart()
D/MainActivity: onResume()
onResume()
とonPause()
が対になっていることが確認できる。
端末をスリープし、復帰させる
// スリープ
D/MainActivity: onPause()
D/MainActivity: onStop()
// 復帰
D/MainActivity: onRestart()
D/MainActivity: onStart()
D/MainActivity: onResume()
端末のスリープ = バックグラウンドへの移動
復帰 = フォアグラウンドへの移動
画面回転
D/MainActivity: onPause()
D/MainActivity: onStop()
D/MainActivity: onDestroy()
D/MainActivity: onCreate()
D/MainActivity: onStart()
D/MainActivity: onResume()
画面回転の際にはActivityが再生成されることがわかる。
アプリキル
D/MainActivity: onPause()
D/MainActivity: onStop()
D/MainActivity: onDestroy()
アプリキルはActivityの消滅を表す
ステータスバーを表示
// ログ表示なし
ステータスバーの表示/非表示はライフサイクルに影響を与えなかった。
ステータスバーからダークテーマへ切り替え
Android10からOSレベルでのダークテーマ切り替えが可能になった。
D/MainActivity: onPause()
D/MainActivity: onStop()
D/MainActivity: onDestroy()
D/MainActivity: onCreate()
D/MainActivity: onStart()
D/MainActivity: onResume()
テーマ切り替えをする処理setDefaultNightMode()
が呼ばれることで、Activityが再生成されている。
アプリをクラッシュ
adb shell am force-stop package_name
このコマンドでアプリを強制シャットダウンできる。
// 起動
D/MainActivity: onCreate()
D/MainActivity: onStart()
D/MainActivity: onResume()
// ここでクラッシュ
// ログ表示なし
Activityの消滅の際にはonPause()
->onStop()
->onDestroy()
を通過するはずが、クラッシュした時にはログが表示されない = 正常なライフサイクルを通っていないことが確認できた。
onCreate()でfinish()を呼ぶ
MainActivity#onStart()
でActivity完了処理finish()
を呼んでみる。
D/MainActivity: onCreate()
D/MainActivity: onStart()
D/MainActivity: onStop()
D/MainActivity: onDestroy()
通常 : onPause()
->onStop()
->onDestory()
今回 : onStop()
->onDestroy()
意外なことにonPause()
が呼ばれなかった。
onfinish()
をするとonPause()
が呼ばれないのか?と思い、他の場所でもonfinish()
を呼んでみた。
D/MainActivity: onCreate()
D/MainActivity: onStart()
D/MainActivity: onResume()
D/MainActivity: onPause()
D/MainActivity: onStop()
D/MainActivity: onDestroy()
D/MainActivity: onCreate()
D/MainActivity: onDestroy()
onCreate()
とonDestroy()
、onResume()
とonPause()
が対応していることがわかる。
参考資料
Android Developers アクティビティのライフサイクルについて
URL:https://developer.android.com/guide/components/activities/activity-lifecycle?hl=ja#kotlin
最後に
今回はActivityのライフサイクルについて、サンプルコードを用いてその動作を確認した。
次はFragmentも交えたライフサイクルについてまとめたい。