onPause——是Android生命周期方法,当Activity失去输入焦点但仍部分可见于屏幕时被调用。系统在新Activity进入前台之前、打开对话框时、按下“最近应用”按钮或来电时调用onPause。此方法是保存用户数据的最后保证点,因为在onStop和onDestroy之后,系统可以在没有额外调用的情况下终止进程。在onPause中,开发者保存草稿、暂停动画、释放摄像头并将当前UI状态写入SharedPreferences。关于Activity完整生命周期的更多信息,请阅读文章Activity Lifecycle。
要点
onPause——Activity生命周期的第四个方法,当屏幕失去输入焦点但仍部分对用户可见时被调用。这是应用活跃工作和隐藏之间的“过渡”状态。系统在以下场景中调用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,但在对话框下方保持可见。当对话框关闭时,地图收到onResume而不调用onStart(屏幕未被完全隐藏)。
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、暂停视频文件、释放摄像头和音频焦点。每次调用都轻量快速,不会阻塞UI线程到足以导致ANR。注意顺序:super.onPause()在第一行调用——这保证了即使用户代码中出现异常,系统逻辑也会执行。
onPause——开发者可以在应用被系统隐藏或终止之前保证保存用户数据的最后一点。在onStop之后,系统可能在内存不足时销毁进程而不调用onDestroy。onSaveInstanceState()方法在onPause之后调用,但其Bundle不适用于长期存储——只存活到下一次onCreate。
使用异步apply()的SharedPreferences——在onPause中保存小量数据的最佳方式。与同步将数据写入磁盘并返回布尔值的commit()不同,apply()立即将数据保存到内存并安排异步写入磁盘。这在UI线程中耗时不到1毫秒,而commit()需要10–100毫秒。
override fun onPause() {
super.onPause()
// ❌ 不好:同步写入阻塞线程
// prefs.edit().putInt("score", score).commit()
// ✅ 好:异步写入
prefs.edit().putInt("score", score).apply()
// 对于复杂对象——在ViewModel中缓存
viewModel.saveState()
}
对于结构化数据(通过Room的SQLite),在onPause中使用带有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在主动跟踪模式(follow mode)下消耗大量GPU资源。失去焦点时,禁用地图动画并降低标记更新频率是合理的,而在焦点恢复时恢复全部功能。这提高了屏幕切换时的性能并降低了功耗。
初级Android开发者最常见的困惑之一——不理解onPause和onStop之间的区别。让我们检查每个场景并确定正确的方法。
| 场景 | onPause | onStop |
|---|---|---|
| 打开对话框 | 调用 | 不调用 |
| 打开新Activity(非透明) | 调用 | 调用 |
| 按下“主页”按钮 | 调用 | 调用 |
| 锁定屏幕 | 调用 | 调用 |
| 来电 | 调用 | 调用 |
| 当前之上的透明Activity | 调用 | 不调用 |
| 分屏(半个屏幕) | 调用 | 不调用 |
| 画中画 | 调用 | 不调用 |
主要规则:onPause在每次失去焦点时调用,onStop——仅在完全失去可见性时。如果Activity保持可见(即使部分可见),onStop不会被调用。这对于分屏、画中画和透明Activity模式至关重要——这里onPause/onResume工作,但onStart/onStop不工作。
onPause——生命周期中时间最关键的调用,因为它会阻塞下一个Activity的渲染。系统会等待当前Activity的onPause完成后才显示新的Activity。如果onPause执行超过100毫秒,用户体验到过渡延迟;如果超过5秒——系统显示ANR。
Google Android性能指南(2025)为onPause提供以下建议:不要执行网络请求——应取消或移至WorkManager;不要将大文件写入磁盘——在后台线程中使用BufferedWriter;不要执行复杂SQL查询——Room操作应通过协程异步进行;避免创建新对象——onPause中的垃圾回收会加剧延迟;对SharedPreferences使用apply()而不是commit()。
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 pipeline中发出性能回归信号。
即使经验丰富的开发者也会在onPause中犯错。让我们检查五个典型问题及其解决方案。
在onPause中使用同步查询(.executeAsObservable()不带协程)调用Room DAO会阻塞UI线程10–50毫秒。如果此时发生垃圾回收或写入数据库的竞争,延迟可能达到200–500毫秒。解决方案:使用带有Dispatchers.IO的协程或对SharedPreferences使用apply()。
onPause不是注册监听器的地方。如果在onPause中注册BroadcastReceiver,当Activity不再可见时它仍保持活跃。注册应仅在onStart/onResume中进行,在onPause/onStop中——仅取消注册。例外——需要在调用前注册的基于Intent的API。
如果在onPause中发生未处理的异常,系统不会调用onStop和onDestroy。Activity陷入不确定状态,返回时的onResume可能无法正确恢复已释放的资源。解决方案:将关键操作包裹在try/catch中并通过Log.e()记录日志。
不需要在onPause中保存易于恢复的数据。例如,API请求的结果在收到时即缓存到Room或DataStore中,而不是在onPause中。只保存用户手动输入且无法自动恢复的数据——字段中的文本、选中的元素、滚动位置。
super.onPause()应该被调用,但与onCreate不同,缺少它不会立即导致崩溃。系统会“原谅”在onPause中省略super,但内部状态机进入错误状态。下次onResume调用可能无法恢复输入焦点,Activity保持“冻结”状态。始终尽早调用super.onPause()。
常见问题
在onPause中调用finish()将在方法返回后立即结束Activity。如果在失去焦点时需要关闭屏幕(例如,应用最小化时的授权屏幕),这是正确的场景。但是,finish()会启动完整的结束循环:onStop → onDestroy,这会给过渡增加延迟。只有在确实必要时才在onPause中使用finish()。
onPause——用于保存必须跨进程终止的数据(SharedPreferences/Room中的草稿)。onSaveInstanceState——用于保存临时UI状态,仅需到下一次onCreate(滚动位置、选中的标签页)。onSaveInstanceState的Bundle在应用完全终止时不会保存——仅存在于内存中。onPause数据保存到磁盘并能在重启后存活。
不推荐。在onPause中打开对话框或弹出窗口会导致WindowLeakException,如果Activity已经结束。如果需要在失去焦点时显示通知,请使用NotificationManager(系统通知)——这是安全的且用户期望的。对于延迟操作,请使用AlarmManager或WorkManager。
onPause在Activity停止活动之前保证被调用。如果系统为释放内存而终止进程,onStop可能不会被调用——在这种情况下onDestroy也不会被调用。onPause是onResume之后唯一无论焦点丢失原因如何始终被调用的方法。因此所有关键数据都在onPause中保存。
测试onPause使用AndroidX Test中的Robolectric或FragmentScenario。FragmentScenario.create() → moveToState(State.STARTED) → moveToState(State.RESUMED) → moveToState(State.STARTED)顺序调用onPause。然后检查数据是否已保存到SharedPreferences或摄像头是否已通过mock对象释放。Robolectric 4.12+支持无需物理设备的onPause/onResume模拟。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。