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

Jetpack Compose中如何正确暴露SharedPreferences值为Flow并监听变更?

实现可监听的SharedPreferences封装类 + Jetpack Compose显示缩放

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 08:48:21