web-dev-qa-db-fra.com

Comment renvoyer la valeur DataSnapshot à la suite d'une méthode?

Je n'ai pas beaucoup d'expérience avec Java. Je ne sais pas si cette question est stupide, mais je dois obtenir un nom d'utilisateur de la base de données en temps réel Firebase et renvoyer ce nom à la suite de cette méthode. J'ai donc trouvé comment obtenir cette valeur, mais je ne comprends pas comment la renvoyer à la suite de cette méthode. Quelle est la meilleure façon de faire cela?

private String getUserName(String uid) {
    databaseReference.child(String.format("users/%s/name", uid))
            .addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // How to return this value?
            dataSnapshot.getValue(String.class);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
}
30
Ilya Cucumber

Il s'agit d'un problème classique avec les API Web asynchrones. Vous ne pouvez pas retourner quelque chose maintenant qui n'a pas encore été chargé. En d’autres termes, vous ne pouvez pas simplement créer une variable globale et l’utiliser en dehors de la méthode onDataChange() parce que ce sera toujours null. Cela se produit parce que la méthode onDataChange() est appelée asynchrone. En fonction de la vitesse de votre connexion et de l'état de votre connexion, cela peut prendre quelques centaines de millisecondes à quelques secondes avant que ces données ne soient disponibles.

Mais non seulement la base de données Firebase Realtime charge les données de manière asynchrone, mais presque toutes les autres API Web modernes, car cela peut prendre un certain temps. Ainsi, au lieu d'attendre les données (ce qui peut entraîner des boîtes de dialogue non conformes pour vos utilisateurs), le code de votre application principale continue pendant que les données sont chargées sur un thread secondaire. Ensuite, lorsque les données sont disponibles, votre méthode onDataChange () est appelée et peut utiliser les données. En d'autres termes, au moment où la méthode onDataChange() est appelée, vos données ne sont pas encore chargées.

Prenons un exemple, en plaçant quelques instructions de journal dans le code, pour voir plus clairement ce qui se passe.

private String getUserName(String uid) {
    Log.d("TAG", "Before attaching the listener!");
    databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // How to return this value?
            dataSnapshot.getValue(String.class);
            Log.d("TAG", "Inside onDataChange() method!");
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
    Log.d("TAG", "After attaching the listener!");
}

Si nous courons ce code, la sortie sera:

Avant de joindre l'auditeur!

Après avoir attaché l'auditeur!

À l'intérieur de la méthode onDataChange ()!

Ce n'est probablement pas ce à quoi vous vous attendiez, mais cela explique précisément pourquoi vos données sont null lors de leur renvoi.

La réponse initiale pour la plupart des développeurs est d'essayer de "réparer" ce asynchronous behavior, Ce que je recommande personnellement de ne pas utiliser. Le Web est asynchrone et plus vite vous l'accepterez, plus vite vous pourrez apprendre à devenir productif avec les API Web modernes.

J'ai trouvé le plus facile de recadrer les problèmes pour ce paradigme asynchrone. Au lieu de dire "Commencez par récupérer les données, puis enregistrez-les", le problème est présenté comme suit: "Commencez pour obtenir des données. Lorsque les données sont chargées, enregistrez-les". Cela signifie que tout code nécessitant des données doit être à l'intérieur de la méthode onDataChange() ou appelé de l'intérieur, comme ceci:

databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // How to return this value?
        if(dataSnapshot != null) {
            System.out.println(dataSnapshot.getValue(String.class));
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {}
});

Si vous voulez utiliser cela à l'extérieur, il existe une autre approche. Vous devez créer votre propre rappel pour attendre que Firebase vous renvoie les données. Pour y parvenir, vous devez d’abord créer un interface comme ceci:

public interface MyCallback {
    void onCallback(String value);
}

Ensuite, vous devez créer une méthode qui récupère les données de la base de données. Cette méthode devrait ressembler à ceci:

public void readData(MyCallback myCallback) {
    databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            String value = dataSnapshot.getValue(String.class);
            myCallback.onCallback(value);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
}

En fin de compte, appelez simplement la méthode readData() et transmettez une instance de l'interface MyCallback en tant qu'argument chaque fois que vous en avez besoin, comme ceci:

readData(new MyCallback() {
    @Override
    public void onCallback(String value) {
        Log.d("TAG", value);
    }
});

C'est le seul moyen d'utiliser cette valeur en dehors de la méthode onDataChange(). Pour plus d'informations, vous pouvez également jeter un coup d'œil à ceci vidéo.

67
Alex Mamo

Voici une idée folle, dans onDataChange, placez-la dans un TextView avec une visibilité perdue textview.setVisiblity(Gone) ou quelque chose d'autre, puis faites quelque chose comme:

textview.setText(dataSnapshot.getValue(String.class))

puis l'obtenir plus tard avec textview.getText().toString()

juste une idée simple folle.

3
klaid bendio Moran

Je crois comprendre ce que vous demandez. Même si vous dites que vous voulez le "renvoyer" (en tant que tel) à partir de la méthode d'extraction, il peut suffire de dire que vous souhaitez simplement pouvoir utiliser la valeur extraite une fois l'extraction terminée. Si oui, voici ce que vous devez faire:

  1. Créez une variable en haut de votre classe
  2. Récupérez votre valeur (ce que vous avez principalement fait correctement)
  3. Définissez la variable publique dans votre classe égale à la valeur extraite

Une fois que votre extraction a réussi, vous pouvez faire beaucoup de choses avec la variable. 4a et 4b sont quelques exemples simples:

4a. Edit: Comme exemple d'utilisation, vous pouvez déclencher tout ce que vous avez besoin d'exécuter dans votre classe et qui utilise yourNameVariable (et vous pouvez en être sûr que yourNameVariable n'est pas null )

4b. Edit: Comme exemple d'utilisation, vous pouvez utiliser la variable dans une fonction déclenchée par onClickListener d'un bouton.


Essaye ça.

// 1. Create a variable at the top of your class
private String yourNameVariable;

// 2. Retrieve your value (which you have done mostly correctly)
private void getUserName(String uid) {
    databaseReference.child(String.format("users/%s/name", uid))
            .addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            // 3. Set the public variable in your class equal to value retrieved
            yourNameVariable = dataSnapshot.getValue(String.class);
            // 4a. EDIT: now that your fetch succeeded, you can trigger whatever else you need to run in your class that uses `yourNameVariable`, and you can be sure `yourNameVariable` is not null.
            sayHiToMe();
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {}
    });
}

// (part of step 4a)
public void sayHiToMe() {
  Log.d(TAG, "hi there, " + yourNameVariable);
}

// 4b. use the variable in a function triggered by the onClickListener of a button.
public void helloButtonWasPressed() {
  if (yourNameVariable != null) {
    Log.d(TAG, "hi there, " + yourNameVariable);
  }
}

Ensuite, vous pouvez utiliser yourNameVariable où vous voulez pendant votre cours.


Remarque: assurez-vous simplement de vérifier que yourNameVariable n'est pas nul lorsque vous l'utilisez car onDataChange est asynchrone et peut ne pas s'être terminé au moment où vous tentez de l'utiliser. il ailleurs.

2
Rbar

Ceci fonctionne, vous pouvez utiliser les valeurs d'un datasnapshot en dehors de onDataChange en passant la valeur en tant que paramètre à une autre fonction. Veuillez voir ma réponse ci-dessous, il fonctionne et je l'utilise partout dans mon application.

// Get Your Value
private void getValue() {

    fbDbRefRoot.child("fbValue").addListenerForSingleValueEvent(new ValueEventListener() {

        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

            String yourValue = (String) dataSnapshot.getValue();
            useValue(yourValue);

        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });

}

// Use Your Value
private void useValue(String yourValue) {

    Log.d(TAG, "countryNameCode: " + yourValue);

}

La principale différence entre la mienne et la réponse de Rbar est que je passe la valeur en tant que paramètre à une fonction, puis que je l'utilise.

0
DragonFire