web-dev-qa-db-fra.com

ValueEventListener vs ChildEventListener pour RecyclerView dans Android

Les utilisateurs de la base de données Firebase savent qu'il existe deux écouteurs de base pour écouter les données: ValueEventListener et ChildEventListener. Cela fonctionne très bien lorsque nous écoutons un objet, mais devient assez difficile lorsque nous écoutons une collection.

Pour spécifier la question, imaginons que nous avons un flux HackerNews, et nous écoutons par exemple "publie" l'objet dans Firebase.

Bien sûr, nous avons RecyclerView dans notre application pour afficher les messages et je pense que la bonne idée serait d'utiliser FirebaseUI, mais le problème est que nous voulons faire une application plus abstraite en cas de changement du côté serveur ou des tests. Nous utiliserions donc certains adaptateurs, mais ceci est un autre question .

Nous avons deux auditeurs comme je l'ai mentionné, la question est quel est le meilleur ?

Lorsque nous utilisons ValueEventListener, nous obtiendrons toute la collection, mais en cas de changement, comme un utilisateur a changé le contenu de la publication, nous devrons recharger des données entières, ce qui signifie plus d'octets envoyés via un transfert réseau coûteux. Un autre problème est lorsque nous utilisons des multicistres, et voici un exemple:

Post a userId, mais nous voulons afficher son nom, donc dans la méthode onDataChanged nous avons récupérer les données utilisateur, comme ceci:

postsReference.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot data : dataSnapshot.getChildren()) {
            Post post = data.getValue(Post.class);   
            usersReference.child(post.getUserId()).addListenerForSingleValueEvent(new ValueEventListener() {

                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    // Here we have user data
                }

                @Override
                public void onCancelled(FirebaseError firebaseError) {

                }
            });
        }
    }

    @Override
    public void onCancelled(FirebaseError firebaseError) {
    }
});

Vous pouvez voir que maintenant nous devons ajouter chaque publication à RecyclerView séparément, ce qui nous donnerait une indication que nous devrions peut-être utiliser ChildEventListener.

Alors maintenant, lorsque nous utilisons `` ChildEventListener, le problème est le même - nous devons ajouter chaque publication à RecyclerView séparément, mais lorsque quelqu'un modifie le contenu de la publication, Firebase nous envoie uniquement cette publication, ce qui signifie moins de données via le réseau .

Nous n'aimons pas ajouter un article séparément à RecyclerView, car par exemple: - Il est difficile d'ajouter un indicateur de chargement, car nous ne savons pas quand toutes les données arrivent. - Les utilisateurs obtiennent une vue constamment rafraîchissante, tandis que de nouveaux messages arrivent au lieu de listes entières deviennent visibles. - Il est difficile de trier cette collection, nous devrons peut-être le faire dans l'adaptateur.

Question

Quelles sont les meilleures pratiques pour utiliser Firebase avec la collection, et peut-être de meilleures solutions que celles que j'ai écrites ci-dessus?

[~ # ~] modifier [~ # ~]

Le schéma de données ressemblerait à ceci:

"posts" : {
    "123456" : {
        "createdAt" : 1478696885622,
        "content" : "This is post content",
        "title" : "This is post title",
        "userId" : "abc"
    },
    "789012" : {
        "createdAt" : 1478696885622,
        "content" : "This is post content 2",
        "title" : "This is post title 2",
        "userId" : "efg"
    }
}
"users" : {
    "abc" : {
        "name" : "username1"
    },
    "efg" : {
        "name" : "username2"
    }
}

EDIT 2

J'ai fait une erreur -> Firebase ne récupère pas des données entières dans ValueEventListener quand quelque chose a changé. Il n'obtient que "delta", ici est la preuve.

15
Nominalista

Il y a plusieurs préoccupations dans cette question (à savoir la performance, l'indicateur de progrès, le traitement de nouvelles données comme leur tri). Naturellement, vous devriez trouver une solution qui tienne compte de la priorité de vos besoins. IMO ValueEventListener et ChildEventListener ont leurs cas d'utilisation:

  1. ChildEventListener est généralement la méthode recommandée pour synchroniser les listes d'objets. Ceci est même mentionné dans la documentation sur l'utilisation des listes :

    Lorsque vous travaillez avec des listes, votre application doit écouter les événements enfants plutôt que les événements de valeur utilisés pour les objets uniques.

    Cela est dû au fait que votre client reçoit uniquement l'enfant modifié avec la mise à jour spécifique (ajout ou suppression), par opposition à la liste entière à chaque mise à jour. Par conséquent, il permet un niveau de gestion plus granulaire des mises à jour de la liste.

  2. ValueEventListener peut être plus utile lorsque vous devez traiter la liste entière lors de la modification d'un enfant. C'est le cas lorsque vous devez trier la liste dans votre RecyclerView. Il est beaucoup plus facile de le faire en obtenant la liste complète, en la triant et en actualisant l'ensemble de données de la vue. En revanche, en utilisant un ChildEventListener, il est plus difficile de faire le tri car vous n'avez accès qu'à un enfant spécifique dans la liste par événement de mise à jour.

D'un point de vue de performance , étant donné que même ValueEventListener est conscient du "delta" lors de la synchronisation de la mise à jour, je voudrais ont tendance à le considérer comme moins efficace uniquement du côté client, car le client doit effectuer le traitement sur la liste entière, mais c'est encore mieux que d'avoir l'inefficacité côté réseau .

En ce qui concerne les indicateurs de progression et de rafraîchissement constants , la chose la plus importante à noter est que la base de données Firebase est une base de données en temps réel, par conséquent, un flux constant de données en temps réel est une caractéristique inhérente. Si des événements de mise à jour ne sont pas nécessaires, vous pouvez simplement utiliser la méthode addListenerForSingleValueEvent pour lire les données une seule fois. Vous pouvez également l'utiliser si vous souhaitez afficher un indicateur de progression lors du chargement du premier instantané:

// show progress indicator
postsReference.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // load initial data set
        // hide progress indicator when done loading
    }

    ...
});
19
manouti

J'ai eu le même problème exact (ValueEventListener vs ChildEventListener pour RecyclerView sous Android).

La solution que j'ai utilisée était de combiner les deux de cette façon:

Supposons que dbref pointe vers un emplacement de base de données Firebase où se trouvent mes messages.

  1. Utilisez vel (ValueEventListener) pour remplir recyclerview avec la liste des données actuelles à dbref. Quelque chose comme ça: vel = dbref.addValueEventListener(vel);
  2. Une fois que j'ai la liste des données actuelles à dbref, je supprime l'auditeur vel de dbref: dbref.removeEventListener(vel);. Cela n'est pas nécessaire si vous avez utilisé dbref.addListenerForSingleValueEvent(vel); à l'étape 1.
  3. Écrivez une requête requête pour filtrer les nouveaux messages insérés à dbref à partir de maintenant. Quelque chose comme ça: query = dbref.orderByChild(post.createdat).startAt(System.currentTimeMillis())
  4. Attachez un cel (ChildEventListener) à query pour recevoir uniquement les nouveaux messages: query.addChildEventListener(cel);
4
Jean Menye

Lorsque vous travaillez avec des données de collecte sur Firebase, vous devez utiliser:

  • onChildAdded
  • onChildChanged
  • onChildRemoved
  • onChildMoved

Vous n'avez peut-être pas besoin de tout cela, mais j'ai constaté qu'en général, onChildAdded et onChildRemoved sont souvent essentiels. Vous devrez trier et suivre les données changeant dans votre adaptateur et les renvoyer à RecyclerView sur chaque enfant mis à jour.

En revanche, vous pouvez utiliser le ValueEventListener et simplement mettre à jour la liste entière, mais comme vous l'avez dit, cela signifierait que chaque mise à jour de cette liste vous enverra tous les objets de la collection, ce qui coûtera à votre utilisateur données et vous coûte plus de bande passante sur Firebase. Ce n'est pas recommandé si vos données seront mises à jour souvent.

Source

2
Nitish Kasturia

J'utiliserais n'importe quel écouteur onchildAdded dans ce cas, il y a plusieurs avantages, tout d'abord vous avez compris le fonctionnement du réseau qui suppose qu'il y a 100 messages et même si l'un est changé, vous serez rappelé tous. Alors que dans onChildListener, vous obtiendrez un rappel du seul message qui a été modifié. Donc, ce que vous pouvez faire est de mapper les rappels de Firebase avec les méthodes de vues du recycleur comme ceci :: -

onChildAdded -> vous devez convertir le dataSnapshot dans votre classe et appeler recyclerView.notifyItemAdded ()

onChildRemoved -> recyclerView.notifyItemRemoved (int)

onChildChanged -> recyclerView.notifyItemChanged (int)

onChildMoved -> (vous n'en avez probablement pas besoin, c'est pour la commande/priorité)

2
DEEPANKUR SADANA