Android数据缓存框架 - 缓存和加载流程控制

现在我们可以正式开始写缓存的流程了。我们要缓存的数据有列表和非列表两种形式,所以我们分两条路线处理缓存流程。首先定义一个注解区分这两种流程。

```
package dora.cache.repository

import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy

/**
 * 每一个具体的Repository类必须配置这个注解,来标记是否为List类型的数据。
 */
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
@Retention(RetentionPolicy.RUNTIME)
annotation class Repository(

        /**
         * 是否是List类型的数据,如果不为List类型的数据,请修改为false。
         *
         * @return
         */
        val isListMode: Boolean = true,

        /**
         * 是否打印调试日志。
         */
        val isLogPrint: Boolean = false)
```

然后我们要定义请求网络数据的接口IDataFetcher。

```
package dora.cache.data.fetcher

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

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

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

    /**
     * 抓取数据的回调。
     */
    fun callback(listener: OnLoadListener? = null): DoraCallback<M>

    /**
     * 开始抓取数据。
     */
    fun fetchData(listener: OnLoadListener? = object : OnLoadListener {
        override fun onSuccess() {
        }

        override fun onFailure(msg: String) {
        }
    }): LiveData<M?>

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

    interface OnLoadListener {
        fun onSuccess()
        fun onFailure(msg: String)
    }
}
```

下面是list模式的DataFetcher。

```
package dora.cache.data.fetcher

import androidx.lifecycle.LiveData
import dora.cache.data.page.IDataPager
import dora.http.DoraListCallback

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

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

    /**
     * 抓取数据的回调。
     */
    fun listCallback(listener: OnLoadListener? = null): DoraListCallback<M>

    /**
     * 开始抓取数据。
     */
    fun fetchListData(listener: OnLoadListener? = object : OnLoadListener {
        override fun onSuccess() {
        }
        override fun onFailure(msg: String) {
        }
    }): LiveData<MutableList<M>>

    /**
     * 获取livedata。
     */
    fun getListLiveData() : LiveData<MutableList<M>>

    /**
     * 获取livedata的分页器。
     */
    fun obtainPager(): IDataPager<M>

    interface OnLoadListener {
        fun onSuccess()
        fun onFailure(msg: String)
    }
}
```

在实际使用过程中,我们希望使用者直接调用fetchData或fetchListData就可以开始请求网络,并自动缓存了。这里出现了一个IDataPager,这个用于将缓存到livedata中的数据进行分页后再显示在UI上,属扩展功能,非主要流程,先不讲。

我们通过定义一个BaseRepository抽象缓存和加载的整体流程。

```
package dora.cache.repository

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import dora.cache.data.fetcher.IDataFetcher
import dora.cache.data.fetcher.IListDataFetcher
import dora.cache.data.page.IDataPager
import dora.cache.holder.CacheHolder
import dora.http.DoraCallback
import dora.http.DoraListCallback
import io.reactivex.Observable
import java.lang.reflect.ParameterizedType

/**
 * 数据仓库,缓存和加载流程处理基类。一个[BaseRepository]要么用于非集合数据,要么用于集合数据。如果要用于
 * 非集合数据,请在实现类配置[Repository]注解将[.isListMode]的值设置为false,默认为集合模式。注意,
 * 无论是集合模式还是非集合模式,Repository注解都是必须的。
 */
abstract class BaseRepository<M>(val context: Context) : ViewModel(), IDataFetcher<M>, IListDataFetcher<M> {

    /**
     * 非集合数据获取接口。
     */
    protected lateinit var dataFetcher: IDataFetcher<M>

    /**
     * 集合数据获取接口。
     */
    protected lateinit var listDataFetcher: IListDataFetcher<M>

    /**
     * 非集合数据缓存接口。
     */
    protected lateinit var cacheHolder: CacheHolder<M>

    /**
     * 集合数据缓存接口。
     */
    protected lateinit var listCacheHolder: CacheHolder<MutableList<M>>

    /**
     * true代表用于集合数据,false用于非集合数据。
     */
    protected var isListMode = true
        protected set

    protected var isLogPrint = false
        protected set

    /**
     * 是否在网络加载数据失败的时候清空数据。
     *
     * @return
     */
    protected val isClearDataOnNetworkError: Boolean
        protected get() = false

    protected abstract fun createDataFetcher(): IDataFetcher<M>

    protected abstract fun createListDataFetcher(): IListDataFetcher<M>

    protected abstract fun createCacheHolder(clazz: Class<M>): CacheHolder<M>

    protected abstract fun createListCacheHolder(clazz: Class<M>): CacheHolder<MutableList<M>>

    /**
     * 手动放入缓存数据,仅listMode为true时使用,注意只会追加到缓存里面去,请调用接口将新数据也更新到服务端,以致
     * 于下次请求api接口时也会有这部分数据。
     */
    fun addData(data: M) {
        if (isListMode) {
            addData(arrayListOf(data))
        }
    }

    /**
     * 手动放入一堆缓存数据,仅listMode为true时使用,注意只会追加到缓存里面去,请调用接口将新数据也更新到服务端,
     * 以致于下次请求api接口时也会有这部分数据。
     */
    fun addData(data: MutableList<M>) {
        if (isListMode) {
            getListLiveData().value?.let {
                it.addAll(data)
                listCacheHolder.addNewCache(data)
            }
        }
    }

    /**
     * 保证成员属性不为空,而成功调用数据库查询方法,提高查询可靠性。比如用来校验属性,a != null && b != null
     * && c != null。
     *
     * @see BaseDatabaseCacheRepository.query
     */
    protected open fun checkValuesNotNull() : Boolean { return true }

    @JvmOverloads
    override fun callback(listener: IDataFetcher.OnLoadListener?): DoraCallback<M> {
        return object : DoraCallback<M>() {
            override fun onSuccess(model: M) {
                if (isLogPrint) {
                    model.let {
                        Log.d(TAG, it.toString())
                    }
                }
                listener?.onSuccess()
            }

            override fun onFailure(msg: String) {
                if (isLogPrint) {
                    Log.d(TAG, msg)
                }
                listener?.onFailure(msg)
            }
        }
    }

    @JvmOverloads
    override fun listCallback(listener: IListDataFetcher.OnLoadListener?): DoraListCallback<M> {
        return object : DoraListCallback<M>() {
            override fun onSuccess(models: MutableList<M>) {
                if (isLogPrint) {
                    models.let {
                        for (model in it) {
                            Log.d(TAG, model.toString())
                        }
                    }
                }
                listener?.onSuccess()
            }

            override fun onFailure(msg: String) {
                if (isLogPrint) {
                    Log.d(TAG, msg)
                }
                listener?.onFailure(msg)
            }
        }
    }

    /**
     * 非集合数据的API接口调用,Retrofit接口的方法返回retrofit.Call类型使用。
     *
     * @param callback
     */
    protected abstract fun onLoadFromNetwork(callback: DoraCallback<M>)

    /**
     * 集合数据的API接口调用,Retrofit接口的方法返回retrofit.Call类型使用。
     *
     * @param callback
     */
    protected abstract fun onLoadFromNetwork(callback: DoraListCallback<M>)

    /**
     * 非集合数据的API接口调用,Retrofit接口的方法返回io.reactivex.Observable类型使用。
     */
    protected abstract fun onLoadFromNetworkObservable() : Observable<M>

    /**
     * 集合数据的API接口调用,Retrofit接口的方法返回io.reactivex.Observable类型使用。
     */
    protected abstract fun onLoadFromNetworkObservableList() : Observable<MutableList<M>>

    /**
     * 从仓库选择数据,处理数据来源的优先级。
     *
     * @param ds 数据的来源
     * @return 数据是否获取成功
     */
    protected abstract fun selectData(ds: DataSource): Boolean

    /**
     * 数据的来源。
     */
    interface DataSource {

        enum class Type {
            /**
             * 数据来源于网络服务器。
             */
            NETWORK,

            /**
             * 数据来源于缓存。
             */
            CACHE
        }

        enum class CacheType {

            /**
             * 内置SQLite数据库。
             */
            DATABASE,

            /**
             * 自定义的数据仓库。
             */
            CUSTOM
        }

        /**
         * 从缓存中加载数据。
         *
         * @param type
         * @return
         */
        fun loadFromCache(type: CacheType): Boolean

        /**
         * 从服务器/网络加载数据。
         */
        fun loadFromNetwork()
    }

    /**
     * 抓取非集合数据,返回给livedata,以便于展示在UI上。抓取成功后会一直在livedata中,可以通过[.getLiveData()]
     * 拿到。
     */
    @JvmOverloads
    override fun fetchData(listener: IDataFetcher.OnLoadListener?): LiveData<M?> {
        return dataFetcher.fetchData(listener)
    }

    /**
     * 抓取集合数据,返回给livedata,以便于展示在UI上。抓取成功后会一直在livedata中,可以通过[.getListLiveData()]
     * 拿到。
     */
    @JvmOverloads
    override fun fetchListData(listener: IListDataFetcher.OnLoadListener?): LiveData<MutableList<M>> {
        return listDataFetcher.fetchListData(listener)
    }

    /**
     * @see IDataFetcher
     */
    override fun clearData() {
        dataFetcher.clearData()
    }

    /**
     * @see IListDataFetcher
     */
    override fun clearListData() {
        listDataFetcher.clearListData()
    }

    /**
     * @see fetchData
     */
    override fun getLiveData(): LiveData<M?> {
        return dataFetcher.getLiveData()
    }

    /**
     * @see fetchListData
     */
    override fun getListLiveData(): LiveData<MutableList<M>> {
        return listDataFetcher.getListLiveData()
    }

    /**
     * @see IListDataFetcher
     */
    override fun obtainPager(): IDataPager<M> {
        return listDataFetcher.obtainPager()
    }

    /**
     * 检测网络是否可用,非是否打开网络开关,而是是否能够收发数据包。
     *
     * @return
     */
    protected val isNetworkAvailable: Boolean
        protected get() = checkNetwork(context)

    private fun checkNetwork(context: Context): Boolean {
        val networkInfo = getActiveNetworkInfo(context)
        return networkInfo != null && networkInfo.isConnected
    }

    private fun getActiveNetworkInfo(context: Context): NetworkInfo? {
        val connectivityManager = context
                .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
        return connectivityManager.activeNetworkInfo
    }

    private fun getGenericType(obj: Any): Class<*>? {
        return if (obj.javaClass.genericSuperclass is ParameterizedType &&
                (obj.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.isNotEmpty()) {
            (obj.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<*>
        } else (obj.javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<*>
    }

    companion object {
        const val TAG = "dcache"
    }

    init {
        val repository = javaClass.getAnnotation(Repository::class.java)
                ?: throw RuntimeException("@Repository is required.")
        isListMode = repository.isListMode
        isLogPrint = repository.isLogPrint
        val MClass: Class<M> = getGenericType(this) as Class<M>
        Log.d(TAG, "MClass:$MClass,isListMode:$isListMode")
        // 二选一实现CacheHolder和DataFetcher并使用
        if (isListMode) {
            listCacheHolder = createListCacheHolder(MClass)
            listCacheHolder.init()
            listDataFetcher = createListDataFetcher()
        } else {
            cacheHolder = createCacheHolder(MClass)
            cacheHolder.init()
            dataFetcher = createDataFetcher()
        }
    }

    /**
     * 拦截网络请求和缓存加载出来的数据,并做一些修改,非集合模式使用。
     */
    protected open fun onInterceptData(type: DataSource.Type, model: M) {}

    /**
     * 拦截网络请求和缓存加载出来的数据,并做一些修改,集合模式使用。
     */
    protected open fun onInterceptData(type: DataSource.Type, models: MutableList<M>) {}
}
```

我们知道,使用者必须要在BaseRepository的最终实现类配置一个Repository注解,而这个注解中有个关键的属性,isListMode,它决定了这个数据仓库专门用来处理非集合数据还是集合数据的缓存加载流程。一个

数据仓库要么是非集合模式要么是集合模式,且不能修改它的模式。如果为非集合模式,就走非集合模式的流程,反之,则走集合模式的流程。也就是说,在一个数据仓库中,**CacheHolder**和**ListCacheHolder**不会同时被使用到。同理的还有,**IDataFetcher**与**IListDataFetcher**、**onInterceptData**(type: DataSource.Type, model: M)与**onInterceptData**(type: DataSource.Type, models: MutableList<M>)、**fetchData**与**fetchListData**、**getLiveData**与**getListLiveData**、**clearData**与**clearListData**。特别要注意的是**onLoadFromNetwork**(callback: DoraCallback<M>)、**onLoadFromNetwork**(callback: DoraListCallback<M>)、**onLoadFromNetworkObservable**() : Observable<M>、**onLoadFromNetworkObservableList**() : Observable<MutableList<M>>为四选一。首先确认与这个数据仓库对应的api接口是返回的*retrofit.Call还是io.reactivex.Observable类型,再确认使用非集合模式的还是集合模式的。*

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dora丶Android

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值