如何在Jetpack Compose中使用DataStore API实现计时器状态持久化
实现思路
我们使用Jetpack官方推荐的DataStore Preferences实现持久化存储,替代已废弃的SharedPreferences,核心逻辑是ViewModel初始化时读取已存储的时间值,每次时间更新时同步写入Preferences。
步骤1:添加依赖
在Module级别的build.gradle(或build.gradle.kts)中添加DataStore依赖,可替换为最新版本:
dependencies { implementation "androidx.datastore:datastore-preferences:1.0.0" }
步骤2:全局创建DataStore实例
通过Kotlin扩展函数定义全局唯一的DataStore实例,建议放在自定义Application类或顶层工具文件中:
import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.preferencesDataStore // 定义存储文件名称 val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "timer_config") // 定义时间存储的键值 val TIME_KEY = longPreferencesKey("saved_timer_time")
步骤3:改造TimeViewModel
改造后支持初始化读存储、时间变更自动写存储:
import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch import java.io.IOException class TimeViewModel(private val dataStore: DataStore<Preferences>) : ViewModel() { private val _time = MutableLiveData<Long>() val time: LiveData<Long> = _time init { // 初始化时读取已持久化的时间值 viewModelScope.launch { dataStore.data .catch { exception -> // 读取异常时返回空配置 if (exception is IOException) { emit(emptyPreferences()) } else { throw exception } } .collect { preferences -> val savedTime = preferences[TIME_KEY] ?: 0L _time.postValue(savedTime) } } } fun onTimeChange(newTime: Long) { _time.value = newTime // 时间更新时同步写入Preferences持久化 viewModelScope.launch { dataStore.edit { preferences -> preferences[TIME_KEY] = newTime } } } }
因为ViewModel添加了自定义参数,需要配套创建Factory来实例化:
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences class TimeViewModelFactory(private val dataStore: DataStore<Preferences>) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(TimeViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return TimeViewModel(dataStore) as T } throw IllegalArgumentException("Unknown ViewModel class") } }
在Activity/Fragment中实例化ViewModel的方式:
// 以Activity为例 val timeViewModel by viewModels<TimeViewModel> { TimeViewModelFactory(applicationContext.dataStore) }
步骤4:修正Composable逻辑
原有代码中LaunchedEffect放在了time变量声明之前,会触发编译错误,调整顺序即可:
import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import java.text.DecimalFormat @Composable fun Timer(timeViewModel:TimeViewModel = viewModel()){ val time: Long by timeViewModel.time.observeAsState(0L) LaunchedEffect(key1 = time ){ delay(1000L) timeViewModel.onTimeChange(time + 1) } val dec = DecimalFormat("00") val min = time / 60 val sec = time % 60 Text( modifier = Modifier.wrapContentSize(), text = "${dec.format(min)}:${dec.format(sec)}", style = MaterialTheme.typography.body1 ) }
内容的提问来源于stack exchange,提问作者Murugappan M
相关产品推荐
相关产品推荐

