2017-01-29 125 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