乐于分享
好东西不私藏

Android App 生命周期与 Process Death 详解

Android App 生命周期与 Process Death 详解

一、概述

Android 应用的生命周期管理是移动开发的核心课题之一。与桌面应用不同,Android 系统在资源紧张时会主动回收后台进程,导致用户再次进入时应用从头重建。如何在这种极端场景下保持良好的用户体验,是衡量 Android 工程师水平的重要标准。

核心挑战

场景
问题
用户感受
进程被系统杀死
数据全部丢失
填写的表单消失,回到初始页
配置变更(旋转屏幕)
Activity 重建
视频重新加载,列表滚动位置丢失
系统内存不足
后台 Activity 被回收
返回后页面状态重置
用户长时间离开
进程进入 LRU 淘汰
下次进入无法恢复到离开时的状态

二、Android 进程生命周期

2.1 进程重要性等级

Android 系统将进程分为五个优先级,内存不足时按优先级从低到高回收:

┌─────────────────────────────────────────────────────────────────────┐│                     Android 进程优先级(由高到低)                      ││                                                                     ││  ┌──────────────────────────────────────────────────────────────┐   ││  │  1. 前台进程(Foreground Process)                             │   ││  │     • 有 Activity 处于 resumed 状态                            │   ││  │     • 有 Service 绑定到前台 Activity                           │   ││  │     • 有前台 Service(startForeground)                        │   ││  │     • BroadcastReceiver 正在 onReceive()                      │   ││  │     ✓ 极少被杀,只在内存极度紧张时                               │   ││  └──────────────────────────────────────────────────────────────┘   ││  ┌──────────────────────────────────────────────────────────────┐   ││  │  2. 可见进程(Visible Process)                                │   ││  │     • Activity 处于 onPause 但仍可见(被 Dialog 覆盖)          │   ││  │     • Service 绑定到可见 Activity                              │   ││  │     ✓ 通常不被杀,除非为维持前台进程运行                          │   ││  └──────────────────────────────────────────────────────────────┘   ││  ┌──────────────────────────────────────────────────────────────┐   ││  │  3. 服务进程(Service Process)                                │   ││  │     • 有 startService() 启动的 Service 在运行                  │   ││  │     • 典型场景:音乐播放、文件下载                               │   ││  │     △ 运行超过 30 分钟后优先级降低                               │   ││  └──────────────────────────────────────────────────────────────┘   ││  ┌──────────────────────────────────────────────────────────────┐   ││  │  4. 缓存进程(Cached Process)                                 │   ││  │     • 所有 Activity 都处于 stopped/destroyed                   │   ││  │     • 用户已导航离开,应用进入后台                               │   ││  │     ✗ 系统随时可能回收,是 Process Death 的主要来源              │   ││  └──────────────────────────────────────────────────────────────┘   ││  ┌──────────────────────────────────────────────────────────────┐   ││  │  5. 空进程(Empty Process)                                    │   ││  │     • 没有任何活跃组件                                          │   ││  │     • 仅作为缓存保留(加速冷启动)                               │   ││  │     ✗ 最先被回收                                               │   ││  └──────────────────────────────────────────────────────────────┘   │└─────────────────────────────────────────────────────────────────────┘

2.2 ProcessLifecycleOwner — App 级生命周期

ProcessLifecycleOwner 是 Jetpack Lifecycle 提供的应用级生命周期观察者,能感知整个 App 的前后台切换,而非单个 Activity 的状态。

┌─────────────────────────────────────────────────────────────────┐│               ProcessLifecycleOwner 状态转换                      ││                                                                 ││  App 冷启动                                                       ││      ↓                                                          ││   ON_CREATE ──→ ON_START ──→ ON_RESUME   ← App 进入前台           ││                                  │                              ││                           用户按 Home 键                          ││                                  ↓                              ││                             ON_PAUSE                            ││                                  ↓ (约 700ms 延迟,确认真正后台)    ││                             ON_STOP    ← App 进入后台             ││                                  │                              ││                           用户返回 App                            ││                                  ↓                              ││                   ON_START ──→ ON_RESUME                         ││                                                                 ││  注意:ProcessLifecycleOwner 永远不会收到 ON_DESTROY               ││       (进程死亡时直接消失,没有回调机会)                            │└─────────────────────────────────────────────────────────────────┘

使用示例:

// Application 级别监听前后台切换classMyApp : Application(), DefaultLifecycleObserver {overridefunonCreate() {super.onCreate()        ProcessLifecycleOwner.get().lifecycle.addObserver(this)    }overridefunonStart(owner: LifecycleOwner) {// App 从后台回到前台        Log.d("AppLifecycle""App 进入前台")        AppStateManager.isInForeground = true    }overridefunonStop(owner: LifecycleOwner) {// App 进入后台        Log.d("AppLifecycle""App 进入后台")        AppStateManager.isInForeground = false    }}

三、Activity 与 Fragment 完整生命周期

3.1 Activity 生命周期详解

┌─────────────────────────────────────────────────────────────────────┐│                      Activity 完整生命周期                             ││                                                                     ││  [首次创建]           [从后台返回]        [配置变更/系统回收后返回]       ││       ↓                   ↓                       ↓                 ││  onCreate()         onRestart()            onCreate()               ││  ↓ 初始化 UI/数据      ↓                   ↓ Bundle 中有数据           ││  onStart()          onStart()             onStart()                 ││  ↓ 可见不可交互        ↓                   ↓                          ││  onResume()         onResume()            onResume()                ││  ↓ 前台,可交互        ↓                                              ││  ┌──────────────────────────────────┐                               ││  │          运行中(Resumed)         │                               ││  └──────────────────────────────────┘                               ││       ↓ 部分遮挡/失去焦点                                              ││  onPause()   ← 应在此保存轻量级持久数据(如草稿)                        ││       ↓ 完全不可见(用户按 Home/切换 App)                               ││  onStop()    ← 停止耗资源的操作(如相机、动画)                           ││       ↓                                                             ││  onSaveInstanceState()  ← 系统可能在此之后杀死进程                      ││       ↓                      ↑                                      ││  [进程可能被杀死]         [用户返回,恢复]                                ││       ↓                                                             ││  onDestroy()  ← finish() 或配置变更导致的销毁                          │└─────────────────────────────────────────────────────────────────────┘

3.2 关键回调的职责边界

回调
应该做
不应该做
onCreate()
初始化 UI、ViewModel、恢复状态
启动动画、网络请求
onStart()
注册广播接收器、开始 UI 更新
耗时初始化
onResume()
恢复相机、传感器、动画
数据库查询
onPause()
暂停动画、提交草稿
保存大量数据(会阻塞切换)
onStop()
释放相机、停止定位、取消非关键请求
提交事务(可能崩溃)
onDestroy()
释放不会随 ViewModel 自动清理的资源
保存用户数据(可能不被调用!)
onSaveInstanceState()
保存 UI 瞬态(滚动位置、输入内容)
保存大对象或持久数据

关键陷阱onDestroy() 在进程被系统杀死时不会被调用,切勿依赖它保存数据。

3.3 Fragment 生命周期与 Activity 的关系

┌──────────────────────────────────────────────────────────────────────┐│              Fragment 与 Activity 生命周期对应关系                       ││                                                                      ││  Activity            Fragment                 Fragment View          ││  ──────────          ────────────────         ──────────────         ││  onCreate()   ──→    onAttach()                                      ││                      onCreate()                                      ││                      onCreateView()    ──→    View 创建               ││                      onViewCreated()   ──→    初始化 View              ││                      onViewStateRestored()                           ││  onStart()    ──→    onStart()                                       ││  onResume()   ──→    onResume()                                      ││                                                                      ││  onPause()    ──→    onPause()                                       ││  onStop()     ──→    onStop()                                        ││                      onSaveInstanceState()                           ││                      onDestroyView()   ──→    View 销毁               ││  onDestroy()  ──→    onDestroy()                                     ││                      onDetach()                                      │└──────────────────────────────────────────────────────────────────────┘

Fragment ViewLifecycleOwner 的重要性:

// ✗ 错误:使用 this(Fragment 生命周期)观察 LiveData// Fragment 重建时 View 已销毁,但 Fragment 实例仍存在,可能导致 null 崩溃viewModel.data.observe(this) { data ->    binding.textView.text = data// binding 可能已经是旧的 View!}// ✓ 正确:使用 viewLifecycleOwner(View 生命周期)viewModel.data.observe(viewLifecycleOwner) { data ->    binding.textView.text = data}

四、Process Death 深度解析

4.1 什么是 Process Death

Process Death(进程死亡)是指 Android 系统为回收内存,主动终止后台应用进程的行为。

┌─────────────────────────────────────────────────────────────────────┐│                    Process Death 触发场景                              ││                                                                     ││  用户操作 App A ──→ 按 Home 键 ──→ App A 进入后台                       ││                                        │                            ││                              [系统内存不足 / LRU 淘汰]                  ││                                        │                            ││                              App A 进程被 Kill -9                     ││                              (所有内存状态全部丢失)                     ││                                        │                            ││                              用户重新点击 App A 图标                     ││                                        ↓                            ││                    ┌───────────────────────────────────┐            ││                    │  系统需要还原用户之前的导航状态!          │            ││                    │                                   │            ││                    │  Back Stack 结构已由系统持久化:        │            ││                    │  [Activity C] ← [Activity B] ← [MainActivity]  ││                    │                                   │            ││                    │  按原有栈结构重新创建 Activity C        │            ││                    │  并将 savedInstanceState Bundle     │            ││                    │  传递给 onCreate()                  │            ││                    └───────────────────────────────────┘            │└─────────────────────────────────────────────────────────────────────┘

4.2 Process Death 与 正常销毁的本质区别

┌──────────────────────┬──────────────────────────┬──────────────────────┐│ 对比项               │ 正常销毁(finish/旋转屏)    │ Process Death         │├──────────────────────┼──────────────────────────┼──────────────────────┤│ onStop 调用          │ ✓ 调用                    │ ✓ 调用               ││ onSaveInstanceState  │ ✓ 调用(旋转/被系统回收)  │ ✓ 调用               ││ onDestroy 调用       │ ✓ 调用                    │ ✗ 不调用             ││ ViewModel 保留       │ 旋转✓ / finish ✗          │ ✗ 全部丢失           ││ 静态变量             │ 可能保留                   │ ✗ 全部丢失           ││ Application 对象     │ 保留                      │ ✗ 重新创建           ││ 单例对象             │ 保留                      │ ✗ 全部重新创建        ││ Bundle 数据          │ onSaveInstanceState 中保存  │ ✓ 系统持久化保留     │└──────────────────────┴──────────────────────────┴──────────────────────┘

4.3 在开发环境中模拟 Process Death

# 方法一:通过 adb 命令(最准确)# 1. 先将 App 切换到后台(按 Home 键)# 2. 执行命令杀死进程(不会触发 onDestroy)adb shell am kill <package_name># 例如:adb shell am kill com.example.myapp# 方法二:Android Studio 的 "Terminate Application" 按钮# Run 面板 → 左侧红色停止按钮旁的下拉 → "Terminate Application"# 注意:直接点击停止会调用 onDestroy,不是真正的 Process Death# 方法三:Developer Options(推荐日常测试)# 系统设置 → 开发者选项 → "Don't keep activities"# 勾选后每次 App 进入后台都会销毁 Activity(但进程不一定死)

五、onSaveInstanceState 机制

5.1 工作原理

┌─────────────────────────────────────────────────────────────────────┐│               onSaveInstanceState 调用时机                            ││                                                                     ││  Android 6.0 之前:onPause() 之前调用                                  ││  Android 6.0 之后:onStop() 之后调用(时间窗口更大)                      ││  Android 9.0 之后:onStop() 之后调用(确保 Fragment 事务可提交)           ││                                                                     ││  触发条件:                                                            ││  ✓ 系统可能回收 Activity(按 Home 键进入后台)                            ││  ✓ 配置变更(旋转屏幕、字体大小改变、语言切换)                             ││  ✓ 多窗口模式切换                                                      ││                                                                     ││  不触发条件:                                                           ││  ✗ 用户主动 finish()(用户明确选择离开,不需要恢复)                        ││  ✗ 用户按返回键(同上)                                                  │└─────────────────────────────────────────────────────────────────────┘

5.2 系统自动保存的 View 状态

Android View 系统内置了状态保存机制,前提是 View 必须有 ID

// EditText、RecyclerView、ScrollView、CheckBox 等控件会自动保存状态// 自动保存示例:// <EditText android:id="@+id/et_input" .../> → 内容自动保存// 没有 ID 的 View 不会自动保存!// <EditText .../> → 内容丢失// 自定义 View 实现状态保存classCustomView@JvmOverloadsconstructor(    context: Context, attrs: AttributeSet? = null) : View(context, attrs) {privatevar selectedIndex = 0overridefunonSaveInstanceState(): Parcelable {val superState = super.onSaveInstanceState()return Bundle().apply {            putParcelable("super_state", superState)            putInt("selected_index", selectedIndex)        }    }overridefunonRestoreInstanceState(state: Parcelable?) {if (state is Bundle) {            selectedIndex = state.getInt("selected_index")super.onRestoreInstanceState(state.getParcelable("super_state"))        } else {super.onRestoreInstanceState(state)        }    }}

5.3 手动保存 UI 状态

classCheckoutActivity : AppCompatActivity() {privatevar cartItems: List<CartItem> = emptyList()privatevar currentStep = 0overridefunonSaveInstanceState(outState: Bundle) {super.onSaveInstanceState(outState)// ✓ 适合保存:轻量级 UI 状态(< 50KB)        outState.putInt("current_step", currentStep)        outState.putParcelableArrayList("cart_items", ArrayList(cartItems))// ✗ 不适合保存:Bitmap、大型列表、网络数据    }overridefunonCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)        setContentView(R.layout.activity_checkout)// 区分首次创建和恢复场景if (savedInstanceState != null) {// 从系统回收或配置变更恢复            currentStep = savedInstanceState.getInt("current_step")            cartItems = savedInstanceState.getParcelableArrayList("cart_items") ?: emptyList()            Log.d("Checkout""恢复状态: step=$currentStep")        } else {// 首次创建            Log.d("Checkout""首次创建")        }    }}

5.4 onSaveInstanceState 的局限性

限制
原因
解决方案
数据大小限制(< 1MB,实践 < 50KB)
通过 Binder 传输,Binder 事务有大小限制
使用 Room/DataStore 持久化
只能存储 Parcelable/Serializable
Bundle 的序列化机制限制
使用 ViewModel + Repository
序列化/反序列化有性能开销
每次重建都需要
复杂对象用 ViewModel 保存
主线程执行
不能做耗时操作
异步预存到数据库

六、ViewModel 在生命周期管理中的角色

6.1 ViewModel 的生命周期

┌─────────────────────────────────────────────────────────────────────┐│              ViewModel vs Activity 生命周期对比                         ││                                                                     ││  Activity 生命周期:                                                    ││  onCreate → onStart → onResume → onPause → onStop → onDestroy       ││     ↑                                                    ↑          ││     │               [旋转屏幕]                             │          ││     │                                                    │          ││  onCreate → ... → onDestroy → onCreate → ... → onDestroy           ││                                                                     ││  ViewModel 生命周期:                                                   ││  ┌─────────────────────────────────────────────────────────────┐    ││  │                     ViewModel 存活                           │    ││  │   旋转/配置变更不会销毁 ViewModel,Activity 重建后自动关联       │    ││  └─────────────────────────────────────────────────────────────┘    ││                                          ↓ finish() 或 Process Death  ││                                      onCleared() 被调用               ││                                      ViewModel 销毁                   ││                                                                     ││  ViewModel 无法在 Process Death 中存活!                                │└─────────────────────────────────────────────────────────────────────┘

6.2 ViewModel 的正确使用

// ViewModel 保存内存中的 UI 状态(抵抗配置变更)classProductViewModel(privateval repository: ProductRepository,privateval savedStateHandle: SavedStateHandle  // 处理 Process Death 的关键) : ViewModel() {// 从 savedStateHandle 恢复 productId(Process Death 后可恢复)privateval productId: String = savedStateHandle["product_id"]        ?: throw IllegalArgumentException("product_id is required")// UI 状态(仅存活于内存,Process Death 后丢失,需要重新加载)privateval _uiState = MutableStateFlow(ProductUiState.Loading)val uiState: StateFlow<ProductUiState> = _uiState.asStateFlow()// 用户选择状态(存储在 savedStateHandle,Process Death 后可恢复)var selectedColor: String?get() = savedStateHandle["selected_color"]set(value) { savedStateHandle["selected_color"] = value }init {        loadProduct()    }privatefunloadProduct() {        viewModelScope.launch {            _uiState.value = ProductUiState.Loadingtry {val product = repository.getProduct(productId)                _uiState.value = ProductUiState.Success(product)            } catch (e: Exception) {                _uiState.value = ProductUiState.Error(e.message ?: "加载失败")            }        }    }overridefunonCleared() {super.onCleared()// 释放资源(viewModelScope 会自动取消协程)    }}

七、SavedStateHandle — Process Death 的核心解决方案

7.1 SavedStateHandle 原理

SavedStateHandle 是 Jetpack 提供的状态保存方案,它将 ViewModel 的数据桥接到 onSaveInstanceState 机制中,从而在 Process Death 后也能恢复状态。

┌─────────────────────────────────────────────────────────────────────┐│               SavedStateHandle 工作原理                                ││                                                                     ││  [进程活跃时]                                                          ││  SavedStateHandle ←→ ViewModel(内存中)                               ││                                                                     ││  [onSaveInstanceState 触发时]                                         ││  SavedStateHandle 的数据 ──序列化──→ Bundle ──→ 系统持久化               ││                                                                     ││  [Process Death 后用户返回]                                            ││  系统持久化 Bundle ──→ Activity.onCreate(savedState)                   ││       │                                                             ││       └──→ ViewModelProvider 将 Bundle 传给 SavedStateHandle          ││                   │                                                 ││                   └──→ ViewModel.init 时可读取上次保存的值               │└─────────────────────────────────────────────────────────────────────┘

7.2 SavedStateHandle 完整使用示例

// build.gradle// implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.7.0"classSearchViewModel(privateval savedStateHandle: SavedStateHandle,privateval searchRepository: SearchRepository) : ViewModel() {// 方式一:直接读写(适合简单值)var searchQuery: Stringget() = savedStateHandle["search_query"] ?: ""set(value) {            savedStateHandle["search_query"] = value            performSearch(value)        }// 方式二:StateFlow(推荐,支持响应式)val searchQueryFlow: StateFlow<String> =        savedStateHandle.getStateFlow("search_query""")// 方式三:LiveData(兼容旧代码)val searchQueryLive: LiveData<String> =        savedStateHandle.getLiveData("search_query""")privateval _searchResults = MutableStateFlow<List<SearchResult>>(emptyList())val searchResults: StateFlow<List<SearchResult>> = _searchResults.asStateFlow()init {// Process Death 恢复后,自动恢复上次的搜索val lastQuery = savedStateHandle.get<String>("search_query")if (!lastQuery.isNullOrEmpty()) {            performSearch(lastQuery)        }    }funonQueryChanged(query: String) {        savedStateHandle["search_query"] = query        performSearch(query)    }privatefunperformSearch(query: String) {        viewModelScope.launch {val results = searchRepository.search(query)            _searchResults.value = results        }    }}// Activity 中使用(Hilt 注入)@HiltViewModelclassSearchViewModel@Injectconstructor(privateval savedStateHandle: SavedStateHandle,privateval searchRepository: SearchRepository) : ViewModel()// Fragment 中使用classSearchFragment : Fragment() {privateval viewModel: SearchViewModel by viewModels()}

7.3 SavedStateHandle 的容量限制

SavedStateHandle 底层依赖 onSaveInstanceState同样受 Bundle 大小限制(实践中 < 50KB)。

对于大数据,应结合持久化存储:

classCartViewModel(privateval savedStateHandle: SavedStateHandle,privateval cartRepository: CartRepository) : ViewModel() {// ✓ 在 savedStateHandle 中仅存储 ID(轻量)privatevar cartId: String?get() = savedStateHandle["cart_id"]set(value) { savedStateHandle["cart_id"] = value }privateval _cart = MutableStateFlow<Cart?>(null)val cart: StateFlow<Cart?> = _cart.asStateFlow()init {val restoredCartId = cartIdif (restoredCartId != null) {// Process Death 后:用 ID 从数据库重新加载完整数据            loadCart(restoredCartId)        }    }funcreateCart(userId: String) {        viewModelScope.launch {val newCart = cartRepository.createCart(userId)            cartId = newCart.id  // 保存 ID 到 savedStateHandle            _cart.value = newCart        }    }privatefunloadCart(id: String) {        viewModelScope.launch {            _cart.value = cartRepository.getCart(id)        }    }}

八、数据持久化策略分层

8.1 数据存储选择指南

┌─────────────────────────────────────────────────────────────────────┐│                    数据持久化策略选择                                    ││                                                                     ││  ┌──────────────────────────────────────────────────────────────┐   ││  │ 层级一:内存(ViewModel 中的 StateFlow/LiveData)               │   ││  │ 生存时间:配置变更后存活,Process Death 后丢失                    │   ││  │ 适合:从网络/数据库加载的数据缓存,用于驱动 UI                     │   ││  └──────────────────────────────────────────────────────────────┘   ││              ↓ 如需在 Process Death 后恢复                             ││  ┌──────────────────────────────────────────────────────────────┐   ││  │ 层级二:SavedStateHandle / onSaveInstanceState               │   ││  │ 生存时间:Process Death 后存活,应用被用户强杀后丢失               │   ││  │ 适合:UI 瞬态(滚动位置、输入内容、选中状态、当前步骤)              │   ││  │ 限制:< 50KB,仅存简单值和 ID                                   │   ││  └──────────────────────────────────────────────────────────────┘   ││              ↓ 如需永久保存                                            ││  ┌──────────────────────────────────────────────────────────────┐   ││  │ 层级三:本地持久化存储                                           │   ││  │ Room Database:结构化数据,支持复杂查询                           │   ││  │ DataStore:用户配置、简单键值对(替代 SharedPreferences)         │   ││  │ File:媒体文件、缓存数据                                        │   ││  │ 生存时间:卸载前永久存在                                         │   ││  └──────────────────────────────────────────────────────────────┘   ││              ↓ 如需跨设备同步                                          ││  ┌──────────────────────────────────────────────────────────────┐   ││  │ 层级四:远端存储(服务器 API / Firebase)                        │   ││  └──────────────────────────────────────────────────────────────┘   │└─────────────────────────────────────────────────────────────────────┘

8.2 DataStore 替代 SharedPreferences

SharedPreferences 在主线程 apply() 虽不阻塞 UI,但 commit() 会阻塞,且 apply() 在 onStop() 时会等待写入完成,可能导致 ANR。DataStore 完全异步且支持 Flow:

// 定义 DataStoreval Context.userPreferencesStore by preferencesDataStore("user_preferences")classUserPreferencesRepository(privateval context: Context) {privateval USER_DARK_MODE = booleanPreferencesKey("dark_mode")privateval USER_LANGUAGE = stringPreferencesKey("language")// 读取(Flow,响应式)val darkModeEnabled: Flow<Boolean> = context.userPreferencesStore.data        .map { preferences -> preferences[USER_DARK_MODE] ?: false }// 写入(挂起函数,异步)suspendfunsetDarkMode(enabled: Boolean) {        context.userPreferencesStore.edit { preferences ->            preferences[USER_DARK_MODE] = enabled        }    }// Proto DataStore(强类型,需要 protobuf 定义)// 推荐用于结构化配置}

九、极端场景处理实战

9.1 场景一:多步骤表单(Process Death 中途恢复)

// 模型定义dataclassCheckoutState(val step: Int = 0,                    // 当前步骤(0=地址, 1=支付, 2=确认)val addressId: String? = null,        // 选择的地址 IDval paymentMethodId: String? = null,  // 选择的支付方式 IDval couponCode: String = ""// 输入的优惠码) : Parcelable  // 实现 Parcelable 才能存入 savedStateHandle@HiltViewModelclassCheckoutViewModel@Injectconstructor(privateval savedStateHandle: SavedStateHandle,privateval checkoutRepository: CheckoutRepository) : ViewModel() {// CheckoutState 实现了 Parcelable,可直接存入 savedStateHandlevar checkoutState: CheckoutStateget() = savedStateHandle["checkout_state"] ?: CheckoutState()set(value) { savedStateHandle["checkout_state"] = value }val checkoutStateFlow: StateFlow<CheckoutState> =        savedStateHandle.getStateFlow("checkout_state", CheckoutState())funselectAddress(addressId: String) {        checkoutState = checkoutState.copy(            step = 1,            addressId = addressId        )    }funselectPayment(paymentMethodId: String) {        checkoutState = checkoutState.copy(            step = 2,            paymentMethodId = paymentMethodId        )    }funupdateCoupon(code: String) {        checkoutState = checkoutState.copy(couponCode = code)    }}// Fragment 中恢复 UIclassCheckoutFragment : Fragment() {privateval viewModel: CheckoutViewModel by activityViewModels()overridefunonViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)        viewLifecycleOwner.lifecycleScope.launch {            viewModel.checkoutStateFlow.collect { state ->// 根据 state 恢复到正确步骤                showStep(state.step)                state.addressId?.let { restoreAddressSelection(it) }                binding.etCoupon.setText(state.couponCode)            }        }    }}

9.2 场景二:后台任务在 Process Death 后继续执行

普通协程在 Process Death 后会被取消。对于必须完成的任务(如上传文件、同步数据),应使用 WorkManager

// 定义可靠后台任务classUploadWorker(    context: Context,    workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) {overridesuspendfundoWork(): Result {val fileUri = inputData.getString("file_uri") ?: return Result.failure()val uploadId = inputData.getString("upload_id") ?: return Result.failure()returntry {// 即使进程重启,WorkManager 也会重新执行此任务val uploadedUrl = uploadRepository.uploadFile(fileUri)val outputData = workDataOf("uploaded_url" to uploadedUrl)            Result.success(outputData)        } catch (e: IOException) {// 网络错误:重试if (runAttemptCount < 3) Result.retry() else Result.failure()        } catch (e: Exception) {            Result.failure()        }    }}// 提交任务funscheduleUpload(fileUri: String) {val uploadId = UUID.randomUUID().toString()val constraints = Constraints.Builder()        .setRequiredNetworkType(NetworkType.CONNECTED)        .build()val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()        .setInputData(workDataOf("file_uri" to fileUri,"upload_id" to uploadId        ))        .setConstraints(constraints)        .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)        .addTag("file_upload")        .build()    WorkManager.getInstance(context)        .enqueueUniqueWork(            uploadId,  // 唯一名称,防止重复提交            ExistingWorkPolicy.KEEP,            uploadRequest        )}

9.3 场景三:网络请求去重与 Process Death 后重试

@HiltViewModelclassArticleViewModel@Injectconstructor(privateval savedStateHandle: SavedStateHandle,privateval articleRepository: ArticleRepository) : ViewModel() {privateval articleId: String = savedStateHandle["article_id"]        ?: throw IllegalArgumentException("article_id required")// 使用 stateIn 将 Flow 转换为 StateFlow,支持订阅者离开后缓存val articleState: StateFlow<UiState<Article>> = articleRepository        .getArticle(articleId)        .map<Article, UiState<Article>> { UiState.Success(it) }        .catch { emit(UiState.Error(it.message ?: "加载失败")) }        .stateIn(            scope = viewModelScope,            started = SharingStarted.WhileSubscribed(5000),  // 5s 内无订阅者则停止上游            initialValue = UiState.Loading        )// SharingStarted.WhileSubscribed(5000) 的作用:// - 屏幕旋转时,新 Activity 5秒内重新订阅,数据不会重新加载// - Process Death 后,新进程重新订阅,重新从数据库/网络加载}

9.4 场景四:检测 App 是从 Process Death 恢复还是正常启动

classMainActivity : AppCompatActivity() {overridefunonCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val launchSource = when {// savedInstanceState 不为空 → 系统恢复(包括 Process Death 恢复和旋转)            savedInstanceState != null -> LaunchSource.SYSTEM_RESTORE// intent 中有特定 flag → 通知点击等外部唤起            intent.hasExtra("from_notification") -> LaunchSource.NOTIFICATION// 否则 → 正常冷启动else -> LaunchSource.COLD_START        }when (launchSource) {            LaunchSource.SYSTEM_RESTORE -> {// Process Death 恢复:不需要显示引导页,直接恢复到上次状态                Log.d("Launch""从系统恢复,跳过引导")            }            LaunchSource.COLD_START -> {// 冷启动:可以显示 Splash、检查登录状态等                handleColdStart()            }            LaunchSource.NOTIFICATION -> {// 通知唤起:导航到对应页面                handleNotificationLaunch()            }        }    }}enumclassLaunchSource{    COLD_START, SYSTEM_RESTORE, NOTIFICATION}

十、生命周期感知组件最佳实践

10.1 使用 repeatOnLifecycle 安全收集 Flow

classHomeFragment : Fragment() {privateval viewModel: HomeViewModel by viewModels()overridefunonViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// ✗ 错误:lifecycleScope.launch 在 Fragment stop 后仍会收到更新// 可能在 View 不可见时更新 UI,浪费资源甚至崩溃        lifecycleScope.launch {            viewModel.uiState.collect { state ->                updateUi(state)  // Fragment 后台时仍执行            }        }// ✓ 正确:repeatOnLifecycle 仅在生命周期 STARTED 及以上才收集        viewLifecycleOwner.lifecycleScope.launch {            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {                viewModel.uiState.collect { state ->                    updateUi(state)  // 只在 Fragment 可见时执行                }            }        }// ✓ 多个 Flow 并发收集        viewLifecycleOwner.lifecycleScope.launch {            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {                launch {                    viewModel.products.collect { updateProductList(it) }                }                launch {                    viewModel.cartCount.collect { updateCartBadge(it) }                }            }        }    }}

10.2 LifecycleObserver 解耦生命周期逻辑

// 将生命周期相关逻辑封装成独立组件,解耦 Activity/FragmentclassLocationTracker(privateval context: Context,privateval lifecycle: Lifecycle) : DefaultLifecycleObserver {privatevar locationClient: FusedLocationProviderClient? = nullprivatevar locationCallback: LocationCallback? = nullinit {        lifecycle.addObserver(this)    }overridefunonStart(owner: LifecycleOwner) {        startLocationUpdates()    }overridefunonStop(owner: LifecycleOwner) {        stopLocationUpdates()    }overridefunonDestroy(owner: LifecycleOwner) {        lifecycle.removeObserver(this)    }privatefunstartLocationUpdates() { /* ... */ }privatefunstopLocationUpdates() { /* ... */ }}// Activity 中只需一行代码classMapActivity : AppCompatActivity() {overridefunonCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// LocationTracker 自己管理生命周期        LocationTracker(this, lifecycle)    }}

10.3 避免内存泄漏的生命周期管理

classImageUploadActivity : AppCompatActivity() {// ✗ 错误:持有 Activity 引用的回调,可能导致内存泄漏privateval uploadCallback = object : UploadCallback {overridefunonSuccess(url: String) {// 如果 Activity 已销毁,这里会崩溃            updateUI(url)        }    }// ✓ 正确方案一:使用 lifecycleScope,Activity 销毁时自动取消privatefunuploadImage(uri: Uri) {        lifecycleScope.launch {try {val url = uploadRepository.upload(uri)                updateUI(url)            } catch (e: CancellationException) {// 正常取消,不处理            }        }    }// ✓ 正确方案二:在 ViewModel 中处理上传,Activity 只观察结果privateval viewModel: UploadViewModel by viewModels()overridefunonCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)        viewLifecycleOwner.lifecycleScope.launch {            repeatOnLifecycle(Lifecycle.State.STARTED) {                viewModel.uploadResult.collect { result ->when (result) {is UploadResult.Success -> showSuccessUI(result.url)is UploadResult.Error -> showError(result.message)                        UploadResult.Loading -> showProgress()                    }                }            }        }    }}

十一、完整架构设计:应对 Process Death 的最佳实践

11.1 推荐架构分层

┌─────────────────────────────────────────────────────────────────────┐│              应对 Process Death 的完整架构设计                          ││                                                                     ││  ┌──────────────────────────────────────────────────────────────┐   ││  │  UI 层(Activity / Fragment)                                  │   ││  │  • 观察 StateFlow / LiveData                                  │   ││  │  • 使用 repeatOnLifecycle 安全收集                              │   ││  │  • 通过 savedInstanceState 保存 View 的纯 UI 瞬态              │   ││  └────────────────────────┬─────────────────────────────────────┘   ││                           │ 委托                                      ││  ┌────────────────────────▼─────────────────────────────────────┐   ││  │  ViewModel 层                                                  │   ││  │  • 持有 UI 状态(内存级,抵抗配置变更)                            │   ││  │  • 使用 SavedStateHandle 保存关键 ID 和轻量状态(抵抗 Process Death)││  │  • 通过 viewModelScope 管理协程                                 │   ││  └────────────────────────┬─────────────────────────────────────┘   ││                           │ 请求                                      ││  ┌────────────────────────▼─────────────────────────────────────┐   ││  │  Repository 层(Single Source of Truth)                       │   ││  │  • 协调网络数据与本地数据库                                       │   ││  │  • 暴露 Flow,支持数据库驱动 UI(Room + Flow)                    │   ││  │  • 数据库作为唯一真实数据源,网络数据先写库再读取                    │   ││  └────────────┬─────────────────────────┬────────────────────────┘   ││               │                         │                            ││  ┌────────────▼───────────┐  ┌──────────▼───────────────────────┐   ││  │  网络层(Retrofit/OkHttp)│  │  本地存储层(Room / DataStore)    │   ││  │  • 无状态,只做请求       │  │  • Process Death 后数据不丢失      │   ││  └────────────────────────┘  └──────────────────────────────────┘   │└─────────────────────────────────────────────────────────────────────┘

11.2 数据流设计(Room + Flow + ViewModel)

// Repository:数据库作为单一数据源classProductRepository@Injectconstructor(privateval productDao: ProductDao,privateval apiService: ApiService) {// Room + Flow:数据库变化时自动通知 ViewModelfungetProducts(): Flow<List<Product>> = productDao.getAllProducts()// 刷新:从网络获取后写入数据库,Flow 自动推送更新suspendfunrefreshProducts() {val remoteProducts = apiService.fetchProducts()        productDao.insertAll(remoteProducts)// 不需要手动通知 UI,Room 的 Flow 会自动触发    }}// ViewModel@HiltViewModelclassProductListViewModel@Injectconstructor(privateval repository: ProductRepository) : ViewModel() {val products: StateFlow<List<Product>> = repository.getProducts()        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())init {// Process Death 后重新创建 ViewModel,自动重新从数据库加载// 数据库有缓存则立即显示,同时异步刷新        refreshProducts()    }privatefunrefreshProducts() {        viewModelScope.launch {try {                repository.refreshProducts()            } catch (e: Exception) {// 网络失败不影响显示(数据库中有旧数据)            }        }    }}

十二、测试生命周期与 Process Death

12.1 使用 Robolectric 测试生命周期

@RunWith(RobolectricTestRunner::class)classMainActivityTest{@Testfun `旋转屏幕后 ViewModel 数据不丢失`() {val scenario = ActivityScenario.launch(MainActivity::class.java)        scenario.onActivity { activity ->val viewModel = activity.viewModels<MainViewModel>().value            viewModel.setData("test_data")        }// 模拟配置变更        scenario.recreate()        scenario.onActivity { activity ->val viewModel = activity.viewModels<MainViewModel>().value            assertEquals("test_data", viewModel.getData())        }    }@Testfun `Process Death 后 SavedStateHandle 数据恢复`() {val scenario = ActivityScenario.launch(MainActivity::class.java)        scenario.onActivity { activity ->val viewModel = activity.viewModels<MainViewModel>().value            viewModel.setSearchQuery("kotlin")        }// 模拟 Process Death(保存状态后重建)val bundle = Bundle()        scenario.onActivity { activity ->            activity.onSaveInstanceState(bundle)        }// 用保存的状态重建val newScenario = ActivityScenario.launch(            Intent(ApplicationProvider.getApplicationContext(), MainActivity::class.java)                .also { it.putExtras(bundle) }        )        newScenario.onActivity { activity ->val viewModel = activity.viewModels<MainViewModel>().value            assertEquals("kotlin", viewModel.searchQuery)        }    }}

12.2 StateRestorationTester(Compose)

@Testfun `Compose 组件 Process Death 后状态恢复`() {val restorationTester = StateRestorationTester(composeTestRule)    restorationTester.setContent {var count by rememberSaveable { mutableStateOf(0) }        Button(onClick = { count++ }) {            Text("Count: $count")        }    }// 点击按钮增加计数    composeTestRule.onNodeWithText("Count: 0").performClick()    composeTestRule.onNodeWithText("Count: 1").assertIsDisplayed()// 模拟 Process Death 并恢复    restorationTester.emulateSavedInstanceStateRestore()// 验证状态恢复    composeTestRule.onNodeWithText("Count: 1").assertIsDisplayed()}

十三、常见问题与避坑指南

13.1 高频错误汇总

┌─────────────────────────────────────────────────────────────────────┐│                    Process Death 相关高频错误                           ││                                                                     ││  错误 1:依赖 Application 单例中的内存状态                               ││  ──────────────────────────────────────────────────────────────     ││  class MyApp : Application() {                                      ││      var currentUser: User? = null  // ✗ Process Death 后丢失        ││  }                                                                  ││  解决:将 currentUser 存储在 DataStore/Room 中                         ││                                                                     ││  错误 2:在 onDestroy 中保存关键数据                                    ││  ──────────────────────────────────────────────────────────────     ││  override fun onDestroy() {                                         ││      saveData()  // ✗ Process Death 时不调用!                        ││  }                                                                  ││  解决:在 onPause/onStop 中保存,或使用 Room 实时同步                    ││                                                                     ││  错误 3:将大对象存入 savedInstanceState                               ││  ──────────────────────────────────────────────────────────────     ││  outState.putParcelable("bitmap", largeBitmap)  // ✗ 超过 1MB 崩溃   ││  解决:保存 URI 或文件路径,重建时重新加载                               ││                                                                     ││  错误 4:Fragment 提交事务在错误的时机                                  ││  ──────────────────────────────────────────────────────────────     ││  override fun onStop() {                                            ││      supportFragmentManager.commit { ... }  // ✗ IllegalStateException ││  }                                                                  ││  解决:使用 commitAllowingStateLoss() 或在 onStart/onResume 中提交    ││                                                                     ││  错误 5:未处理 Fragment 回退栈在 Process Death 后的恢复                ││  ──────────────────────────────────────────────────────────────     ││  // 系统会自动恢复 Fragment 回退栈,但重复 add 会导致 Fragment 叠加      ││  if (savedInstanceState == null) {  // ✓ 仅在首次创建时添加 Fragment   ││      supportFragmentManager.commit {                                ││          add(R.id.container, HomeFragment())                        ││      }                                                              ││  }                                                                  │└─────────────────────────────────────────────────────────────────────┘

13.2 Process Death 全场景应对清单

数据类型
保存方式
恢复方式
当前页面/步骤
SavedStateHandle
ViewModel init 中读取
表单输入内容
SavedStateHandle

 / View 自动保存(有 ID)
自动恢复
选中状态、滚动位置
onSaveInstanceState

 / View 自动保存
onRestoreInstanceState
已加载的列表数据
Room 数据库(Single Source of Truth)
Repository Flow 自动推送
用户登录信息
DataStore

 / EncryptedSharedPreferences
Application 启动时读取
上传/下载任务
WorkManager
进程重启后自动恢复执行
正在播放的媒体
MediaSession

 + 前台 Service
Service onStartCommand 中恢复
导航目的地
Navigation 组件自动管理 Back Stack
自动恢复

十四、问题解答

Q1:什么情况下 onSaveInstanceState 会被调用?什么情况下不会?

会调用:按 Home 键进入后台(系统可能回收)、屏幕旋转、多窗口切换、用户切换 App。不会调用:用户按返回键(明确关闭)、调用 finish()(明确结束)。核心判断标准:用户是否有意离开当前页面。意外离开调用,主动离开不调用。

Q2:ViewModel 能在 Process Death 后存活吗?

不能。ViewModel 存储在内存中,Process Death 后进程被杀死,内存全部释放,ViewModel 实例消失。ViewModel 能抵抗的是配置变更(屏幕旋转等),而非进程被杀。处理 Process Death 需要使用 SavedStateHandle(轻量数据)或持久化存储(Room/DataStore)。

Q3:onSaveInstanceState 和 SharedPreferences 有什么区别?

onSaveInstanceState:临时保存 UI 瞬态,用户”返回”该页面时恢复;用户主动关闭 App 后即失效;适合存储 UI 选择状态(如滚动位置)。SharedPreferences/DataStore:持久化存储,卸载前永远存在;适合用户偏好、登录 token 等需要跨会话保留的数据。

Q4:如何模拟 Process Death 进行测试?

最准确的方式:将 App 切换到后台,然后执行 adb shell am kill <package_name>,再切回 App。注意:直接在 Android Studio 点击停止按钮会触发 onDestroy(),不是真正的 Process Death。

Q5:repeatOnLifecycle 和 launchWhenStarted 有什么区别?

launchWhenStarted:生命周期低于 STARTED 时挂起协程,但协程仍然存活,当 Flow 有新值时会缓存,恢复后一次性处理,可能导致短时间内大量 UI 更新。repeatOnLifecycle(STARTED):生命周期低于 STARTED 时取消协程,恢复后重新启动,不会积压数据,是推荐做法。

Q6:Fragment 在 Process Death 后恢复时,如何避免 Fragment 重叠?

系统恢复时会自动从 Back Stack 重建 Fragment,开发者不应再次手动添加。正确做法:if (savedInstanceState == null) { /* 首次创建时才添加 Fragment */ }检查 savedInstanceState != null 表示正在从系统恢复,Fragment 会自动重建,不需要手动添加。

Q7:SharingStarted.WhileSubscribed(5000) 中 5000ms 的意义?

当所有订阅者离开时,等待 5000ms 后再停止上游 Flow 的收集。这样在屏幕旋转时(Activity 销毁→重建约需 300ms),新 Activity 能在 5s 内重新订阅,不会触发重新加载,直接使用缓存数据。而 Process Death 后重建进程需要更长时间,超过 5s 后上游已停止,重建后会重新订阅并触发加载。


十五、总结

┌─────────────────────────────────────────────────────────────────────┐│              Process Death 完整应对策略一览                             ││                                                                     ││  数据分类:                                                            ││  ┌──────────────┬────────────────────┬──────────────────────────┐   ││  │ 数据特性      │ 推荐存储方式          │ 示例                      │   ││  ├──────────────┼────────────────────┼──────────────────────────┤   ││  │ UI 瞬态(轻量)│ SavedStateHandle   │ 当前步骤、选中ID、查询词    │   ││  │ 业务数据      │ Room Database      │ 商品列表、用户信息、订单     │   ││  │ 用户偏好      │ DataStore          │ 主题、语言、登录状态         │   ││  │ 后台任务      │ WorkManager        │ 文件上传、数据同步           │   ││  │ 网络缓存      │ Room + Flow        │ 文章内容、图片元数据         │   ││  └──────────────┴────────────────────┴──────────────────────────┘   ││                                                                     ││  架构原则:                                                            ││  1. 以 Room 数据库作为 Single Source of Truth                          ││  2. ViewModel + SavedStateHandle 处理 UI 状态                         ││  3. repeatOnLifecycle 安全收集 Flow                                    ││  4. WorkManager 保证后台任务可靠执行                                    ││  5. 不依赖 onDestroy 做任何重要操作                                     ││  6. 首次创建和恢复场景明确区分处理逻辑                                    │└─────────────────────────────────────────────────────────────────────┘