onPauseはAndroidのライフサイクルメソッドで、Activityが入力フォーカスを失ったものの、画面上に部分的に表示されたままになっているときに呼び出されます。システムは、新しいActivityがフォアグラウンドに表示される前、ダイアログが開かれたとき、最近使ったアプリボタンが押されたとき、または着信があったときにonPauseを呼び出します。このメソッドはユーザーデータを保存するための最後の保証されたポイントであり、onStopとonDestroyの後、システムは追加の呼び出しなしにプロセスを終了できます。onPause内で、開発者は下書きを保存し、アニメーションを一時停止し、カメラを解放し、現在のUI状態をSharedPreferencesに書き込みます。完全なActivityライフサイクルの詳細については、記事Activity Lifecycleをお読みください。
重要なポイント
onPauseはActivityライフサイクルの4番目のメソッドで、画面が入力フォーカスを失うものの、ユーザーに部分的に表示されたままになっているときに呼び出されます。これはアプリがアクティブに実行されている状態と非表示の間の「遷移」状態です。システムは次のシナリオでonPauseを呼び出します: 別のActivityが開く(新しい画面が現在の画面を部分的に覆う)、ダイアログウィンドウが表示される(Dialog、PopupWindow、SnackbarはonPauseをトリガーしませんが、DialogFragmentはトリガーします)、最近使ったアプリボタンを押す、着信、画面をロックするために電源ボタンを押す。
onPauseの主な目的は、アプリが非表示または破棄される可能性に備えることです。これはライフサイクルの中で、システムが別のコンポーネントへの遷移を進める前に、開発者が自分のコードが確実に実行されることを確認できる最後のポイントです。onPauseの後、システムはonStopを呼び出し(Activityが完全に非表示になった場合)、その後プロセスは追加の通知なしにいつでも終了される可能性があります。
Android Developersのドキュメント(2025)によると、onPauseは可能な限り軽量で高速であるべきです。onPauseが制御を返すまで、システムは次のActivityを開始できません — つまり、ユーザーは画面遷移の遅延を目にすることになります。Googleは100ミリ秒未満でonPauseを完了することを推奨しており、すべての長時間操作(データベースへの保存、ディスクへの書き込み)はコルーチンまたはapply()を介して非同期に実行する必要があります。
Activityでは、画面がアクティブでなくなるたびにonPauseメソッドが呼び出されますが、部分的に表示され続けることがあります。典型的な例: ユーザーがマップアプリを開き、位置情報を共有をタップすると、マップの上にシステムのアプリ選択ダイアログが表示されます。マップのActivityはonPauseを受け取りますが、ダイアログの下に表示されたままです。ダイアログが閉じられると、マップはonStartが呼び出されずにonResumeを受け取ります(画面は完全に非表示になっていませんでした)。
class NoteEditorActivity : AppCompatActivity() {
private var binding: ActivityNoteEditorBinding? = null
private val prefs by lazy {
getSharedPreferences("note_drafts", Context.MODE_PRIVATE)
}
override fun onPause() {
super.onPause()
// メモの下書きを保存 — 非同期
prefs.edit()
.putString("draft_title", binding?.titleInput?.text.toString())
.putString("draft_body", binding?.bodyInput?.text.toString())
.putLong("draft_timestamp", System.currentTimeMillis())
.apply()
// 動画を一時停止
binding?.videoPlayer?.pause()
// 排他リソースを解放
releaseCamera()
releaseAudioFocus()
}
override fun onResume() {
super.onResume()
// 下書きを復元
binding?.titleInput?.setText(prefs.getString("draft_title", ""))
binding?.bodyInput?.setText(prefs.getString("draft_body", ""))
acquireCamera()
acquireAudioFocus()
}
}
NoteEditorActivityの例は、onPauseの正しい処理を示しています: apply()を介したSharedPreferencesへの下書き保存、ビデオファイルの一時停止、カメラとオーディオフォーカスの解放。各呼び出しは軽量かつ高速で、ANRを引き起こすほどUIスレッドをブロックしません。順序に注意してください: super.onPause()が最初の行で呼び出されます — これにより、ユーザーコードで例外が発生した場合でもシステムロジックが確実に実行されます。
onPauseは、アプリが非表示にされたりシステムによって強制終了されたりする前に、開発者がユーザーデータを確実に保存できる最後のポイントです。onStopの後、システムはメモリ不足の場合にonDestroyを呼び出さずにプロセスを終了することがあります。onSaveInstanceState()メソッドはonPauseの後に呼び出されますが、そのBundleは長期間の保存を目的としていません — 次のonCreateまでしか生存しません。
非同期のapply()を使ったSharedPreferencesは、onPauseで少量のデータを保存する最適な方法です。同期的にデータをディスクに書き込んでブール値を返すcommit()とは異なり、apply()は即座にデータをメモリに保存し、非同期のディスク書き込みをスケジュールします。これにより、commit()の10〜100ミリ秒に対して、UIスレッドでの所要時間は1ミリ秒未満です。
override fun onPause() {
super.onPause()
// ❌ 悪い: 同期書き込みがスレッドをブロック
// prefs.edit().putInt("score", score).commit()
// ✅ 良い: 非同期書き込み
prefs.edit().putInt("score", score).apply()
// 複雑なオブジェクトの場合 — ViewModelでのキャッシュ
viewModel.saveState()
}
onPauseでの構造化データ(Room経由のSQLite)には、lifecycleScopeを使ったコルーチンを使用します。ViewModelScopeはViewModelが破棄されると自動的にコルーチンをキャンセルし、閉じたデータベースへの書き込みを防ぎます。コルーチンを使ったRoomでの書き込みは5〜15ミリ秒かかり、UIスレッドをブロックしません。
// ViewModel内:
fun saveDraft(title: String, body: String) {
viewModelScope.launch(Dispatchers.IO) {
noteDao.insert(NoteDraft(title = title, body = body))
}
}
// Activity.onPause内:
viewModel.saveDraft(
binding?.titleInput?.text.toString(),
binding?.bodyInput?.text.toString()
)
FragmentのonPauseは、Fragmentがアクティブでなくなったものの、表示されたままになる可能性があるときに呼び出されます。これは次の場合に発生します: FragmentがFragmentTransactionを介して別のFragmentに置き換えられたとき、FragmentがViewPagerの現在のページでなくなったとき、Fragmentを含むActivityがonPauseを受け取ったとき。ActivityのonPauseとFragmentのonPauseの間の相互作用は厳密に階層的です: 最初にActivityがonPauseを受け取り、次にそのすべてのFragmentが受け取ります。
class MapFragment : Fragment() {
private var mapController: MapController? = null
override fun onPause() {
super.onPause()
mapController?.stopFollowMode()
binding?.mapContainer?.alpha = 0.7f
}
override fun onResume() {
super.onResume()
binding?.mapContainer?.alpha = 1.0f
if (isVisible) {
mapController?.startFollowMode()
}
}
}
onPauseでのマップ操作の詳細: Google MapsとYandex Mapsは、アクティブなフォローモードでかなりのGPUリソースを消費します。フォーカスを失った場合、マップアニメーションを無効にし、マーカーの更新頻度を減らすことが合理的であり、フォーカスが戻ったら完全な機能を復元する必要があります。これにより、画面切り替え時のパフォーマンスが向上し、消費電力が削減されます。
初心者のAndroid開発者にとって最も一般的な混乱の1つは、onPauseとonStopの違いを理解していないことです。各シナリオを検証し、正しいメソッドを判断しましょう。
| シナリオ | onPause | onStop |
|---|---|---|
| ダイアログウィンドウを開く | 呼び出される | 呼び出されない |
| 新しいActivity(非透過)を開く | 呼び出される | 呼び出される |
| ホームボタンを押す | 呼び出される | 呼び出される |
| 画面ロック | 呼び出される | 呼び出される |
| 着信 | 呼び出される | 呼び出される |
| 透過的なActivityが重なる | 呼び出される | 呼び出されない |
| 分割画面(画面半分) | 呼び出される | 呼び出されない |
| PiP(ピクチャーインピクチャー) | 呼び出される | 呼び出されない |
主なルール: onPauseはフォーカスが失われるたびに呼び出され、onStopは可視性が完全に失われた場合にのみ呼び出されます。Activityが表示されたままの場合(部分的であっても)、onStopは呼び出されません。これは分割画面、PiP、透過的なActivityモードにとって非常に重要です — ここではonPause/onResumeは機能しますが、onStart/onStopは機能しません。
onPauseは、次のActivityのレンダリングをブロックするため、最も時間的に重要なライフサイクルメソッドです。システムは新しいActivityを表示する前に、現在のActivityのonPauseが完了するのを待ちます。onPauseが100ミリ秒以上かかると、ユーザーは遷移の遅延に気づきます; 5秒以上かかると、システムはANRを表示します。
Google Android Performance Guide(2025)は、onPauseに関して次の推奨事項を提供しています: ネットワークリクエストを実行しない — キャンセルするかWorkManagerに移動する; 大きなファイルをディスクに書き込まない — バックグラウンドスレッドでBufferedWriterを使用する; 複雑なSQLクエリを実行しない — Room操作はコルーチンを介して非同期にする; 新しいオブジェクトの作成を避ける — onPauseでのガベージコレクションは遅延を悪化させる; SharedPreferencesにはcommit()ではなくapply()を使用する。
override fun onPause() {
super.onPause()
// ❌ 悪い: HTTPリクエストがUIをブロック
// val response = api.syncSave(data).execute()
// ❌ 悪い: ファイルへの同期書き込み
// FileOutputStream(file).write(data)
// ✅ 良い: 非同期保存
lifecycleScope.launch {
withContext(Dispatchers.IO) {
api.saveData(data)
fileDao.write(data)
}
}
// ✅ 良い: SharedPreferencesへの軽量書き込み
prefs.edit().putString("key", value).apply()
}
Android Studio Profiler(CPUトレース)によるonPauseのプロファイリングは、正確な実行時間を示します。onPauseが100ミリ秒を超えると、Profilerはメソッドを黄色で強調表示し、500ミリ秒を超えると赤色で強調表示します。IT Sectrの商用プロジェクトでは、Macrobenchmarkテストを使用して、Activity間の遷移時間を自動的にチェックし、CIパイプラインでのパフォーマンス低下を通知しています。
経験豊富な開発者でもonPauseで間違いを犯します。5つの典型的な問題とその解決策を見てみましょう。
onPauseで同期的なクエリ(.executeAsObservable()、コルーチンなし)でRoom DAOを呼び出すと、UIスレッドが10〜50ミリ秒ブロックされます。同時にGCや書き込みの競合が発生すると、遅延が200〜500ミリ秒に達する可能性があります。解決策: Dispatchers.IOを使ったコルーチン、またはSharedPreferencesにはapply()を使用します。
onPauseはリスナーを登録する場所ではありません。onPauseでBroadcastReceiverを登録すると、Activityが表示されなくなってもアクティブなままになります。登録はonStart/onResumeでのみ行い、onPause/onStopでは登録解除のみを行うべきです。例外は、呼び出し前に登録が必要なIntent駆動のAPIです。
onPauseで未処理の例外が発生すると、システムはonStopとonDestroyを呼び出しません。Activityは未定義の状態で停止し、戻ったときのonResumeで解放されたリソースが正しく復元されない可能性があります。解決策: Log.e()によるログ記録とともに、重要な操作をtry/catchでラップします。
簡単に復元できるデータをonPauseで保存する必要はありません。たとえば、APIリクエストの結果は、onPauseではなく、取得時にRoomやDataStoreにキャッシュします。ユーザーが手動で入力し、自動的に復元できないものだけを保存します — フィールドのテキスト、選択したアイテム、スクロール位置などです。
super.onPause()は呼び出すべきですが、onCreateとは異なり、省略しても即座にクラッシュはしません。システムはonPauseでのsuperの欠如を「許容」しますが、内部のステートマシンが不正な状態になります。次のonResume呼び出しで入力フォーカスが復元できず、Activityが「フリーズ」したままになる可能性があります。常に可能な限り早くsuper.onPause()を呼び出してください。
よくある質問
onPauseでfinish()を呼び出すと、メソッドから戻った直後にActivityが終了します。これは、フォーカスを失ったときに画面を閉じる必要がある場合(たとえば、アプリを最小化したときの認証画面など)の正当なシナリオです。ただし、finish()は完全な終了サイクル(onStop onDestroy)をトリガーし、遷移に遅延を追加します。onPauseでのfinish()は、本当に必要な場合にのみ使用してください。
onPauseはプロセス終了後も存続すべきデータを保存するためのものです(SharedPreferences/Roomへの下書き)。onSaveInstanceStateは次のonCreateまでのみ必要な一時的なUI状態を保存するためのものです(スクロール位置、選択されたタブ)。onSaveInstanceStateのBundleはアプリが完全に終了したときには保存されません — メモリにのみ存在します。onPauseのデータはディスクに保存され、再起動後も存続します。
推奨されません。onPauseでダイアログやポップアップを開くと、Activityがすでに終了している場合にWindowLeakExceptionが発生します。フォーカスを失ったときに通知を表示する必要がある場合は、NotificationManager(システム通知)を使用してください — これは安全で、ユーザーにとって期待される動作です。遅延したアクションには、AlarmManagerまたはWorkManagerを使用してください。
onPauseはActivityがアクティブでなくなる前に確実に呼び出されます。システムがメモリを解放するためにプロセスを強制終了した場合、onStopは呼び出されない可能性があります — この場合、onDestroyも呼び出されません。onPauseはonResumeの後に、フォーカスを失った理由に関係なく常に呼び出される唯一のメソッドです。したがって、すべての重要なデータは正確にonPauseで保存されます。
onPauseのテストには、RobolectricまたはAndroidX TestのFragmentScenarioを使用します。FragmentScenario.create() moveToState(State.STARTED) moveToState(State.RESUMED) moveToState(State.STARTED)が順次onPauseを呼び出します。その後、データがSharedPreferencesに保存されたか、モックオブジェクトを介してカメラが解放されたかを検証します。Robolectric 4.12+は、物理デバイスなしでonPause/onResumeのエミュレーションをサポートしています。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。