Reputation: 8203
I don't have a lot of experience writing Android apps. For fun I am writing an app that will upload my call logs to my server. This entire app is running as a Service. The Service (starts when the phone is booted) is the one that registers the ContentObserver, which then calls my custom CallLog
class. I use ContentObserver
to listen for content change events. Unfortunately, the ContentObserver is called multiple times when I e.g. dial a number.
For that reason, I have function that I call after a successful upload (I use Retrofit) called markAsUploaded()
. This function creates a RealmObject
called CallLogUploaded
(which is different from my regular CallLog
model). This CallLogUploaded
simply has one identifier which is the dateTime
of the call, which should be unique enough. Then, when I am iterating through the list of all the call logs, I check every single call log against a isDataUploaded()
function, which does a Realm query and checks to see if there is already a call log with that dateTime stored in the database (realm). In theory, it should work.
However, I have noticed that it does not always work. It seems that very often my data is stale. When I do realm.isAutoRefresh()
, it returns false (although I swear it returned true once). In my isDataUploaded
function, even when I do a findAll()
on the Realm, I do not see all of my data - but the data definitely did hit the markDataAsUploaded
function.
Here's my code - it's in Kotlin but should be easy to understand:
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
}
I am very new to Realm and fairly new to Android development, so I would appreciate any help you can give me. Thanks!
Upvotes: 1
Views: 553
Reputation: 81539
That's because
// 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 method has quite a few errors that I had written about a long time ago
The Realm instance is opened, but never closed
There is a new transaction per every element, rather than inserting all elements in a single transaction
If you don't close the Realm instance (each instance requires its own close()
call), then your Realm instance will never update unless you actually begin a transaction and do things inside the transaction.
You have three solutions:
1.) do your logic on the background thread inside a transaction, if there is nothing to be done, then cancel the transaction - queries made in transactions are never stale
2.) make sure the Realm instance is closed properly (although this is definitely a necessity on any non-autoupdating thread)
3.) a hacky solution is to call RealmRefresh.refreshRealm()
after getDefaultInstance()
according to my answer on Stack Overflow which relies on package-private API, but it works to solve this issue
Typically you need to open the Realm instance at the beginning of the thread, and close it at the end of the thread.
so, it's basically one big try(Realm realm = Realm.getDefaultInstance() { ... }
for onHandleIntent()
.
enqueue(new Callback() { @Override public void onSuccess(..) {...}
runs on the UI thread. To run it on the current thread, you should use call.execute()
.
instead of
for (data in dataToUpload) {
realm.beginTransaction()
val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
callLogUploaded.callDate = data.callDate
realm.commitTransaction()
}
do
realm.beginTransaction()
for (data in dataToUpload) {
val callLogUploaded = realm.createObject(CallLogUploaded::class.java)
callLogUploaded.callDate = data.callDate
}
realm.commitTransaction()
In order to understand about version retention you can read https://medium.com/@Zhuinden/understanding-realm-version-retention-and-synchronization-9a513c2445bb .
Upvotes: 1