Kotlin中如何实现仅首次启动时向Room数据库保存数据?
解决方案
核心问题分析
- 重复保存的根源:启动页ViewModel的
init块会在每次ViewModel实例化时自动执行insertExchanges(),而每次打开应用都会创建新的ViewModel实例,导致重复插入数据。 - SharedPreferences逻辑错误:原
checkDate函数先写入当前时间再读取,导致sharedDate永远等于当前时间,isDayPassed条件永远不成立;且逻辑方向错误,你需要的是标记是否完成过首次初始化,而非判断日期差。
1. 修正SharedPreferences检查逻辑
替换原checkDate函数,改为标记首次启动状态,同时支持可选的每日更新逻辑:
fun checkFirstLaunch(context: Context, viewModel: SplashScreenViewModel) { val sharedPreferences = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE) val isFirstLaunch = sharedPreferences.getBoolean("is_first_launch", true) if (isFirstLaunch) { // 首次启动执行初始插入 viewModel.insertExchanges() // 标记为已完成首次启动 sharedPreferences.edit().putBoolean("is_first_launch", false).apply() // 记录首次更新时间,用于后续每日更新判断 sharedPreferences.edit().putLong("last_update_time", System.currentTimeMillis()).apply() } else { // 非首次启动,判断是否需要每日更新 val lastUpdateTime = sharedPreferences.getLong("last_update_time", 0) val isNeedDailyUpdate = System.currentTimeMillis() - lastUpdateTime >= TimeUnit.DAYS.toMillis(1) if (isNeedDailyUpdate) { viewModel.update() sharedPreferences.edit().putLong("last_update_time", System.currentTimeMillis()).apply() } } }
2. 修改ViewModel,移除自动执行的插入逻辑
删掉ViewModel中的init块,让插入操作由外部检查逻辑触发:
@HiltViewModel class SplashScreenViewModel @Inject constructor( private val insertExchangeUseCase: InsertExchangeUseCase, private val updateExchangeUseCase: UpdateExchangeUseCase, private val getAllExchangeUseCase: GetAllExchangeUseCase ) : ViewModel() { // 改为公开方法,由外部触发初始插入 fun insertExchanges() { viewModelScope.launch { insertExchangeUseCase.insert(DEFAULT_CURRENCY) } } fun update() { viewModelScope.launch { updateExchangeUseCase.updateExchange(getAllExchangeUseCase.get(DEFAULT_CURRENCY)) } } }
3. 更新启动页UI调用逻辑
在SplashScreen中替换原checkDate调用为修正后的函数:
@Composable fun SplashScreen( navController: NavController, viewModel : SplashScreenViewModel = hiltViewModel() ) { val context: Context = LocalContext.current checkFirstLaunch(context, viewModel) // 原有动画与导航逻辑保持不变 val scale = remember { Animatable(0f) } LaunchedEffect(key1 = true) { scale.animateTo( targetValue = 0.3f, animationSpec = tween( durationMillis = 500, easing = { OvershootInterpolator(2f).getInterpolation(it) } ) ) delay(2000L) navController.navigate(Screen.ExchangeMainScreen.route) } Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { Image( painter = painterResource(id = R.drawable.ic_currency_exchange_logo_512), contentDescription = "Logo" ) } }
内容的提问来源于stack exchange,提问作者NewPartizal
相关产品推荐
相关产品推荐

