赞
踩
相关文章:
Jetpack:Room超详细使用踩坑指南!
Jetpack:Room+kotlin协程? 事务问题分析,withTransaction API 详解.
Jetpack:Room使用报错FAQ
只需要Dao接口声明的方法返回类型需要用LiveData包装
//Dao
@Query("select * from $STUDENT_TABLE_NAME")
suspend fun obtainStudentAll(): LiveData<List<StudentEntity>>
//ViewModel
fun obtainStudentAllUseLiveData() = dao.obtainStudentAll()
使用:onCreate里面订阅一次对应的LiveData。之后同一个数据库对象,就可以做到,一旦更新之后就可以里面通知到对应的订阅者了。伪代码:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //liveData viewModel.obtainStudentAllUseLiveData() .observe(this@RoomDemoActivity, object : Observer<List<StudentEntity>> { override fun onChanged(t: List<StudentEntity>?) { //UI 显示 下面所有的增啥改查,导致表发生变化之后,这里会立即收到回调通知 displayToTextView(t!!) } }) } //数据库增删改查操作 btnInsert.setOnClickListener { lifecycleScope.launch { viewModel.insertStudent(StudentEntity(0, "zxf", "18")) } } btnDelete.setOnClickListener { lifecycleScope.launch { viewModel.deleteStudent(StudentEntity(2, "delete", "99")) } } btnUpdate.setOnClickListener { lifecycleScope.launch { viewModel.updateStudent(StudentEntity(199, "update", "99")) } }
只需要Dao接口声明的方法返回的类型为Flow
//flow
@Query("select * from $STUDENT_TABLE_NAME")
fun obtainStudentAll(): Flow<List<StudentEntity>>
//viewModel
fun obtainStudentAllUseFlow() = dao.obtainStudentAll()
使用:在onCreate里面collect一次对应的Flow,之后同一个数据库对象,就可以做到,一旦更新之后就可以里面通知flow的调用者了。伪代码如下:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //直接拉取显示 lifecycleScope.launch { //flow viewModel.obtainStudentAllUseFlow() .collect { //UI 显示 下面所有的增啥改查,导致表发生变化之后,这里会立即收到回调通知 displayToTextView(it) } } } //数据库增删改查操作 btnInsert.setOnClickListener { lifecycleScope.launch { viewModel.insertStudent(StudentEntity(0, "zxf", "18")) } } btnDelete.setOnClickListener { lifecycleScope.launch { viewModel.deleteStudent(StudentEntity(2, "delete", "99")) } } btnUpdate.setOnClickListener { lifecycleScope.launch { viewModel.updateStudent(StudentEntity(199, "update", "99")) } }
由于官方推荐kotlin,且在加上flow配合kotlin协程强大的性能。所以官方后期主推的就是flow,这里呢,就对room配合flow做一下原理解析:
看一下,生成Dao的实现类查询对应的源码:
public Flow<List<StudentEntity>> obtainStudentAll() {
final String _sql = "select * from student";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return CoroutinesRoom.createFlow(__db, false, new String[]{"student"}, new Callable<List<StudentEntity>>() {
@Override
public List<StudentEntity> call() throws Exception {
...
}
});
}
省略掉中间具体的查询的内容,看返回的具体是一个CoroutinesRoom.createFlow这个东西。那我们就具体找一下CoroutinesRoom.createFlow方法,最后在CoroutinesRoom.kt 里面找到了createFlow方法。createFlow完整源码如下:
public fun <R> createFlow( db: RoomDatabase, inTransaction: Boolean, tableNames: Array<String>, callable: Callable<R> ): Flow<@JvmSuppressWildcards R> = flow { // Observer channel receives signals from the invalidation tracker to emit queries. val observerChannel = Channel<Unit>(Channel.CONFLATED) val observer = object : InvalidationTracker.Observer(tableNames) { override fun onInvalidated(tables: MutableSet<String>) { observerChannel.offer(Unit) } } observerChannel.offer(Unit) // Initial signal to perform first query. val flowContext = coroutineContext val queryContext = coroutineContext[TransactionElement]?.transactionDispatcher ?: if (inTransaction) db.transactionDispatcher else db.queryDispatcher withContext(queryContext) { db.invalidationTracker.addObserver(observer) try { // Iterate until cancelled, transforming observer signals to query results to // be emitted to the flow. for (signal in observerChannel) { val result = callable.call() withContext(flowContext) { emit(result) } } } finally { db.invalidationTracker.removeObserver(observer) } } } }
大致先分析一下:使用channel,当channel并设置更新模式为CONFLATED(保留最新的值发送)。所以当每次往channel发送一个值得时候,下面得for循环就会迭代一次,调用一次emit,否则for循环挂起。
在分析一下细节,什么时候会给channel发送值呢?我们看到,第一次调用该方法以及onInvalidated回调。第一次调用就不解释了;onInvalidated回调是什么呢?这个东西其实就是一个回调,当当前得数据库对象得表出现了变动(增删改),此时就会回调上来。此时往channel里面发送值。所以在首次调用createFlow以及表发生变动得时候channel会发送值。for循环执行一次。
看看for循环执行干了什么吧。首先看一下执行for循环得操作被调度到了异步得线程,不了解coroutineContext[TransactionElement]得可以参考:Jetpack:Room+kotlin协程? 事务问题分析,withTransaction API 详解.当for循环执行得时候,调用了callable.call(),这个是上面省略的表具体查询内容可以获取表格查询result,然后把线程切换到上面外面调用者自定义得线程把result emit出去。
总结一下就是,当首次调用createFlow方法时,channel发送一次,然后执行一次查询,最后把结果emit出去。其他时候当表发生变动之后,执行一次查询,把结果emit发送出去。如何取消呢?只要把外层得协程取消掉就可以了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。