【问题标题】:Realm returning stale data领域返回陈旧数据
【发布时间】:2017-01-29 19:11:44
【问题描述】:

我没有太多编写 Android 应用程序的经验。为了好玩,我正在编写一个应用程序,它将我的通话记录上传到我的服务器。整个应用程序作为服务运行。服务(在手机启动时启动)是注册 ContentObserver 的服务,然后调用我的自定义 CallLog 类。我使用ContentObserver 来监听内容更改事件。不幸的是,ContentObserver 被多次调用,例如拨打号码。

因此,我有一个在成功上传(我使用 Retrofit)后调用的函数,称为 markAsUploaded()。这个函数创建了一个名为CallLogUploadedRealmObject(这与我的常规CallLog 模型不同)。这个CallLogUploaded 仅具有一个标识符,即调用的dateTime,它应该足够唯一。然后,当我遍历所有调用日志的列表时,我会对照 isDataUploaded() 函数检查每个调用日志,该函数执行 Realm 查询并检查是否已经存在存储在数据库(领域)。从理论上讲,它应该可以工作。

但是,我注意到它并不总是有效。似乎我的数据经常是陈旧的。当我执行realm.isAutoRefresh() 时,它返回 false(尽管我发誓它返回了一次 true)。在我的 isDataUploaded 函数中,即使我在 Realm 上执行 findAll(),我也看不到我的所有数据 - 但数据确实命中了 markDataAsUploaded 函数。

这是我的代码 - 它在 Kotlin 中,但应该很容易理解:

    val callLogCall = service.sendCalLLogs(childId, dataToUpload)
    callLogCall.enqueue(object : Callback<Void> {
        override fun onResponse(call: Call<Void>, response: Response<Void>) {
            if (response.isSuccessful) {
                Log.i(AppConstants.LOG_TAG, "Call log data uploaded successfully!")
                this@CallLogData.markDataAsUploaded(dataToUpload)
            } else {
                Log.w(AppConstants.LOG_TAG, "Call log data upload failed")
            }
        }

        override fun onFailure(call: Call<Void>, t: Throwable) {
            Log.w(AppConstants.LOG_TAG, "Call log data upload error (onFailure) called")
        }
    })


// This function simply stores a Realm model for all the data that has been uploaded to the server
private fun markDataAsUploaded(dataToUpload: List<CallLog>) {
    realm = Realm.getDefaultInstance()
    for (data in dataToUpload) {
        realm.beginTransaction()
        val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
        callLogUploaded.callDate = data.callDate

        realm.commitTransaction()
    }
}

// This function checks to see if the data is already uploaded.
private fun isDataUploaded(callLog: CallLog) : Boolean { 
    return realm
            .where(CallLogUploaded::class.java)
            .equalTo("callDate", callLog.callDate)
            .count() > 0L
}

// Gets the call logs - not the entire function
for (call in callLogs) {
    val callLog = CallLog()
    callLog.id = call.id
    callLog.callDate = Utilities.getTimestampAsSeconds(call.callDate)

    if (this.isDataUploaded(callLog)) {
        continue
    }

    callLog.name = call.name
    callLog.number = call.number
 }

我对 Realm 很陌生,对 Android 开发也很陌生,所以如果你能给我任何帮助,我将不胜感激。谢谢!

【问题讨论】:

    标签: java android database realm kotlin


    【解决方案1】:

    那是因为

    // This function simply stores a Realm model for all the data that has been uploaded to the server
    private fun markDataAsUploaded(dataToUpload: List<CallLog>) {
        realm = Realm.getDefaultInstance()
        for (data in dataToUpload) {
            realm.beginTransaction()
            val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
            callLogUploaded.callDate = data.callDate
    
            realm.commitTransaction()
        }
    }
    

    这个方法有quite a few errors that I had written about a long time ago

    • Realm 实例已打开,但从未关闭

    • 每个元素都有一个新事务,而不是在单个事务中插入所有元素

    如果您不关闭 Realm 实例(每个实例都需要自己的 close() 调用),那么您的 Realm 实例将永远不会更新,除非您真正开始事务并在事务中执行操作。

    你有三个解决方案:

    1.) 在事务内的后台线程上执行逻辑,如果没有什么可做的,则取消事务 - 在事务中进行的查询永远不会过时

    2.) 确保 Realm 实例正确关闭(尽管这绝对是任何非自动更新线程的必要条件)

    3.) 一个 hacky 解决方案是致电 RealmRefresh.refreshRealm() after getDefaultInstance() according to my answer on Stack Overflow which relies on package-private API, but it works to solve this issue


    通常您需要在线程开始时打开 Realm 实例,并在线程结束时关闭它。

    所以,对于onHandleIntent(),它基本上是一个大的try(Realm realm = Realm.getDefaultInstance() { ... }


    enqueue(new Callback() { @Override public void onSuccess(..) {...} 在 UI 线程上运行。要在当前线程上运行它,你应该使用call.execute()


    而不是

    for (data in dataToUpload) {
        realm.beginTransaction()
        val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
        callLogUploaded.callDate = data.callDate
    
        realm.commitTransaction()
    }
    

    realm.beginTransaction()
    for (data in dataToUpload) {
        val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
        callLogUploaded.callDate = data.callDate
    }
    realm.commitTransaction()
    

    要了解版本保留,您可以阅读https://medium.com/@Zhuinden/understanding-realm-version-retention-and-synchronization-9a513c2445bb

    【讨论】:

      猜你喜欢
      • 2019-01-24
      • 1970-01-01
      • 2020-06-23
      • 2015-04-07
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 2023-03-04
      • 2019-06-12
      相关资源
      最近更新 更多