Android Compose应用退出/崩溃后恢复最后页面的实现方案咨询
以下是几种适配Compose+Navigation场景的实现方案,避开SharedPreferences:
1. 使用Jetpack DataStore
DataStore是Google官方推荐的SharedPreferences替代方案,基于Coroutine和Flow实现,支持异步操作与类型安全,完美适配Compose生命周期,适合存储路由这类小型结构化数据。
实现步骤:
- 创建DataStore实例,负责路由的持久化读写:
class RouteDataStore(private val context: Context) { private val Context.dataStore by preferencesDataStore(name = "last_route") private val LAST_ROUTE_KEY = stringPreferencesKey("last_route") suspend fun saveLastRoute(route: String) { context.dataStore.edit { preferences -> preferences[LAST_ROUTE_KEY] = route } } fun getLastRoute(): Flow<String?> { return context.dataStore.data .map { preferences -> preferences[LAST_ROUTE_KEY] } } }
- 在NavHost中监听目的地变化,实时保存当前路由:
val navController = rememberNavController() val routeDataStore = remember { RouteDataStore(context) } LaunchedEffect(navController) { navController.currentBackStackEntryFlow.collect { backStackEntry -> routeDataStore.saveLastRoute(backStackEntry.destination.route ?: "") } } NavHost(navController = navController, startDestination = "home") { composable("home") { HomeScreen() } composable("profile") { ProfileScreen() } composable("settings") { SettingsScreen() } // 其他目的地定义 }
- 应用启动时读取路由并恢复页面:
val lastRoute by routeDataStore.getLastRoute().collectAsState(initial = null) LaunchedEffect(lastRoute) { lastRoute?.takeIf { it.isNotEmpty() }?.let { navController.navigate(it) { popUpTo(navController.graph.startDestinationId) { inclusive = true } } } }
2. 使用ViewModel + SavedStateHandle
SavedStateHandle是ViewModel的扩展能力,能在进程被系统销毁(崩溃、内存回收等场景)时自动保存状态,无需手动处理持久化逻辑,适合与ViewModel绑定的页面状态恢复需求。
实现步骤:
- 创建带SavedStateHandle的ViewModel,管理路由状态:
class NavRestoreViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { private val LAST_ROUTE_KEY = "last_route" val lastRoute = savedStateHandle.getLiveData<String>(LAST_ROUTE_KEY) fun updateLastRoute(route: String) { savedStateHandle.set(LAST_ROUTE_KEY, route) } }
- 在Compose中监听导航变化,更新ViewModel中的路由:
val viewModel: NavRestoreViewModel = viewModel() val navController = rememberNavController() LaunchedEffect(navController) { navController.currentBackStackEntryFlow.collect { backStackEntry -> viewModel.updateLastRoute(backStackEntry.destination.route ?: "") } } NavHost(navController = navController, startDestination = "home") { // 导航目的地定义 }
- 应用启动时从ViewModel读取路由并恢复:
val lastRoute by viewModel.lastRoute.observeAsState() LaunchedEffect(lastRoute) { lastRoute?.takeIf { it.isNotEmpty() }?.let { navController.navigate(it) { popUpTo(navController.graph.startDestinationId) { inclusive = true } } } }
3. 使用Room数据库(适合需保存页面参数的场景)
如果需要恢复的不仅是路由路径,还有页面携带的参数(如商品ID、用户ID),Room数据库可以存储结构化的导航历史记录,支持更复杂的状态恢复需求。
实现步骤:
- 定义导航记录实体类与Dao:
@Entity(tableName = "navigation_history") data class NavHistory( @PrimaryKey(autoGenerate = true) val id: Int = 0, val route: String, val arguments: String?, // 用JSON序列化存储参数 val timestamp: Long = System.currentTimeMillis() ) @Dao interface NavHistoryDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertOrUpdateHistory(history: NavHistory) @Query("SELECT * FROM navigation_history ORDER BY timestamp DESC LIMIT 1") suspend fun getLastNavHistory(): NavHistory? }
- 导航时插入/更新历史记录:
val navController = rememberNavController() val db = remember { AppDatabase.getInstance(context) } LaunchedEffect(navController) { navController.currentBackStackEntryFlow.collect { backStackEntry -> val arguments = backStackEntry.arguments?.let { args -> Json.encodeToString(args.keySet().associateWith { args.get(it) }) } db.navHistoryDao().insertOrUpdateHistory( NavHistory(route = backStackEntry.destination.route ?: "", arguments = arguments) ) } }
- 启动时查询最后一条记录,解析参数并导航:
LaunchedEffect(Unit) { val lastHistory = db.navHistoryDao().getLastNavHistory() lastHistory?.let { history -> val navOptions = NavOptions.Builder() .setPopUpTo(navController.graph.startDestinationId, inclusive = true) .build() history.arguments?.let { argsJson -> val argsMap = Json.decodeFromString<Map<String, Any?>>(argsJson) val navArgs = bundleOf(*argsMap.map { it.key to it.value }.toTypedArray()) navController.navigate(history.route, navArgs, navOptions) } ?: run { navController.navigate(history.route, navOptions) } } }
内容的提问来源于stack exchange,提问作者Daviti Gogochishvili
相关产品推荐
相关产品推荐

