JetPack知识点实战系列九:Room数据库Migration

在APP的迭代过程中,会遇到给数据库表增加字段或者增加表的的需求,这时候就涉及到数据库的Migration。本节我们将来介绍一下RoomMigration

上节我们用数据库建了一张play_list_tags表实现了歌单标签的增删改查功能。

本节我们建一张net_cache表,用来缓存一些主要界面的网络数据内容,这样当网络异常时可以用数据库中的缓存内容填充界面,不至于整个空白一片,优化用户的使用体验。

新建 Entity

我们新建一个NetCache,代码如下:

@Entity(tableName = "net_cache")
data class NetCache (
    @PrimaryKey(autoGenerate = false)
    val type: Int,
    val content: String
)

NetCache 比较简单,表名为net_cache; 表包含两个字段,type表示网络请求数据的类型,content表示请求到的数据的JSON字符串。

@Dao
interface NetCacheDao {

    @Query("SELECT * FROM net_cache WHERE type = :type")
    suspend fun getNetCache(type: Int): NetCache

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertNetCache(cache: NetCache)

    @Delete
    suspend fun deleteNetCache(cache: NetCache)

    @Update(onConflict = OnConflictStrategy.REPLACE)
    suspend fun updateNetCache(cache: NetCache)

}

新建 Dao

接下来我们新建一个 NetCacheDao

@Dao
interface NetCacheDao {

    @Query("SELECT * FROM net_cache WHERE type = :type")
    suspend fun getNetCache(type: Int): NetCache

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertNetCache(cache: NetCache)

    @Delete
    suspend fun deleteNetCache(cache: NetCache)

    @Update(onConflict = OnConflictStrategy.REPLACE)
    suspend fun updateNetCache(cache: NetCache)

}

其他的方法比较简单,重点说一下getNetCache方法,我们看到方法接收一个type参数,然后这个参数替换@Query的SQL语句中SELECT * FROM net_cache WHERE type = :type的占位符:type.

修改 Database

我们接下来修改下MusicDatabase

@Database(version = 1, entities = [PlayListTagResponse.PlayListTag::class, NetCache::class], exportSchema = false)
abstract class MusicDatabase : RoomDatabase() {
    // ...
    abstract fun netCacheDao(): NetCacheDao
}
  1. @Database的注解中的entities元素中增加NetCache::class
  2. 添加一个netCacheDao方法,这个方法返回NetCacheDao

修改 HomeViewModel

发现页有两个请求,一个请求Banner,一个请求其他Block信息。

两个请求

修改的主要代码如下:

// 1
class HomeViewModel(application: Application) : AndroidViewModel(application) {
    // 2
    private val netRepository : NetCacheRepository

    init {
        val netCacheDao = MusicDatabase.getInstance(application).netCacheDao()
        netRepository = NetCacheRepository(netCacheDao)

        fetchData()
    }

    // 请求数据
    fun fetchData() {

        try {
            viewModelScope.launch(Dispatchers.IO) {

                val bannerJob = async { HomeRepository.getHomeBanner() }
                val blocksJob = async { HomeRepository.getHomeBlocks() }

                var bannerResponse = bannerJob.await()
                var blocksResponse = blocksJob.await()
                
                if (bannerResponse.code != NetErrorType.UnSpecified && blocksResponse.code != NetErrorType.UnSpecified) {
                    withContext(Dispatchers.Main) {
                        _homeLiveData.value = Pair(bannerResponse.banners, blocksResponse.data)
                    }
                    // 3
                    updateBannerCache(bannerResponse)
                    updateBlockCache(blocksResponse)
                    return@launch
                }

                // 4
                if (bannerResponse.code == NetErrorType.UnSpecified) {
                    val cache = getBannerCache()
                    cache?.let {
                        bannerResponse = it
                    }
                }
                if (blocksResponse.code == NetErrorType.UnSpecified) {
                    val cache = getBlockCache()
                    cache?.let {
                        blocksResponse = it
                    }
                }
                withContext(Dispatchers.Main) {
                    _homeLiveData.value = Pair(bannerResponse.banners, blocksResponse.data)
                }
            }
        } catch (e: Exception) {

        }

    }

    /* 存入Banner缓存 */
    private suspend fun updateBannerCache(banners: BannerResponse) {
        val jsonAdapter = Moshi.Builder().build().adapter(BannerResponse::class.java)
        val jsonStr = jsonAdapter.toJson(banners)
        netRepository.insertNetCache(NetCache(NetCacheType.HomeBanner, jsonStr))
    }

    /* 存入Blocks缓存 */
    private suspend fun updateBlockCache(blocks: HomeResponse) {
        val jsonAdapter = Moshi.Builder().build().adapter(HomeResponse::class.java)
        val jsonStr = jsonAdapter.toJson(blocks)
        netRepository.insertNetCache(NetCache(NetCacheType.Discovery, jsonStr))
    }

    /* 获取Banner缓存 */
    private suspend fun getBannerCache(): BannerResponse? {
        val cacheStr = netRepository.getNetCache(NetCacheType.HomeBanner) ?: return null
        val jsonAdapter = Moshi.Builder().build().adapter(BannerResponse::class.java)
        return jsonAdapter.fromJson(cacheStr.content)
    }

    /* 获取Block缓存 */
    private suspend fun getBlockCache(): HomeResponse? {
        val cacheStr = netRepository.getNetCache(NetCacheType.Discovery) ?: return null
        val jsonAdapter = Moshi.Builder().build().adapter(HomeResponse::class.java)
        return jsonAdapter.fromJson(cacheStr.content)
    }


}

主要修改有:

  1. 为了获取到ApplicationViewModel改为AndroidViewModel
  2. 定义一个NetCacheRepository属性来操作数据库
  3. 获取到网络数据后存入数据库中,见updateBannerCacheupdateBlockCache方法
  4. 如果没有获取到网络数据,就从数据库中获取。见getBannerCachegetBlockCache方法

到目前为止,代码已经修改完毕,运行程序,程序报错了。

报错内容为Room cannot vertify the data integrity. Looks like you've changed schema but forgot to update the version number.

错误

这是错误是因为我们添加了表,需要进行Migration

Migration

  • MusicDatabase的Version修改为2
@Database(version = 2, entities = [PlayListTagResponse.PlayListTag::class, NetCache::class], exportSchema = false)
  • 新建一个Migration
private class Migration1To2: Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("CREATE TABLE IF NOT EXISTS net_cache" +
            "('type' INTEGER NOT NULL PRIMARY KEY, 'content' TEXT NOT NULL)")
    }
}
  1. 新建一个Migration1To2,继承自Migration,构造函数的1,2代表从版本1前移到版本2.
  2. migrate方法中执行建表操作,CREATE TABLE IF NOT EXISTS net_cache ('type' INTEGER NOT NULL PRIMARY KEY, 'content' TEXT NOT NULL)
  • 初始化MusicDatabase的时候调用addMigrations方法
val instance = Room.databaseBuilder(application, MusicDatabase::class.java, "music_database")
                    .addCallback(MusicDatabaseCallBack())
                    .addMigrations(Migration1To2())
                    .build()

运行程序,现在一切正常了。通过Migration我们就实现了页面网络请求的数据库缓存。

网络请求缓存

猜你喜欢

转载自blog.csdn.net/lcl130/article/details/108903985