Jetpack Compose中如何正确暴露SharedPreferences值为Flow并监听变更?
1. 封装SharedPreferences操作类
创建一个统一的工具类,处理SharedPreferences的读写,并通过Flow监听值变更,替代轮询方案:
import android.content.Context import android.content.SharedPreferences import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow class AppPreferences private constructor(context: Context) { private val prefs: SharedPreferences = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE) private val KEY_DENSITY_FACTOR = "density_factor" // 暴露可监听密度因子变化的Flow val densityFactorFlow: Flow<Float> = callbackFlow { // 发送初始值 trySend(getDensityFactor()) // 注册SharedPreferences变更监听 val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> if (key == KEY_DENSITY_FACTOR) { trySend(getDensityFactor()) } } prefs.registerOnSharedPreferenceChangeListener(listener) // Flow取消时自动注销监听,避免内存泄漏 awaitClose { prefs.unregisterOnSharedPreferenceChangeListener(listener) } } // 获取当前密度因子 fun getDensityFactor(): Float { return prefs.getFloat(KEY_DENSITY_FACTOR, 1.0f) // 默认缩放比例1.0 } // 设置密度因子 fun setDensityFactor(factor: Float) { prefs.edit().putFloat(KEY_DENSITY_FACTOR, factor).apply() } // 单例实现,确保全局唯一实例 companion object { @Volatile private var INSTANCE: AppPreferences? = null fun getInstance(context: Context): AppPreferences { return INSTANCE ?: synchronized(this) { val instance = AppPreferences(context.applicationContext) INSTANCE = instance instance } } } }
2. 在ViewModel中统一使用封装类
所有ViewModel都通过这个封装类操作密度因子,避免逻辑分散:
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.stateIn class MainViewModel(context: Context) : ViewModel() { private val appPreferences = AppPreferences.getInstance(context) // 转换为StateFlow,方便Compose安全观察 val densityFactorState: StateFlow<Float> = appPreferences.densityFactorFlow .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5000), // 组件后台5秒后停止收集 initialValue = appPreferences.getDensityFactor() ) // 对外暴露修改密度因子的方法 fun updateDensityFactor(factor: Float) { appPreferences.setDensityFactor(factor) } }
如果使用Hilt依赖注入,可以更优雅地传入AppPreferences:
// Hilt模块配置 @Module @InstallIn(SingletonComponent::class) object PreferencesModule { @Provides fun provideAppPreferences(@ApplicationContext context: Context): AppPreferences { return AppPreferences.getInstance(context) } } // ViewModel注入示例 @HiltViewModel class MainViewModel @Inject constructor( private val appPreferences: AppPreferences ) : ViewModel() { // 同上的stateFlow和update方法 }
3. 在Compose中应用缩放逻辑
观察StateFlow的变化,动态调整UI缩放:
@Composable fun ScaledContent(viewModel: MainViewModel = viewModel()) { val densityFactor by viewModel.densityFactorState.collectAsState() Box( modifier = Modifier .fillMaxSize() .scale(densityFactor) // 整体缩放布局 ) { Text( text = "缩放演示文本", fontSize = 16.sp * densityFactor // 单独缩放字体 ) Button( onClick = { viewModel.updateDensityFactor(densityFactor + 0.1f) }, modifier = Modifier.align(Alignment.BottomCenter) ) { Text("放大") } Button( onClick = { viewModel.updateDensityFactor(densityFactor - 0.1f) }, modifier = Modifier.align(Alignment.BottomStart) ) { Text("缩小") } } }
核心优势
- 替代轮询:仅在SharedPreferences值真正变更时发送新数据,避免无效资源消耗
- 逻辑统一:所有SharedPreferences操作集中在
AppPreferences,多个ViewModel无需重复实现读写逻辑 - 生命周期安全:通过
stateIn配置自动停止收集的规则,符合Android组件生命周期最佳实践
内容的提问来源于stack exchange,提问作者toshiomagic
相关产品推荐
相关产品推荐

