2017-01-29 4 views
1

У меня нет большого опыта написания приложений для Android. Для удовольствия я пишу приложение, которое будет загружать мои журналы вызовов на мой сервер. Все это приложение работает как служба. Служба (запускается при загрузке телефона) является той, которая регистрирует ContentObserver, который затем вызывает мой пользовательский класс CallLog. Я использую ContentObserver для прослушивания событий изменения контента. К сожалению, ContentObserver вызывается несколько раз, когда я, например. наберите номер.Realm возвращает устаревшие данные

По этой причине у меня есть функция, которую я вызываю после успешной загрузки (я использую Retrofit) под названием markAsUploaded(). Эта функция создает RealmObject под названием CallLogUploaded (что отличается от моей обычной модели CallLog). Этот CallLogUploaded просто имеет один идентификатор, который является dateTime вызова, который должен быть достаточно уникальным. Затем, когда я повторяю список всех журналов вызовов, я проверяю каждый журнал вызовов на функцию isDataUploaded(), которая выполняет запрос Realm, и проверяет, существует ли уже журнал вызовов с этой датой, хранящейся в базе данных (область). Теоретически он должен работать.

Однако, я заметил, что это не всегда работает. Похоже, что очень часто мои данные устарели. Когда я делаю realm.isAutoRefresh(), он возвращает false (хотя я клянусь, что он вернул true однажды). В моей функции isDataUploaded, даже когда я делаю findAll() на Realm, я не вижу всех своих данных, но данные определенно попали в функцию 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 
} 

Я очень новой для Realm и достаточно новое для разработки 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, но никогда не закрывался

  • Там есть новая транзакция по каждому элементу, а не вставлять все элементы в одной транзакции

Если вы не закрываете экземпляр 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 в начале резьбы, и закрыть его в конце нити.

так, это в основном один большой try(Realm realm = Realm.getDefaultInstance() { ... } для onHandleIntent().


enqueue(new Callback() { @Override public void onSuccess(..) {...} работает на потоке пользовательского интерфейса. Чтобы запустить его в текущем потоке, вы должны использовать 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.

Смежные вопросы