如何结合LiveData监听SharedPreferences变化并触发网络请求
嘿,这个需求其实挺贴合MVVM架构最佳实践的——用LiveData监听SharedPreferences变化,既能利用它的生命周期感知能力避免内存泄漏,又能优雅地触发网络请求更新数据。我给你梳理一套完整的实现步骤,附带代码示例:
首先咱们需要一个自定义的LiveData子类,专门用来监听SharedPreferences指定key的变化,把SP的变化转换成LiveData的数据流:
class SharedPreferenceLiveData<T>( private val sharedPreferences: SharedPreferences, private val key: String, private val defaultValue: T ) : LiveData<T>() { // SP变化监听器 private val preferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { _, changedKey -> if (changedKey == key) { value = getValueFromPreferences() } } override fun onActive() { super.onActive() // 当有观察者订阅时,立即发送当前值,并注册监听器 value = getValueFromPreferences() sharedPreferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener) } override fun onInactive() { super.onInactive() // 当没有观察者时,注销监听器,避免内存泄漏 sharedPreferences.unregisterOnSharedPreferenceChangeListener(preferenceChangeListener) } @Suppress("UNCHECKED_CAST") private fun getValueFromPreferences(): T { return when (defaultValue) { is Boolean -> sharedPreferences.getBoolean(key, defaultValue) as T is Float -> sharedPreferences.getFloat(key, defaultValue) as T is Int -> sharedPreferences.getInt(key, defaultValue) as T is Long -> sharedPreferences.getLong(key, defaultValue) as T is String -> sharedPreferences.getString(key, defaultValue) as T else -> throw IllegalArgumentException("Unsupported type") } } }
这个类会在LiveData活跃(有UI组件观察)时自动注册SP监听器,不活跃时自动注销,完美适配Android生命周期。
2. 用Repository整合SP监听与网络请求
接下来创建一个Repository类,负责把SP的变化和网络请求绑定起来。这里用Transformations.switchMap实现:当SP的LiveData值变化时,自动触发新的网络请求,并返回请求结果的LiveData。
先模拟你的网络请求接口(用Kotlin协程的挂起函数):
// 替换成你的实际网络请求逻辑 suspend fun fetchDataWithParams(param1: String, param2: Int): Result<DataModel> { return Result.success(DataModel()) }
然后是Repository的实现:
class DataRepository(private val sharedPreferences: SharedPreferences) { // 监听SP中param1的变化 private val param1LiveData = SharedPreferenceLiveData(sharedPreferences, "param1", "") // 监听SP中param2的变化 private val param2LiveData = SharedPreferenceLiveData(sharedPreferences, "param2", 0) // 合并参数变化,触发网络请求 fun getUpdatedData(): LiveData<Result<DataModel>> { return Transformations.switchMap(param1LiveData) { param1 -> Transformations.switchMap(param2LiveData) { param2 -> liveData(Dispatchers.IO) { emit(fetchDataWithParams(param1, param2)) } } } } }
如果你的参数更多,可以用MediatorLiveData来合并多个SP参数的变化,只要任意一个参数更新,就会触发新的网络请求。
3. ViewModel中持有Repository,暴露给UI层
ViewModel作为UI和数据层的中间层,直接持有Repository实例,把数据请求的LiveData暴露给Activity/Fragment:
class MainViewModel(application: Application) : AndroidViewModel(application) { private val sharedPreferences = application.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE) private val repository = DataRepository(sharedPreferences) // 暴露给UI观察的LiveData val dataLiveData = repository.getUpdatedData() }
4. 在Activity中观察LiveData,处理结果
最后在你的设置页面或主页面中,观察ViewModel的LiveData,一旦有新的网络请求结果就更新UI:
class MainActivity : AppCompatActivity() { private lateinit var viewModel: MainViewModel private val sharedPreferences by lazy { getSharedPreferences("MyPrefs", Context.MODE_PRIVATE) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel = ViewModelProvider(this)[MainViewModel::class.java] // 观察数据变化 viewModel.dataLiveData.observe(this) { result -> when (result) { is Result.Success -> { // 更新UI,展示新数据 updateUI(result.data) } is Result.Failure -> { // 处理错误 showError(result.exception) } } } } // 模拟设置页面保存参数的方法(比如点击按钮触发) fun saveSettings(param1: String, param2: Int) { sharedPreferences.edit() .putString("param1", param1) .putInt("param2", param2) .apply() } }
关键注意点
- 生命周期安全:自定义的
SharedPreferenceLiveData会在UI组件销毁时自动注销SP监听器,不会造成内存泄漏。 - 自动触发请求:只要SP中指定的参数变化,
switchMap就会自动触发新的网络请求,无需手动调用API。 - 线程切换:网络请求在
Dispatchers.IO线程执行,结果自动切换到主线程,符合Android线程规范。
内容的提问来源于stack exchange,提问作者nick.tdr
相关产品推荐
相关产品推荐

