领域7,冷冻宝座
#database #android #realm



吞噬

我们许多人可能知道,领域已经引入了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

如果您要阅读RealmObjectRealmResults<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