6
votes

Android Firebase comment gérer la connexion entre le serveur en temps réel et la base de données locale

En ce qui concerne les questions similaires sur ce sujet et sur ChildEventListener , il n'y a pas de réponse pertinente, alors voici la mienne.

J'ai une base de données SQLite locale qui contient tout les données, j'ai également la base de données en temps réel Firebase que je mets à jour avec de nouvelles entrées ou des changements en temps réel pour tous les utilisateurs. Je le fais actuellement avec l'utilisation de ChildEventListener comme suit:

rootRef.orderByKey().startAt("-WhatTF123456789")...

En ce qui concerne les fonctionnalités, avec ce code, je peux obtenir des modifications en temps réel sur les enfants , obtenez de nouvelles entrées, des enfants supprimés et tout ce dont j'ai besoin, mais il y a un problème. Lorsque cette activité spécifique avec l'auditeur se charge, l'auditeur onChildAdded est appelé énormément de fois pour chaque enfant sur cette racine, comme indiqué dans la documentation:

child_added est déclenché une fois pour chaque enfant existant, puis à nouveau chaque fois qu'un nouvel enfant est ajouté au chemin spécifié

J'ai donc pensé à me concentrer sur les éléments dont j'avais vraiment besoin et je l'ai fait avec:

DatabaseReference rootRef = FirebaseDatabase.getInstance().getDatabase().getReference();
    DatabaseReference childRef = rootRef.child("my_root");

    ChildEventListener eventListener = new ChildEventListener()
    {
        ....
    };
    childRef.addChildEventListener(eventListener);

Mais ensuite, j'ai perdu mes capacités CRUD parce qu'il écoute les nouvelles entrées et pas toutes.

J'ai donc trouvé une solution. Gardez le nœud avec toutes les modifications qui ont été apportées à la base de données FireBase et un nœud avec tous les utilisateurs qui ont lu et apporté les modifications à la base de données locale pour savoir qui a besoin d'une mise à jour, puis utilisez addChildEventListener pour ce nœud spécifique. Mais cela semble redondant.

Quelles sont mes options pour gérer ce genre de situation?


2 commentaires

Vos options sont 1) ignorer les nœuds enfants redondants, 2) demander de démarrer au premier nouveau nœud enfant. La deuxième option est bien sûr préférée, car la première télécharge trop de données. Vous jour "Mais alors j'ai perdu mes capacités CRUD". Pouvez-vous expliquer ce que vous entendez par là? Ou mieux encore, le montrer dans le code?


@FrankvanPuffelen Mon code est fondamentalement le ChildEventListener nu comme je l'ai montré ci-dessus, qui contient ses méthodes implémentées. Lorsque je déclare que je perds mes capacités CRUD, c'est parce que lorsque j'écoute le dernier enfant tel que: childRef.orderByKey (). LimitToLast (1) , je reçois des rappels de mise à jour et de suppression uniquement sur ce dernier enfant spécifique. Je veux les connaître tous sur le nœud, donc je n'aurai pas à utiliser la 1ère option. Ou parlez-vous d'autre chose? Est-ce que j'ai râté quelque chose?


3 Réponses :


5
votes

L'auditeur onChildAdded est appelé énormément de fois pour chaque enfant sur cette racine.

Comme vous l'avez déjà mentionné et comme l'indique la documentation, c'est le comportement attendu. En général, il n'est pas recommandé d'attacher un ChildEventListener sur un nœud (nœud racine) qui contient une énorme quantité de données. Veuillez faire attention à cette pratique car lors du téléchargement de grandes quantités de données, vous pouvez obtenir des erreurs telles que: OutOfMemoryError . Cela se produit parce que vous téléchargez implicitement le nœud entier que vous écoutez, ainsi que toutes les données en dessous. Ces données peuvent être présentes sous forme de propriétés simples ou d'objets complexes. Cela peut donc être considéré comme un gaspillage de ressources et de bande passante. Dans ce cas, la meilleure approche consiste à aplatir la base de données autant que possible. Si vous êtes nouveau dans les bases de données NoSQL, cette pratique est appelée dénormalisation et est une pratique courante en ce qui concerne Firebase. Pour une meilleure compréhension, je vous recommande de jeter un œil à:

Veuillez également noter que lorsque vous dupliquez des données, il y a une chose à garder à l'esprit. De la même manière que vous ajoutez des données, vous devez les maintenir. En d'autres termes, si vous souhaitez mettre à jour / dételer un élément, vous devez le faire dans tous les endroits où il existe.

Je vous recommande également de voir la dernière partie de ma réponse du post suivant:

C'est pour Cloud Firestore mais les mêmes règles s'appliquent à la base de données en temps réel Firebase.

Mais ensuite, j'ai perdu mes capacités CRUD car il écoute les nouvelles entrées et pas toutes.

Tout dans Firebase concerne les auditeurs. Vous ne pouvez pas obtenir de mises à jour en temps réel pour les objets d'un nœud, sauf si vous les écoutez. Vous ne pouvez donc pas limiter les résultats et vous attendre à obtenir des mises à jour d'objets que vous n'écoutez pas. Si vous avez besoin d'obtenir des mises à jour pour tous les objets d'un nœud, vous devez les écouter tous. Comme cette approche n'est pas du tout pratique, vous pouvez soit utiliser la dénormalisation comme expliqué ci-dessus, soit restreindre les résultats en utilisant des requêtes qui peuvent vous aider à limiter la quantité de données que vous obtenez de la base de données. Concernant vos solutions, la seconde est très préférée mais vous pouvez également envisager une autre approche qui consisterait à charger les données en petits morceaux selon une propriété timestamp , ou selon n'importe quel autre propriété dont vous avez besoin.

Modifier: Selon votre commentaire:

Pouvez-vous s'il vous plaît fournir des tests pour chaque solution (1. dénormalisation, 2. ma solution) examiner l'utilisation de la bande passante et des ressources et laquelle est vraiment préférée?

Toutes les données sont modélisées pour permettre les cas d'utilisation requis par une application. Malheureusement, je ne peux pas faire de tests car cela dépend vraiment du cas d'utilisation de l'application et de la quantité de données qu'elle contient. Cela signifie que ce qui fonctionne pour une application peut être insuffisant pour une autre application. Les tests peuvent donc ne pas convenir à tout le monde. Le processus de dénormalisation ou votre solution dépend entièrement de la façon dont vous envisagez d'interroger la base de données. Dans la liste ci-dessus, j'ai ajouté une nouvelle ressource qui est une de mes réponses concernant le technique de dénormalisation dans les bases de données NoSQL . J'espère que cela aidera également les visiteurs à présenter.


2 commentaires

Hé, désolé pour le retard que je suis actuellement en vacances, Je vous informe simplement que j'ai déjà implémenté la solution qui utilise un nœud avec toutes les modifications, l'ajout, les suppressions qui ont été apportées à la base de données Firebase. Le seul auditeur est ce nœud, les utilisateurs obtiennent des mises à jour RealTime et l'utilisation de la bande passante est vraiment faible. Même si j'ai déjà implémenté ma solution sur l'application, pouvez-vous s'il vous plaît fournir des tests pour chaque solution (1. dénormalisation, 2. ma solution) examiner l'utilisation de la bande passante et des ressources et laquelle est vraiment préférée? C'est bien sûr pour les futurs utilisateurs rencontrant ce problème.


ne t'inquiète pas mon ami, je sais comment fonctionne le système. Ici, prenez vos points Internet;)



1
votes

Je créerais un nœud racine avec le nom, par exemple MaintenanceUpdate . Tous les clients sont abonnés aux modifications ici. Dès que MaintenanceUpdate devient = true , tous les clients se désabonnent des modifications apportées à la "base de données" principale. Et puis (lorsque MaintenanceUpdate = false ) sont réinscrits à nouveau. En ce moment, vous mettez à jour la base de données.


0 commentaires

1
votes

J'ai des exigences similaires, avec Firebase et Room , alors que je l'ai résolu de la même manière:

public class BaseModel extends BaseObservable implements IDataModel {

    /** Sqlite default PK */
    private int itemId = 0;

    /** Firebase uniqueId */
    @ColumnInfo(name = SqliteBaseHelper.KEY_FIREBASE_UNIQUE_ID)
    protected String uniqueId = null;

    /** Firebase lastSync */
    @ColumnInfo(name = SqliteBaseHelper.KEY_FIREBASE_LAST_SYNC)
    protected long lastSync = 0;

    ...
}

cela signifie, lorsqu'un enregistrement local a un KEY_FIREBASE_UNIQUE_ID qui est null et que le KEY_FIREBASE_LAST_SYNC est 0 , il doit être inséré dans Firebase - sinon, il vérifierait, lors de l'exécution d'une synchronisation AsyncTask , si l'enregistrement local ou distant doit être mis à jour. cela est dû au fait que le problème principal est que lors de l'insertion à distance, le ChildEventListener tentera de synchroniser les doublons avec le même client - à moins d'avoir de tels indicateurs pour l'état de la synchronisation en place, localement et à distance. les clés primaires locales peuvent varier d'un client à l'autre (en fonction de la durée pendant laquelle elles étaient hors ligne et du nombre d'enregistrements insérés localement pendant l'état hors ligne), tandis que le KEY_FIREBASE_UNIQUE_ID synthétique est utilisé pour les identifier; c'est la "clé du succès".


0 commentaires