Android数据缓存框架 - 内存数据载体从LiveData到StateFlow

引言:所有成功者的背后,都有一份艰苦的历程,不要只看到了人前的风光,而低估了他们背后所付出的努力。

随着flow到流行度越来越高,有开发者呼吁我使用flow,于是我就如你们所愿,新增了StateFlow作为新的数据载体。当然你仍然可以使用旧版本的LiveData,代码写法略微不同罢了。如果对我的dcache框架设计不是很理解的小伙伴,可以看我的专栏其他文章。

为什么推荐使用StateFlow

如果你非要问我为什么要使用StateFlow?我可以告诉你,因为可以装逼,哈哈,开个玩笑。新技术的流行必然有一部分炒作的部分,但也肯定是有其改进的地方的。要讲StateFlow,就不得不从flow开始说起。flow是属于kotlin语言范畴的,你可以把它当成kotlin协程的一个API。没错,kotlin语言的野心就是要做跨平台的语言,答案就在这里,LiveData是android的API,而SharedFlow与StateFlow直接就是Kotlin编程语言级别的,代码复用性更好。

LiveData和StateFlow使用对比

以列表数据模式的Repository为例。从2.1.5开始@Repository注解拆分成了@Repository注解和@ListRepository,所以2.1.4版本你应该使用@Repository注解,而如果说,你使用的是2.1.5及以上版本的dcache库,要使用@ListRepository注解。由于StateFlow在2.2.0版本才开始支持,所以自然要使用@ListRepository注解。

先看StateFlow的写法。

kotlin 复制代码
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // 此处省略代码若干行
    lifecycleScope.launchWhenCreated {
        repository.getListFlowData().collect {
            adapter.setTemperatures(it)
        }
    }
}

不要忘了使用协程作用域。

然后我们调用fetchListData()。

kotlin 复制代码
repository.fetchListData(listener = object : OnLoadStateListener {
    override fun onLoad(state: Int) {
        Log.d("WeatherActivity", "数据是否加载成功:${state==0}")
    }
}, description = null)

加载状态监听接口和描述信息可以传null。这个抓取数据方法一经调用,collect代码块就会刷新数据。由于fetchListData()天然就返回的StateFlow,所以你并不一定要分为两步观察数据。而如果你要分为两步,则调用getListFlowData()或getFlowData()。

再看原来LiveData的写法,这次我们不用list模式的Repository,如果要使用,直接配置@Repository注解。

kotlin 复制代码
minutelyRepository.latlng = "116.407526,39.90403"
minutelyRepository.fetchData("按分钟统计天气").observe(this, Observer {
    it?.apply {
        tvCacheMinutely.text = "minutely:${toString()}\n"
    }
})

很明显,fetchData()返回LiveData,直接调用observe()进行数据的观察。简单总结下,API的设计在调用层面具有相似性,所以,无论你使用的是LiveData为数据载体的Repository还是StateFlow的,都是调用fetchData()或fetchListData()更新缓存数据,框架内部自动帮你缓存到数据库,同时常驻在内存并递送给UI层刷新界面。所以你可以专心开发你的业务逻辑,这是不是很棒?

kotlin 复制代码
package com.example.dcache.repository

import android.content.Context
import com.example.dcache.biz.weather.WeatherService
import com.example.dcache.model.WeatherModel
import dora.cache.data.fetcher.OnLoadStateListener
import dora.cache.repository.DoraDatabaseCacheRepository
import dora.cache.repository.Repository
import dora.http.DoraCallback
import dora.http.retrofit.RetrofitManager

@Repository
class WeatherRepository(context: Context) : DoraDatabaseCacheRepository<WeatherModel>(context) {

    var latlng: String = ""

    override fun onLoadFromNetwork(
        callback: DoraCallback<WeatherModel>,
        listener: OnLoadStateListener?
    ) {
        RetrofitManager.getService(WeatherService::class.java).getWeather(latlng).enqueue(callback)
    }
}

最后简单复习一下Repository的写法。详细Demo代码https://github.com/dora4/DoraCacheSample

框架设计的变化

这是StateFlow的。

kotlin 复制代码
/**
 * 用于网络数据抓取。
 */
interface IFlowDataFetcher<M> {

    /**
     * 清空flow data的数据。
     */
    fun clearData()

    /**
     * 抓取数据的回调。
     */
    fun callback(): DoraCallback<M>

    /**
     * 开始抓取数据。
     */
    fun fetchData(description: String?, listener: OnLoadStateListener? = OnLoadStateListenerImpl()): StateFlow<M?>

    /**
     * 获取flow data。
     */
    fun getFlowData(): StateFlow<M?>
}

这是LiveData的。

kotlin 复制代码
package dora.cache.data.fetcher

import androidx.lifecycle.LiveData
import dora.http.DoraCallback

/**
 * 用于网络数据抓取。
 */
interface IDataFetcher<M> {

    /**
     * 清空livedata的数据。
     */
    fun clearData()

    /**
     * 抓取数据的回调。
     */
    fun callback(): DoraCallback<M>

    /**
     * 开始抓取数据。
     */
    fun fetchData(description: String?, listener: OnLoadStateListener? = OnLoadStateListenerImpl()): LiveData<M?>

    /**
     * 获取livedata。
     */
    fun getLiveData(): LiveData<M?>
}

是不是没啥变化?对的,这就是架构设计的魅力所在。前期架构设计比较到位,所以只需要遵循开闭原则。对扩展开放,对修改关闭。

原先继承BaseRepository的,现在继承BaseFlowRepository的。名字有带Flow单词的,就是StateFlow的。

开源框架支持

笔者写框架和文档不容易,希望你的支持。你的支持是我改进优化最大的动力!

数据缓存dcache框架 https://github.com/dora4/dcache-android

dora框架的开发插件 https://github.com/dora4/dora-studio-plugin

dora框架 https://github.com/dora4/dora

相关推荐
阿巴斯甜21 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker21 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android