上期咱们聊到room,本期就来简单说一下room的用法。
惯例room咱们不聊怎么用了,跟着官方文档一步一步运用即可。

传送门

老规矩,先上作用。

 override fun testRoom() {
        //惯例flow监听
        lifecycleScope.launchWhenResumed {
            UserDB.getUserFlow("test1").collect {
                Log.v("collect", "collect: $it")
            }
        }
        //惯例flow操作符监听
        UserDB.getUserFlow("test1")
//            .catch {  }//过错捕获
            .onEach {
                Log.v("launchIn", "launchIn: $it")
            }
//            .catch {  } //onEach中的过错捕获
            .launchIn(lifecycleScope).start()
        //转化为liveData后的监听 private val userModelObs = UserDB.getUserFlow("test1").asLiveData()
        //live_data_ktx 库  将flow转为livedata private val userModelObs = UserDB.getUserFlow("test1").asLiveData()
        userModelObs.observe(this) {
            Log.v("livedata", "livedata: $it")
        }
        //修正数据
        lifecycleScope.launchWhenResumed {
            UserDB.updateUserAsync(UserModel("test1", "test6", 5))
        }
    }
//打印成果
2023-08-28 15:43:44.691 23583-23583 launchIn                yz.l.fm                              V  launchIn: UserModel(uid=test1, name=test5, gender=5)
2023-08-28 15:43:44.692 23583-23583 launchIn                yz.l.fm                              V  launchIn: UserModel(uid=test1, name=test6, gender=5)
2023-08-28 15:43:44.692 23583-23583 livedata                yz.l.fm                              V  livedata: UserModel(uid=test1, name=test5, gender=5)
2023-08-28 15:43:44.692 23583-23583 livedata                yz.l.fm                              V  livedata: UserModel(uid=test1, name=test6, gender=5)
2023-08-28 15:43:44.692 23583-23583 collect                 yz.l.fm                              V  collect: UserModel(uid=test1, name=test5, gender=5)
2023-08-28 15:43:44.692 23583-23583 collect                 yz.l.fm                              V  collect: UserModel(uid=test1, name=test6, gender=5)

咱们看到每个成果打印了两次,其间name由5变成了6,其间5是原始值,6是最终修正数据运用的值,这儿就是运用flow的好处了,修正数据库直接能够反馈到所有监听flow的地方,而且flow自带生命周期,无需忧虑内存泄露问题。如此处理,也能让本地数据根绝EventBus等事情总线来回传递,形成Event灾祸。

下面咱们一步一步来实现这个作用。

初始化room,这儿我与官方处理的办法略有差异
依据咱们的模块化计划,room初始化咱们放置在:features:feature_common:common_room_db模块中

@SuppressLint("StaticFieldLeak")
object RoomDB {
    private lateinit var context: Context
    //application初始化时调用,假如采用其他的单例办法需求每次传入context,运用比较费事。
    fun init(context: Context) {
        this.context = context.applicationContext
    }
    val INSTANCE: AppDataBase by lazy { Holder.holder }
    private object Holder {
        val holder by lazy {
            Room.databaseBuilder(
                context.applicationContext,
                AppDataBase::class.java,
                "android_room_db.db" //数据库名称
            )
                .allowMainThreadQueries() //允许启用同步查询,即:允许主线程能够查询数据库,这个配置要视状况运用,一般不推荐同步查询
                .fallbackToDestructiveMigration()//假如数据库晋级失利了,删去从头创建
                .enableMultiInstanceInvalidation()//多进程查询支撑
//              .addMigrations(MIGRATION_1_2) //数据库版别晋级,MIGRATION_1_2为要履行的表格履行sql句子,例如database.execSQL("ALTER TABLE localCacheMusic ADD COLUMN time Int NOT NULL default 0 ;")
                .build()
        }
    }
}
@Database(
    entities = [UserEntity::class],
    version = 1, exportSchema = false
)
@TypeConverters(value = [LocalTypeConverter::class]) //自定义数据处理转化,这儿咱们将list都转为json存储
abstract class AppDataBase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
//本类能够依据详细事务需求自行处理,这儿随便写了个demo,没有通过测验。
open class LocalTypeConverter {
    @TypeConverter
    fun json2StrListEntity(src: String?): List<String>? =
        src.toObject()
    @TypeConverter
    fun strList2Json(data: List<String>?): String = gson.toJson(data ?: mutableListOf<String>())
    @TypeConverter
    fun date2Long(date: Date?): Long {
        return date?.time ?: System.currentTimeMillis()
    }
    @TypeConverter
    fun long2Date(time: Long): Date {
        return Date(time)
    }
}

接下来咱们创建table,这儿咱们将数据库模型与实际运用的模型彻底隔脱离,而且运用扩展办法进行数据转化处理,避免事务模型改变影响到数据改变,到时候维护起来比较费事。而且难以做数据库晋级。本文中所有entity结束的类为数据库模型,model结束的类为事务模型。

依据咱们的模块化计划,其间Entity放置在:features:feature_common:common_room_db模块中,Model类及转化类放置在data_xxxx模块中,依赖关系为,data_xxxxx implementation project(“:features:feature_common:common_room_db”)

@Entity(primaryKeys = ["uid", "remoteName"])
data class UserEntity(
    var uid: String,
    var remoteName: String = "",
    val name: String = "",
    val gender: Int = 1
)
data class UserModel(
    var uid: String = "",
    var name: String = "",
    var gender: Int = 1
)
//数据转化
fun UserModel.toEntity(remoteName: String) =
    UserEntity(uid = this.uid, remoteName = remoteName, name = this.name, gender = gender)
fun UserEntity.toUserModel() = UserModel(uid, name, gender)

然后咱们创建查询dao,room根本用法,不明白能够查看下上述的官网说明。

依据咱们的模块化计划,dao存储在:features:feature_common:common_room_db模块中

//这儿留意,增修改查都能够运用@Query操作符,只需求在后边写上需求操作的句子即可
//例如 @Query("DELETE FROM UserEntity") 也能够正常履行。
@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun saveAsync(userEntity: UserEntity)
    @Update
    suspend fun updateAsync(userEntity: UserEntity)
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun saveSync(userEntity: UserEntity)
    @Delete
    suspend fun deleteAsync(userEntity: UserEntity)
    @Query("SELECT * FROM UserEntity WHERE remoteName=:remoteName")
    suspend fun getUserListAsync(remoteName: String): List<UserEntity>
    @Query("SELECT * FROM UserEntity WHERE remoteName=:remoteName")
    fun getUserListFlow(remoteName: String): Flow<List<UserEntity>>
    @Query("SELECT * FROM UserEntity WHERE uid=:uid")
    suspend fun getUserAsync(uid: String): UserEntity?
    @Query("SELECT * FROM UserEntity WHERE uid=:uid")
    fun getUserSync(uid: String): UserEntity?
    @Query("SELECT * FROM UserEntity WHERE uid=:uid")
    fun getUserFlow(uid: String): Flow<UserEntity?>
}

然后咱们在data_xxxx模块中创建署理查询类,并提供将事务模型转为数据库模型&数据库模型转为事务模型的署理,方便运用。

代码如下

//这儿我列举了 Async异步办法,Sync同步办法及flow办法进行数据的增修改查。
//sync办法需求创建room时调用allowMainThreadQueries(),不然会报错
//Async办法需求在协程中运用。
//flow需求协程的scope支撑,尽量运用activity&fragment中的lifecycleScope来处理
object UserDB {
    private val dao: UserDao by lazy {
        RoomDB.INSTANCE.userDao()
    }
    suspend fun saveUserAsync(user: UserModel) {
        dao.saveAsync(user.toEntity("test"))
    }
    suspend fun updateUserAsync(user: UserModel) {
        dao.updateAsync(user.toEntity("test"))
    }
    fun saveUserSync(user: UserModel) {
        dao.saveSync(user.toEntity("test"))
    }
    suspend fun getUserAsync(uid: String): UserModel? {
        return dao.getUserAsync(uid)?.toUserModel()
    }
    fun getUserSync(uid: String): UserModel? {
        return dao.getUserSync(uid)?.toUserModel()
    }
    fun getUserFlow(uid: String): Flow<UserModel?> {
        return dao.getUserFlow(uid).map {
            it?.toUserModel()
        }
    }
}

如此,咱们便达到了文章最初的运用办法。

完整项目地址