java - ¿Cómo devolver el valor de DataSnapshot como resultado de un método?
android firebase (4)
Aquí hay una idea loca, dentro de DataDataChange, póngala dentro de un TextView con la visibilidad desaparecida
textview.setVisiblity(Gone)
o algo así, luego haga algo como
textview.setText(dataSnapshot.getValue(String.class))
luego,
textview.getText().toString()
con
textview.getText().toString()
solo una idea simple y loca.
No tengo mucha experiencia con Java. No estoy seguro si esta pregunta es estúpida, pero necesito obtener un nombre de usuario de la base de datos en tiempo real de Firebase y devolver este nombre como resultado de este método. Entonces, descubrí cómo obtener este valor, pero no entiendo cómo devolverlo como resultado de este método. ¿Cuál es la mejor manera de hacer esto?
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) {}
});
}
Creo que entiendo lo que estás preguntando. Aunque usted dice que quiere "devolverlo" (per se) del método fetch, puede ser suficiente decir que en realidad solo quiere poder usar el valor recuperado después de que su recuperación se haya completado. Si es así, esto es lo que debe hacer:
- Crea una variable en la parte superior de tu clase
- Recupere su valor (lo que ha hecho principalmente correctamente)
- Establezca la variable pública en su clase igual al valor recuperado
Una vez que su búsqueda tiene éxito, puede hacer muchas cosas con la variable. 4a y 4b son algunos ejemplos simples:
4a.
Editar:
Como ejemplo de uso, puede activar cualquier otra cosa que necesite ejecutar en su clase que use
yourNameVariable
(y puede estar seguro de que
yourNameVariable
no es nulo)
4b. Editar: Como ejemplo de uso, puede usar la variable en una función que se activa mediante el botón onClickListener.
Prueba esto.
// 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);
}
}
Luego, puede usar
yourNameVariable
donde desee en toda su clase.
Nota: solo asegúrese de verificar que
yourNameVariable
no sea nulo cuando lo use, ya que
onDataChange
es asíncrono y puede no haberse completado en el momento en que intenta usarlo en otro lugar.
Este es un problema clásico con las API web asíncronas.
No puede devolver algo ahora que aún no se ha cargado.
En otras palabras, no puede simplemente crear una variable global y usarla fuera del método
onDataChange()
porque siempre será
null
.
Esto sucede porque el método
onDataChange()
se llama asíncrono.
Dependiendo de la velocidad de su conexión y el estado, pueden pasar de unos cientos de milisegundos a unos segundos antes de que esos datos estén disponibles.
Pero no solo Firebase Realtime Database carga datos de forma asincrónica, sino que casi todas las API web modernas lo hacen, ya que puede llevar algo de tiempo.
Por lo tanto, en lugar de esperar los datos (que pueden generar diálogos de aplicación que no responden para sus usuarios), el código de la aplicación principal continúa mientras los datos se cargan en un subproceso secundario.
Luego, cuando los datos están disponibles, se llama a su método onDataChange () y puede usar los datos.
En otras palabras, cuando se llama al método
onDataChange()
, sus datos aún no se han cargado.
Tomemos un ejemplo, colocando algunas declaraciones de registro en el código, para ver más claramente lo que está sucediendo.
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 ejecutamos este código, la salida será:
¡Antes de unir al oyente!
Después de adjuntar el oyente!
¡Dentro del método onDataChange ()!
Probablemente esto no sea lo que esperaba, pero explica precisamente por qué sus datos son
null
al devolverlos.
La respuesta inicial para la mayoría de los desarrolladores es tratar de "arreglar" este
asynchronous behavior
, que personalmente recomiendo en contra de esto.
La web es asíncrona, y cuanto antes lo aceptes, antes podrás aprender a ser productivo con las API web modernas.
Me ha resultado más fácil replantear problemas para este paradigma asincrónico.
En lugar de decir "Primero obtenga los datos, luego regístrelos", enmarco el problema como "Comenzar a obtener datos. Cuando los datos estén cargados, regístrelos".
Esto significa que cualquier código que requiera los datos debe estar dentro del método
onDataChange()
o invocado desde allí, de esta manera:
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 quieres usar eso afuera, hay otro enfoque.
Debe crear su propia devolución de llamada para esperar a que Firebase le devuelva los datos.
Para lograr esto, primero debe crear una
interface
como esta:
public interface MyCallback {
void onCallback(String value);
}
Luego, debe crear un método que realmente obtenga los datos de la base de datos. Este método debería verse así:
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) {}
});
}
Al final, simplemente llame
readData()
método
readData()
y pase una instancia de la interfaz
MyCallback
como argumento donde lo necesite de esta manera:
readData(new MyCallback() {
@Override
public void onCallback(String value) {
Log.d("TAG", value);
}
});
Esta es la única forma en que puede usar ese valor fuera del método
onDataChange()
.
Para obtener más información, también puede ver este
video
.
Esto está funcionando, puede usar los valores de una instantánea de datos fuera de onDataChange pasando el valor como un parámetro a otra función, vea mi respuesta a continuación, está funcionando y lo estoy usando en todas partes en mi aplicación.
// 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 principal diferencia entre la respuesta mía y la de Rbar es que paso el valor como parámetro a una función y luego lo uso.