web-dev-qa-db-fra.com

Bibliothèque persistante Android - Les appels DAO sont asynchrones. Par conséquent, comment obtenir un rappel?

D'après ce que j'ai lu Room ne vous permet pas d'émettre des requêtes de base de données sur le thread principal (car cela peut entraîner des retards sur le thread principal)). donc imaginez que je suis en train de mettre à jour un textview sur le fil principal de l’interface utilisateur sur laquelle des données, comment pourrais-je recevoir un rappel. Permettez-moi de vous montrer un exemple. Imaginez que je souhaite stocker les données de mon modèle métier dans un objet appelé Evénements. Nous aurions donc un objet EventDao:

imaginons que nous ayons cet objet DAO ci-dessous:

@Dao
public interface EventDao {

   @Query("SELECT * FROM " + Event.TABLE_NAME + " WHERE " + Event.DATE_FIELD + " > :minDate" limit 1)
   LiveData<List<Event>> getEvent(LocalDateTime minDate);

   @Insert(onConflict = REPLACE)
   void addEvent(Event event);

   @Delete
   void deleteEvent(Event event);

   @Update(onConflict = REPLACE)
   void updateEvent(Event event);

}

et maintenant, dans certaines activités, j'ai un textview et je voudrais mettre à jour sa valeur, donc je le fais:

 myTextView.setText(EventDao.getEvent(someDate));/*i think this is illegal as im trying to call room dao on mainthread, therefore how is this done correctly ? would i need to show a spinner while it updates ?*/

depuis la récupération est en cours hors du fil principal, je ne pense pas que je peux l'appeler comme ça et attendre une mise à jour en douceur. Quelle est la meilleure approche ici? 

Quelques informations supplémentaires: je voulais utiliser la base de données de la salle comme mécanisme permettant de récupérer les informations du modèle au lieu de les conserver statiquement en mémoire. le modèle serait donc disponible pour moi localement par le biais de la base de données après le téléchargement via un service de repos. 

UPDATE: donc depuis que je retourne un liveata, je peux le faire:

eventDao = eventDatabase.eventDao();
eventDao.getEvent().observe(this, event -> {
     myTextView.setText(event.get(0));
});

et cela fonctionne pour quelque chose de très petit. mais imaginez que ma base de données contient un million d'éléments. alors quand je fais cet appel, il y aura un délai pour récupérer les données. La toute première fois que cet appel est appelé, il sera visible à l'utilisateur qu'il y a un délai. Comment éviter cela? Donc, pour être clair, il y a des fois où je ne veux pas de données en direct, je dois juste mettre à jour une fois la vue. J'ai besoin de savoir comment faire ça? même si ce n'est pas avec liveData. 

11
j2emanue

Si vous souhaitez effectuer votre requête de manière synchrone et ne pas recevoir de notifications de mises à jour sur l'ensemble de données, ne renvoyez pas simplement la valeur renvoyée à un objet LiveData. Découvrez l'exemple de code fourni par Google.

Jetez un coup d'œil à loadProductSync () ici

8
Bohsen

Il existe un moyen de désactiver le mode asynchrone et d'autoriser l'accès synchrone. 

lors de la construction de la base de données, vous pouvez utiliser: allowMainThreadQueries ()

et pour une utilisation en mémoire: Room.inMemoryDatabaseBuilder ()

Bien que ce ne soit pas recommandé. Donc, au final, je peux utiliser une base de données en mémoire et un accès au fil principal si je voulais un accès super rapide. Je suppose que cela dépend de la taille de mes données et que, dans ce cas, il est très petit. 

mais si vous voulez utiliser un rappel ... en utilisant rxJava, voici celui que j'ai créé pour une liste de pays que je voulais stocker dans une base de données:

public Observable<CountryModel> queryCountryInfoFor(final String isoCode) {
    return Observable.fromCallable(new Callable<CountryModel>() {
        @Override
        public CountryModel call() throws Exception {
            return db.countriesDao().getCountry(isoCode);
        }
    }).subscribeOn(Schedulers.io())

 .observeOn(AndroidSchedulers.mainThread());

}

vous pouvez ensuite facilement ajouter un abonné à cette fonction pour obtenir le rappel avec Rxjava. 

6
j2emanue

As Bohsen a suggéré l'utilisation de l'état liveata pour une requête synchrone. Mais dans certains cas particuliers, nous souhaitons effectuer des opérations asynchrones basées sur la logique. Dans l'exemple ci-dessous, je dois extraire des commentaires enfants pour les commentaires parents. Il est déjà disponible dans la base de données, mais doit être extrait en fonction de son parent_id in recyclerview. Pour ce faire, j'ai utilisé le concept de retour AsyncTask pour obtenir le résultat. (Retour à Kotlin)

Classe de dépôt

fun getChildDiscussions(parentId: Int): List<DiscussionEntity>? {
        return GetChildDiscussionAsyncTask(discussionDao).execute(parentId).get()
    }

private class GetChildDiscussionAsyncTask constructor(private val discussionDao: DiscussionDao?): AsyncTask<Int, Void, List<DiscussionEntity>?>() {
        override fun doInBackground(vararg params: Int?): List<DiscussionEntity>? {
            return discussionDao?.getChildDiscussionList(params[0]!!)
        }
    }

Classe Dao

@Query("SELECT * FROM discussion_table WHERE parent_id = :parentId")
fun getChildDiscussionList(parentId: Int): List<DiscussionEntity>?
0
Naveen Kumar M