2017-01-29 82 views
1

我没有很多编写Android应用程序的经验。为了好玩,我正在编写一个应用程序,将我的通话记录上传到我的服务器。这整个应用程序作为服务运行。服务(在电话启动时启动)是注册ContentObserver的服务,然后调用我的自定义类CallLog。我使用ContentObserver来收听内容更改事件。不幸的是,当我例如将ContentObserver多次调用时。拨打一个号码。返回陈旧数据的领域

由于这个原因,我有一个功能,我称为markAsUploaded()成功上传(我使用Retrofit)后调用。此功能创建一个名为CallLogUploadedRealmObject(与我的常规CallLog型号不同)。这个CallLogUploaded只有一个标识符,该标识符是呼叫的dateTime,它应该足够独特。然后,当我遍历所有通话记录的列表时,我检查每个呼叫日志对照isDataUploaded()函数,该函数执行Realm查询并检查是否已经存在具有存储在数据库中的dateTime的通话记录(领域)。理论上,它应该工作。

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

这里是我的代码 - 它在科特林但应该是很容易理解:

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!") 
       [email protected](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 
} 

我很新的领域和相当新的Android开发,所以我希望得到任何帮助,你可以给我。谢谢!

回答

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实例(每个实例都需要调用它自己的close()),那么您的Realm实例将永远不会更新,除非您真的开始一个事务并在事务内部执行任何操作。

你有三种解决方法:

1)做你的后台线程逻辑在事务内,如果有什么工作要做,然后取消交易 - 交易进行的查询是不腐

2.)确保Realm实例关闭正确(虽然这在任何非自动化线程上都是必需的)

3。)哈克解决方法是调用RealmRefresh.refreshRealm() after getDefaultInstance() according to my answer on Stack Overflow which relies on package-private API, but it works to solve this issue


通常需要在螺纹开始打开领域实例,并在线程结束其关闭。

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


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