上期我們聊到room蝙昙,本期就來簡單說一下room的用法杭棵。
常規(guī)room我們不聊怎么用了客年,跟著官方文檔一步一步使用即可谎僻。
老規(guī)矩,先上效果膛锭。
override fun testRoom() {
//常規(guī)flow監(jiān)聽
lifecycleScope.launchWhenResumed {
UserDB.getUserFlow("test1").collect {
Log.v("collect", "collect: $it")
}
}
//常規(guī)flow操作符監(jiān)聽
UserDB.getUserFlow("test1")
// .catch { }//錯誤捕獲
.onEach {
Log.v("launchIn", "launchIn: $it")
}
// .catch { } //onEach中的錯誤捕獲
.launchIn(lifecycleScope).start()
//轉(zhuǎn)化為liveData后的監(jiān)聽 private val userModelObs = UserDB.getUserFlow("test1").asLiveData()
//live_data_ktx 庫 將flow轉(zhuǎn)為livedata private val userModelObs = UserDB.getUserFlow("test1").asLiveData()
userModelObs.observe(this) {
Log.v("livedata", "livedata: $it")
}
//修改數(shù)據(jù)
lifecycleScope.launchWhenResumed {
UserDB.updateUserAsync(UserModel("test1", "test6", 5))
}
}
//打印結(jié)果
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)
我們看到每個結(jié)果打印了兩次粮坞,其中name由5變成了6蚊荣,其中5是原始值,6是最后修改數(shù)據(jù)使用的值莫杈,這里就是使用flow的好處了互例,修改數(shù)據(jù)庫直接能夠反饋到所有監(jiān)聽flow的地方,并且flow自帶生命周期筝闹,無需擔(dān)心內(nèi)存泄露問題媳叨。如此處理,也能讓本地數(shù)據(jù)杜絕EventBus等事件總線來回傳遞关顷,造成Event災(zāi)難糊秆。
下面我們一步一步來實現(xiàn)這個效果。
初始化room议双,這里我與官方處理的方式略有差異
根據(jù)我們的模塊化方案痘番,room初始化我們放置在:features:feature_common:common_room_db模塊中
@SuppressLint("StaticFieldLeak")
object RoomDB {
private lateinit var context: Context
//application初始化時調(diào)用,如果采用其他的單例方式需要每次傳入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" //數(shù)據(jù)庫名稱
)
.allowMainThreadQueries() //允許啟用同步查詢汞舱,即:允許主線程可以查詢數(shù)據(jù)庫,這個配置要視情況使用宗雇,一般不推薦同步查詢
.fallbackToDestructiveMigration()//如果數(shù)據(jù)庫升級失敗了昂芜,刪除重新創(chuàng)建
.enableMultiInstanceInvalidation()//多進程查詢支持
// .addMigrations(MIGRATION_1_2) //數(shù)據(jù)庫版本升級,MIGRATION_1_2為要執(zhí)行的表格執(zhí)行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]) //自定義數(shù)據(jù)處理轉(zhuǎn)換泌神,這里我們將list都轉(zhuǎn)為json存儲
abstract class AppDataBase : RoomDatabase() {
abstract fun userDao(): UserDao
}
//本類可以根據(jù)具體業(yè)務(wù)需求自行處理,這里隨便寫了個demo舞虱,沒有經(jīng)過測試欢际。
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)
}
}
接下來我們創(chuàng)建table,這里我們將數(shù)據(jù)庫模型與實際使用的模型完全隔離開砾嫉,并且使用擴展方法進行數(shù)據(jù)轉(zhuǎn)換處理幼苛,避免業(yè)務(wù)模型變更影響到數(shù)據(jù)變更窒篱,到時候維護起來比較麻煩焕刮。并且難以做數(shù)據(jù)庫升級。本文中所有entity結(jié)尾的類為數(shù)據(jù)庫模型墙杯,model結(jié)尾的類為業(yè)務(wù)模型配并。
根據(jù)我們的模塊化方案,其中Entity放置在:features:feature_common:common_room_db模塊中高镐,Model類及轉(zhuǎn)換類放置在data_xxxx模塊中溉旋,依賴關(guān)系為,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
)
//數(shù)據(jù)轉(zhuǎn)換
fun UserModel.toEntity(remoteName: String) =
UserEntity(uid = this.uid, remoteName = remoteName, name = this.name, gender = gender)
fun UserEntity.toUserModel() = UserModel(uid, name, gender)
然后我們創(chuàng)建查詢dao嫉髓,room基本用法观腊,不懂可以查看下上述的官網(wǎng)說明邑闲。
根據(jù)我們的模塊化方案,dao存儲在:features:feature_common:common_room_db模塊中
//這里注意,增刪改查都可以使用@Query操作符梧油,只需要在后邊寫上需要操作的語句即可
//例如 @Query("DELETE FROM UserEntity") 也可以正常執(zhí)行苫耸。
@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模塊中創(chuàng)建代理查詢類,并提供將業(yè)務(wù)模型轉(zhuǎn)為數(shù)據(jù)庫模型&數(shù)據(jù)庫模型轉(zhuǎn)為業(yè)務(wù)模型的代理儡陨,方便使用褪子。
代碼如下
//這里我列舉了 Async異步方式,Sync同步方式及flow方式進行數(shù)據(jù)的增刪改查骗村。
//sync方式需要創(chuàng)建room時調(diào)用allowMainThreadQueries()嫌褪,否則會報錯
//Async方式需要在協(xié)程中使用。
//flow需要協(xié)程的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()
}
}
}
如此笼痛,我們便達到了文章開頭的使用方式。