You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Compose应用退出/崩溃后恢复最后页面的实现方案咨询

恢复Compose Navigation应用的最后浏览页面(替代SharedPreferences方案)

以下是几种适配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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 02:17:33