web-dev-qa-db-fra.com

Obtenir l'élément par identifiant dans la chambre

J'utilise Room + LiveData dans mon Android. Suite à Google Blueprints, j'ai implémenté la couche de données de mon application.

Voici à quoi ressemble mon Dao:

@Query("SELECT * FROM events WHERE id=:arg0")
    fun loadSingle(id: String): LiveData<Event>

Je l'appelle depuis mon EventRepository:

fun loadSingle(eventId: String): LiveData<RequestReader<Event>> {
        return object: NetworkManager<Event, Event>(appExecutors!!) {

            override fun loadLocal(): LiveData<Event> {
                val item = eventLocal!!.loadSingle("Title 1")
                Crashlytics.log(Log.VERBOSE, TAG, "loadFromServer::loadLocal=$item")
                return item
            }

            override fun isUpdateForced(data: Event?): Boolean {
                Crashlytics.log(Log.VERBOSE, TAG, "loadFromServer::isUpdateForced")
                return data == null || requestTimeout.isAllowed(UNDEFINED_KEY.toString())
            }

            override fun makeRequest(): LiveData<ApiResponse<Event>> {
                Crashlytics.log(Log.VERBOSE, TAG, "loadFromServer::makeRequest")
                return Database.createService(EventRemote::class.Java).load(eventId)
            }

            override fun onSuccess(item: Event) {
                eventLocal?.save(item)
            }

            override fun onFail() {
                Crashlytics.log(Log.VERBOSE, TAG, "loadFromServer::onFail")
                requestTimeout.reset(UNDEFINED_KEY.toString())
            }

        }.liveData
    }

Où se trouve la classe NetworkManager (a été "prise" de ici):

    abstract class NetworkManager<ResultType, RequestType> @MainThread constructor(val appExecutors: AppExecutors) {

        companion object {
            private val TAG = "TAG_NETWORK_MANAGER"
        }

        val liveData: MediatorLiveData<RequestReader<ResultType>> = MediatorLiveData()

        init {
            liveData.value = RequestReader.loading(null)
            val localSource: LiveData<ResultType> = loadLocal()
            Log.d(TAG, "before add::localSource=${localSource.value}")
            liveData.addSource(localSource, { data ->
                Log.d(TAG, "data=$data")
                liveData.removeSource(localSource)
                if (isUpdateForced(data)) {
                    loadRemote(localSource)
                } else {
                    liveData.addSource(localSource, { reusedData -> liveData.value = RequestReader.success(reusedData)})
                }
            })
        }

        private fun loadRemote(localSource: LiveData<ResultType>) {
            val remoteSource = makeRequest()
            liveData.addSource(localSource, {
                liveData.value = RequestReader.success(it)
            })
            liveData.addSource(remoteSource) { response ->
                liveData.removeSource(localSource)
                liveData.removeSource(remoteSource)
                if (response!!.isSuccessful) {
                    appExecutors.diskIO.execute {
                        onSuccess(processResponse(response))
                        appExecutors.mainThread.execute {
                            liveData.addSource(localSource, {
                                liveData.value = RequestReader.success(it)
                            })
                        }
                    }
                } else {
                    onFail()
                    liveData.addSource(localSource, {
                        liveData.value = RequestReader.error("Error: ${response.errorMessage}", it)
                    })
                }
            }

        }

        @MainThread
        protected abstract fun loadLocal(): LiveData<ResultType>

        @MainThread
        protected abstract fun isUpdateForced(data: ResultType?): Boolean

        @MainThread
        protected abstract fun makeRequest(): LiveData<ApiResponse<RequestType>>

        @WorkerThread
        protected abstract fun onSuccess(item: RequestType)

        @MainThread
        protected abstract fun onFail()

        @WorkerThread
        protected fun processResponse(response: ApiResponse<RequestType>): RequestType {
        return response.body!!
    }
}

Et après je m'attends à obtenir mes LiveData dans ViewModel:

open class EventSingleViewModel: ViewModel(), RepositoryComponent.Injectable {

    companion object {
        private val TAG = "TAG_EVENT_SINGLE_VIEW_MODEL"
    }

    @Inject lateinit var eventRepository: EventRepository

    var eventSingle: LiveData<RequestReader<Event>>? = null

    override fun inject(repositoryComponent: RepositoryComponent) {
        repositoryComponent.inject(this)
        eventSingle = MutableLiveData<RequestReader<Event>>()
    }

    fun load(eventId: String) {
        Crashlytics.log(Log.VERBOSE, TAG, "starts to loadList::eventId=$eventId")
        eventSingle = eventRepository.loadSingle(eventId)
    }

}

Le problème. Je reçois une liste d'événements de la même manière (ça marche!) J'ai décrit ci-dessus, mais avec un seul événement (ce l'événement est déjà dans la base de données) cela ne fonctionne pas. J'ai découvert que localSource.value est null (dans NetworkManager ). Peut-être que ma requête est mauvaise ou .. autre chose.

8
P. Savrov

Vérifiez à nouveau votre implémentation DAO, l'argument doit être le même dans les deux, le paramètre de fonction et l'argument d'annotation.

Change ça:

@Query("SELECT * FROM events WHERE id=:arg0")
fun loadSingle(id: String): LiveData<Event>

À:

@Query("SELECT * FROM events WHERE id=:id ")
fun loadSingle(id: String): LiveData<Event>
15
Fredy Mederos