吞噬
我们许多人可能知道,领域已经引入了freezing objects。就个人而言,我已经等待了很长时间的此类功能。那么,这解决了什么实际问题?
我们很多人可能面临这个问题:
Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
我相信这个错误是自言自语的。
领域<7.0:
当您调用Realm.getDefaultInstance()
时,您并非每次调用此方法时都创建一个新对象。但是,在每种方法上都有它是丑陋的。如果您需要另一个线程中的对象,则在没有DTO辅助数据类的情况下,不可能使用它。
让我们考虑以下情况:
private lateinit var realm: Realm
...
override var onCreate(){
...
realm = Realm.getDefaultInstance() // Main thread
}
suspend fun doSomething() = withContext(Dispatchers.IO){
val result = realm.where<MyRealmObject>().findFirst() //Crash
result?.let {
Timber.d("${it.id}")
}
}
每当我们必须启动领域的交互时,每次您都在新线程中时,我们都必须以前调用Realm.getDefaultInstance()
。否则,它将带来上面提到的错误。如果您想要一个sinlge,app-scope Realm
实例,则必须制作some workaround。
如果您要阅读RealmObject
或RealmResults<T>
:
也会发生同样的事情
private lateinit var realm: Realm
private var myObject: MyRealmObject? = null
...
override var onCreate(){
...
realm = Realm.getDefaultInstance() // Main thread
myObject = realm.where<MyRealmObject>().findFirst()
}
suspend fun doSomething() = withContext(Dispatchers.IO){
result?.let {
Timber.d("${it.id}") // Crash
}
}
领域> = 7.0:
由于这将是一个巨大的障碍,尤其是对于反应性编程而言,领域团队正在为游戏带来新的演员:冷冻领域。只是一个可以跨线程使用的新对象,该对象连接到唯一的实例化范围对象。
private lateinit var liveRealm: Realm
private lateinit var frozenRealm: Realm
override var onCreate(){
...
realm = Realm.getDefaultInstance() // Main thread
frozenRealm = realm.freeze()
}
suspend fun doSomething() = withContext(Dispatchers.IO){
val result = frozenRealm.where<MyRealmObject>().findFirst()
result?.let {
Timber.d("${it.id}") // Works
}
}
您可以执行上述解决方案,也可以直接冻结RealmResults<T>
或直接冻结对象(必须是RealmObject
)。这甚至更容易:
private lateinit var realm: Realm
private var myObject: RealmResults? = null
override var onCreate(){
...
realm = Realm.getDefaultInstance() // Main thread
myObject = realm.where<MyRealmObject>().findAll().freeze()
}
suspend fun doSomething() = withContext(Dispatchers.IO){
myObject?.let {
Timber.d("${it.id}") // Works
}
}
此新功能还有更多补充,但是文档已经完美。
这个示例真的需要Coroutines吗?
根本不是,但是Coroutines用withContext
块提供了线程开关的不错介绍。领域有自己的线程用法:
val result = realm.where<MyRealmObject>().findAllAsync()
findAllAsync()
消除了拥有其他类型的线程池的必要性。但是,如果您想更具反应性,将领域与Flow
(尤其是callbackFlow
)相结合将是一个不错的选择。让我们考虑以下情况:从数据库中选择一些数据,并且每次数据都会更改时,都会出现新的吐司。
当务之急:
// In a ViewModel
fun getData(){
val result = realm.where<MyRealmObject>().findAllAsync()
result.addChangeListener{ result ->
_toastLiveData.value = ShowToastFlag
}
}
作为一个例子,足以消除coroutines的使用。但是,让我们考虑以下情况:在本地获取对象列表,并在将其序列化为JSON后在网络上提出帖子请求。
suspend fun sendListToNetwork() = withContext(Dispatchers.IO){
val result = frozenRealm.where<MyRealmObject>().findAll()
// We are in another thread because of the Network Request and not because of Realm
result.addChangeListener{ mResult ->
val jsonStringResultObject = serialize(result.toList())
val response = request().postRequest(jsonStringResult).execute()
when(response){
is Success -> doSomething()
else -> doSomethingElse()
}
}
如果情况会更长,我们将有一个更难阅读的代码。
让我们尝试使它更具反应性
让我们为将选择作为列表选择的所有领域对象做一个通用方法:
inline fun observeData(): Flow> = callbackFlow{
val result = frozenRealm.where<T>().findAllAsync()
result.addChangeListener{ mResult ->
offer(result.toList())
}
awaitClose {result.removeAllChangeListeners()}
}
是时候观察这一新功能的魔力,结合了珊瑚:
// inside the ViewModel
suspend fun sendDataToNetwork(){
observeData().flowOn(Dispatchers.IO)
.map {
return@map serialize(it)
}.flowOn(Dispatchers.Default) // same Realm object, different threads
.map {
return@map postRequest(it).execute()
}.flowOn(Dispatchers.IO)
.collect { networkResult ->
handleResult(networkResult)
}
}
就是这样。
ondestroy
没有领域的新功能,这是不可能的。相反,我们将浪费DTO,以便参考我们可以获得的每个选择。允许跨线程共享对象是一个非常方便且必要的功能。节省时间和代码并不是正在触摸的唯一因素。此新功能也为反应编程开了一种新的方法。
Stavro Xhardha