onStop — AndroidのActivityライフサイクルのメソッドで、Activityがユーザーに表示されなくなったときにシステムによって呼び出されます。新しいActivityが完全にカバーした後、またはアプリが最小化されたときに、ActivityはStopped状態に移行します。onStopメソッドでは、開発者はアニメーションを停止し、カメラやセンサーのリソースを解放し、入力データの下書きを保存する必要があります。Android Vitals(Google、2025年)によると、onStopを適切に処理すると、アプリ最小化時のANR(アプリケーションが応答しない)の数が35%減少します。onStopの後、システムはonRestart(画面に戻る)またはonDestroy(完全な終了)を呼び出すことができます。Android DevelopersのActivityライフサイクルに関するドキュメントでは、onStopを可視状態と不可視状態の境界として説明しています。
重要なポイント
onStop — AppCompatActivityクラス(およびその前身のActivity)のコールバックメソッドで、Androidオペレーティングシステムによって、Activityがユーザーに完全に表示されなくなったときに呼び出されます。この時点で、Activityは別のActivity、ダイアログウィンドウ、システムランチャー、またはロック画面によって隠されています。ライフサイクルの観点から見ると、onStopはonPauseの後に続き、Activityオブジェクトとその状態はメモリに残っているものの、Activityが画面上に表示されなくなったことを示します。
ActivityがStopped(停止)状態に移行すると、RAMに状態を保持します — すべてのフィールド、View階層、ViewModelがアクセス可能なままです。これにより、StoppedはActivityが完全に削除されるDestroyed(破棄)状態と区別されます。システムUIは、メモリ不足時にStopped状態のアプリケーションプロセスを強制終了することがあります — これはいわゆるプロセス強制終了です。開発者は、プロセス強制終了時の復元を保証するために、onStopの前に呼び出されるonSaveInstanceState()に重要なデータ(下書き、スクロール位置)を保存する必要があります。
Android互換性定義ドキュメント(CDD)バージョン14+によると、Stopped状態のプロセスはOOM Killerによる強制終了の優先度が低くなります — バックグラウンドフェーズのプロセスよりは低いが、キャッシュされたプロセスよりは高いです。Googleの統計によると、68%のプロセス強制終了は、ActivityがPaused状態ではなくStopped状態のときに発生します。
onStopはActivityが完全に可視性を失ったときに呼び出されます。理由は問いません:現在の上に新しいActivityを起動する、アプリを最小化する(ホームボタン)、画面をロックする、着信、またはシステムダイアログを開くなど。これらすべての場合で、Activityは最初にonPause(部分的なフォーカス喪失)を受け取り、次にonStop(完全な可視性喪失)を受け取ります。
onStop呼び出しの主なシナリオ:
画面回転時にはonStopは呼び出されないことを理解することが重要です — この場合、Activityは破棄(onPause → onStop → onDestroy)され、再作成(onCreate → onStart → onResume)されます。例外はマニフェストのandroid:configChanges="orientation"フラグで、Activityの再作成を防ぎ、代わりにonConfigurationChanged()を呼び出します。
onStopは、Activityライフサイクルシーケンスの中で、可視状態と不可視状態の間の中心的な位置を占めます。完全なシーケンス:onCreate → onStart → onResume →(アクティブ状態)→ onPause → onStop → onDestroy(または復帰時にonRestart → onStart → onResume)。
| 状態 | メソッド | 可視性 | 操作 | メモリ |
|---|---|---|---|---|
| Created | onCreate | なし | なし | 割り当て済み |
| Started | onStart | 部分的 | なし | 完全 |
| Resumed | onResume | 完全 | あり | 完全 |
| Paused | onPause | 部分的 | なし | 完全 |
| Stopped | onStop | なし | なし | 完全* |
| Destroyed | onDestroy | なし | なし | 解放済み |
*Stopped状態では、Activityはメモリに保持されますが、リソース不足時にシステムによって強制終了される可能性があります。Stoppedプロセスの強制終了優先度は最後から2番目で、キャッシュされた空のプロセスのみが下です。
onStopとonSaveInstanceState: システムは動的UI状態を保存するために、onStopの前にonSaveInstanceState(Bundle)を呼び出します。開発者はこのメソッドをオーバーライドして、入力フィールドの値、RecyclerViewの位置、選択されたアイテムをBundleに保存します。Activityが破棄されない場合でも(ユーザーが単に最小化して戻った場合)、Bundleは構成変更時にonCreateに渡されます。Googleは、Activityの外部に存在するリポジトリデータやViewModelではなく、一時的なUI状態のみを保存することを推奨しています。
onStopでは、Activityが表示されていないときに不要なすべてのリソースを開発者が解放する必要があります。これにより、バッテリー、CPU、メモリの負荷が軽減され、アクティビティに戻る際のANRも防止されます。
onStopで解放するもの:
onStopでやってはいけないこと: 長時間の操作(データベースへの大量データ保存、ネットワークリクエスト、複雑な計算)は実行しないでください。onStopはメインスレッドで実行され、Activityへの復帰をブロックします。長時間の操作には、遅延付きのWorkManagerまたはviewModelScopeのコルーチンを使用してください。ViewModelのリソースは解放しないでください — ViewModelはonStopを生き残り、復帰時に使用されます。
onPauseとonStopは、可視性喪失の程度と必須アクションの範囲が異なります。onPauseは部分的なフォーカス喪失時(ダイアログウィンドウやシステムメニューを開くなど)に呼び出され、onStopは完全な可視性喪失時に呼び出されます。この違いは、各段階でどのリソースを解放するかを選択する上で重要です。
| 特性 | onPause | onStop |
|---|---|---|
| 可視性レベル | 部分的に可視 | 完全に不可視 |
| フォーカス | 喪失 | 喪失 |
| 実行時間 | 最大500ミリ秒 | 最大5秒(ANRタイムアウト) |
| 解放するリソース | 重要なもの(メディア、カメラ) | すべての不可視リソース(センサー、アニメーション、位置情報) |
| 復元 | onResume | onRestart → onStart → onResume |
| プロセス優先度 | 高(フォアグラウンド) | 中(バックグラウンド) |
一般的なルール:onPauseでは、別のアプリのユーザーエクスペリエンスに即座に影響を与えるシステムリソース(カメラ、メディアプレーヤー)を解放します。onStopでは、Activityが非表示のときに不要なその他すべてのリソースを解放します。Googleは、素早い切り替え時にonStopが呼び出されない可能性があるため、onPauseで重要なユーザーデータ(メールの下書き、設定)を保存することを推奨しています。
ユーザーが非表示のActivityに戻ると、システムはonRestart → onStart → onResumeを呼び出します。onRestartメソッドは、ActivityがStopped状態から戻ってきていることを示します。これは、onStopで解放されたUIとリソースを復元するための重要な段階です。
復帰時の呼び出しシーケンス:
アプリケーションプロセスがStopped状態でシステムによって強制終了された場合、onRestartの代わりにonCreateが呼び出され、onSaveInstanceStateからのBundleが状態復元のために渡されます。このシナリオ(プロセス強制終了)はAndroidアプリのバグの最も一般的な原因の1つです。開発者はonRestartを実装しても、プロセス強制終了後のonCreateによる復元を考慮するのを忘れてしまいます。
Activity非表示時の正しいセンサー登録解除とアニメーション停止を示します。画面に戻ると、onStartでリソースが復元されます。
class MainActivity : AppCompatActivity() {
private lateinit var sensorManager: SensorManager
private var accelerometer: Sensor? = null
private var rotationAnimator: ObjectAnimator? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
}
override fun onStart() {
super.onStart()
accelerometer?.let {
sensorManager.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_NORMAL)
}
rotationAnimator = ObjectAnimator.ofFloat(findViewById(R.id.icon), "rotation", 0f, 360f)
rotationAnimator?.apply {
duration = 3000
repeatMode = ValueAnimator.RESTART
repeatCount = ValueAnimator.INFINITE
start()
}
}
override fun onStop() {
super.onStop()
sensorManager.unregisterListener(sensorListener)
rotationAnimator?.cancel()
}
override fun onRestart() {
super.onRestart()
Log.d("MainActivity", "ActivityがStopped状態から復帰")
}
private val sensorListener = SensorEventListener { event, _ ->
Log.d("MainActivity", "加速度: x=${event.values[0]}, y=${event.values[1]}, z=${event.values[2]}")
}
}
コードはonStartで加速度計センサーを登録し、無限回転アニメーションを開始します。onStopでは、センサーが登録解除され、アニメーションがキャンセルされます — これにより、Activityが非表示のときのバッテリー消費を防ぎます。onRestart → onStartを経て戻った後、リソースが再作成されます。
ViewModel + SavedStateHandleを使用する最新のアプローチ。手動のBundle操作なしで、onStop中にフォームデータが自動的に保存されます。
class FormViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
var email: String
get() = savedStateHandle["email"] ?: ""
set(value) { savedStateHandle["email"] = value }
var message: String
get() = savedStateHandle["message"] ?: ""
set(value) { savedStateHandle["message"] = value }
}
class FormActivity : AppCompatActivity() {
private val viewModel: FormViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_form)
Log.d("FormActivity", "onCreate: email=${viewModel.email}")
}
override fun onStop() {
super.onStop()
Log.d("FormActivity", "onStop: データがSavedStateHandleに保存されました")
}
}
SavedStateHandleは、onStopの前に呼び出されるonSaveInstanceStateの間に、自動的にBundleに値を保存します。画面回転やプロセス強制終了時も、データは損失なく復元されます。Googleは、直接のonSaveInstanceStateではなく、フォームや下書きにはSavedStateHandleを推奨しています。
onStopへの移行中に非同期でデータを保存するための、コルーチンを使用したlifecycleScopeの利用。コルーチンはメインスレッドをブロックせずにIOディスパッチャで実行されます。
class NoteActivity : AppCompatActivity() {
private val noteRepository = NoteRepository()
override fun onStop() {
lifecycleScope.launch(Dispatchers.IO) {
val text = findViewById<EditText>(R.id.note_content).text.toString()
noteRepository.saveDraft(text)
withContext(Dispatchers.Main) {
Log.d("NoteActivity", "下書きがonStopで保存されました")
}
}
super.onStop()
}
}
lifecycleScope.launchコルーチンは、Activityのライフサイクルが終了すると自動的にキャンセルされます。Dispatchers.IOを使用することで、データベースやファイルへの書き込みがActivityへの復帰をブロックしないことが保証されます。Googleによると、lifecycleScopeのコルーチンは、onStopで非同期操作を実行する推奨される方法です。
よくある質問
onStop — Activityは表示されなくなりますが、Stopped状態でメモリに残ります。システムはonRestartを介してActivityを復帰させることができます。onDestroy — Activityは破棄され、メモリが解放されます。onDestroy後は、新しいActivityインスタンス(onCreate)を作成することによってのみ復帰が可能です。
はい、必須です。super.onStop()は、フラグメント、LoaderManager、ViewModelStoreなどのシステムコンポーネントの正しい動作を保証します。super.onStop()をスキップすると、メモリリークやフラグメントの復元ミスを引き起こす可能性があります。super.onStop()は常に最後または最初に呼び出してください — 順序は重要ではありませんが、呼び出しは必須です。
各ライフサイクルメソッドでLog.dまたはTimberを使用します。Activityのタグでlogcatフィルターを有効にします。本番環境ではAndroid Vitalsを使用します — Googleが自動的にライフサイクルメトリクスを収集し、Play Consoleで異常を表示します。ProcessLifecycleOwnerを介したライフサイクル監視も利用可能です。
onStopでキャッチされなかった例外は、アプリのForce Closeを引き起こします。システムはライフサイクルコールバックの例外をキャッチしません。onStopで例外をスローする可能性のある操作(ファイル操作、ネットワーク)を実行する場合は、try-catchでラップし、super.onStop()を中断せずにエラーをログに記録してください。
いいえ、Activity内のBitmapは、参照がなければGCによって収集されます。onStopでの強制解放(recycle())は不要であり、むしろ有害です — ActivityがonRestartで戻った場合、Bitmapを再読み込みする必要があります。画像の読み込みにはGlideまたはCoilを使用してください — これらのライブラリは自動的にキャッシュとライフサイクルを管理します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。