Android
Firebase Realtime DataBase
Ricerca…
Osservazioni
Gestore di eventi DataBase in realtime di Firebase
Prima inizializza FirebaseDatabase:
FirebaseDatabase database = FirebaseDatabase.getInstance();
Scrivi nel tuo database:
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
myRef.setValue("Hello, World!");
Leggi dal tuo database:
// Read from the database
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
String value = dataSnapshot.getValue(String.class);
Log.d(TAG, "Value is: " + value);
}
@Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
Recupera dati su eventi Android:
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
Toast.makeText(mContext, "Failed to load comments.",
Toast.LENGTH_SHORT).show();
}
};
ref.addChildEventListener(childEventListener);
Configurazione rapida
Completa la parte Installazione e configurazione per collegare la tua app a Firebase.
Questo creerà il progetto in Firebase.Aggiungi la dipendenza per Firebase Realtime Database al tuo file
build.gradle
livello dibuild.gradle
:
compile 'com.google.firebase:firebase-database:10.2.1'
- Configura le regole del database Firebase
Ora sei pronto per lavorare con il database Realtime in Android.
Ad esempio, si scrive un messaggio Hello World
nel database sotto la chiave del message
.
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
myRef.setValue("Hello, World!");
Progettare e comprendere come recuperare i dati in tempo reale dal database Firebase
Questo esempio presuppone che tu abbia già impostato un database in tempo reale di Firebase. Se sei un principiante, ti preghiamo di informarti qui su come aggiungere Firebase al tuo progetto Android.
Innanzitutto, aggiungi la dipendenza del database Firebase al file build.gradle a livello di app:
compile 'com.google.firebase:firebase-database:9.4.0'
Ora, creiamo un'app di chat che memorizza i dati nel database di Firebase.
Passaggio 1: creare una classe denominata Chat
Basta creare una classe con alcune variabili di base richieste per la chat:
public class Chat{
public String name, message;
}
Passaggio 2: crea alcuni dati JSON
Per inviare / recuperare dati da / verso il database Firebase, è necessario utilizzare JSON. Supponiamo che alcune chat siano già memorizzate al livello root nel database. I dati di queste chat potrebbero apparire come segue:
[
{
"name":"John Doe",
"message":"My first Message"
},
{
"name":"John Doe",
"message":"Second Message"
},
{
"name":"John Doe",
"message":"Third Message"
}
]
Passaggio 3: aggiungere gli ascoltatori
Ci sono tre tipi di ascoltatori. Nell'esempio seguente useremo childEventListener
:
DatabaseReference chatDb = FirebaseDatabase.getInstance().getReference() // Referencing the root of the database.
.child("chats"); // Referencing the "chats" node under the root.
chatDb.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
// This function is called for every child id chat in this case, so using the above
// example, this function is going to be called 3 times.
// Retrieving the Chat object from this function is simple.
Chat chat; // Create a null chat object.
// Use the getValue function in the dataSnapshot and pass the object's class name to
// which you want to convert and get data. In this case it is Chat.class.
chat = dataSnapshot.getValue(Chat.class);
// Now you can use this chat object and add it into an ArrayList or something like
// that and show it in the recycler view.
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// This function is called when any of the node value is changed, dataSnapshot will
// get the data with the key of the child, so you can swap the new value with the
// old one in the ArrayList or something like that.
// To get the key, use the .getKey() function.
// To get the value, use code similar to the above one.
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// This function is called when any of the child node is removed. dataSnapshot will
// get the data with the key of the child.
// To get the key, use the s String parameter .
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
// This function is called when any of the child nodes is moved to a different position.
// To get the key, use the s String parameter.
}
@Override
public void onCancelled(DatabaseError databaseError) {
// If anything goes wrong, this function is going to be called.
// You can get the exception by using databaseError.toException();
}
});
Passaggio 4: aggiungere dati al database
Basta creare un oggetto di classe Chat e aggiungere i valori come segue:
Chat chat=new Chat();
chat.name="John Doe";
chat.message="First message from android";
Ora ottieni un riferimento al nodo chat come fatto nella sessione di recupero:
DatabaseReference chatDb = FirebaseDatabase.getInstance().getReference().child("chats");
Prima di iniziare ad aggiungere dati, tieni presente che hai bisogno di un riferimento più approfondito poiché un nodo chat ha molti più nodi e aggiungere una nuova chat significa aggiungere un nuovo nodo contenente i dettagli della chat. Possiamo generare un nuovo nome univoco del nodo utilizzando la funzione push()
sull'oggetto DatabaseReference
, che restituirà un altro DatabaseReference
, che a sua volta punta a un nodo appena formato per inserire i dati della chat.
Esempio
// The parameter is the chat object that was newly created a few lines above.
chatDb.push().setValue(chat);
La funzione setValue()
farà in modo che tutte le funzioni onDataChanged
dell'applicazione vengano chiamate (incluso lo stesso dispositivo), che risulta essere il listener collegato del nodo "chat".
Denormalizzazione: struttura piatta del database
La denormalizzazione e una struttura piatta del database sono necessarie per scaricare in modo efficiente chiamate separate. Con la seguente struttura, è anche possibile mantenere relazioni bidirezionali. Lo svantaggio di questo approccio è che devi sempre aggiornare i dati in più punti.
Ad esempio, immagina un'app che consenta all'utente di memorizzare i messaggi su se stesso (promemoria).
Struttura del database piatta desiderata:
|--database
|-- memos
|-- memokey1
|-- title: "Title"
|-- content: "Message"
|-- memokey2
|-- title: "Important Title"
|-- content: "Important Message"
|-- users
|-- userKey1
|-- name: "John Doe"
|-- memos
|-- memokey1 : true //The values here don't matter, we only need the keys.
|-- memokey2 : true
|-- userKey2
|-- name: "Max Doe"
La classe memo utilizzata
public class Memo {
private String title, content;
//getters and setters ...
//toMap() is necessary for the push process
private Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("title", title);
result.put("content", content);
return result;
}
}
Recupero dei memo di un utente
//We need to store the keys and the memos seperately
private ArrayList<String> mKeys = new ArrayList<>();
private ArrayList<Memo> mMemos = new ArrayList<>();
//The user needs to be logged in to retrieve the uid
String currentUserId = FirebaseAuth.getInstance().getCurrentUser().getUid();
//This is the reference to the list of memos a user has
DatabaseReference currentUserMemoReference = FirebaseDatabase.getInstance().getReference()
.child("users").child(currentUserId).child("memos");
//This is a reference to the list of all memos
DatabaseReference memoReference = FirebaseDatabase.getInstance().getReference()
.child("memos");
//We start to listen to the users memos,
//this will also retrieve the memos initially
currentUserMemoReference.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Here we retrieve the key of the memo the user has.
String key = dataSnapshot.getKey(); //for example memokey1
//For later manipulations of the lists, we need to store the key in a list
mKeys.add(key);
//Now that we know which message belongs to the user,
//we request it from our memos:
memoReference.child(key).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Here we retrieve our memo:
Memo memo = dataSnapshot.getValue(Memo.class);
mMemos.add(memo);
}
@Override
public void onCancelled(DatabaseError databaseError) { }
});
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) { }
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) { }
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) { }
@Override
public void onCancelled(DatabaseError databaseError) { }
}
Creare un memo
//The user needs to be logged in to retrieve the uid
String currentUserUid = FirebaseAuth.getInstance().getCurrentUser().getUid();
//This is the path to the list of memos a user has
String userMemoPath = "users/" + currentUserUid + "/memos/";
//This is the path to the list of all memos
String memoPath = "memos/";
//We need to retrieve an unused key from the memos reference
DatabaseReference memoReference = FirebaseDatabase.getInstance().getReference().child("memos");
String key = memoReference.push().getKey();
Memo newMemo = new Memo("Important numbers", "1337, 42, 3.14159265359");
Map<String, Object> childUpdates = new HashMap<>();
//The second parameter **here** (the value) does not matter, it's just that the key exists
childUpdates.put(userMemoPath + key, true);
childUpdates.put(memoPath + key, newMemo.toMap());
FirebaseDatabase.getInstance().getReference().updateChildren(childUpdates);
Dopo che il push, o il database appare così:
|--database
|-- memos
|-- memokey1
|-- title: "Title"
|-- content: "Message"
|-- memokey2
|-- title: "Important Title"
|-- content: "Important Message"
|-- generatedMemokey3
|-- title: "Important numbers"
|-- content: "1337, 42, 3.14159265359"
|-- users
|-- userKey1
|-- name: "John Doe"
|-- memos
|-- memokey1 : true //The values here don't matter, we only need the keys.
|-- memokey2 : true
|-- generatedMemokey3 : true
|-- userKey2
|-- name: "Max Doe"
Comprensione del database JSON di Firebase
Prima che ci sporchiamo le mani con il codice, sento che è necessario capire come i dati vengono archiviati in Firebase. A differenza dei database relazionali, Firebase memorizza i dati in formato JSON. Pensa a ogni riga di un database relazionale come un oggetto JSON (che è in pratica una coppia chiave-valore non ordinata). Quindi il nome della colonna diventa chiave e il valore memorizzato in quella colonna per una riga specifica è il valore. In questo modo l'intera riga viene rappresentata come un oggetto JSON e un elenco di questi rappresenta un'intera tabella di database. Il vantaggio immediato che vedo per questo è la modifica dello schema diventa un'operazione molto più economica rispetto al vecchio RDBMS. È più semplice aggiungere un paio di ulteriori attributi a un JSON piuttosto che alterare una struttura di tabella.
ecco un esempio JSON per mostrare come i dati sono memorizzati in Firebase:
{
"user_base" : {
"342343" : {
"email" : "[email protected]",
"authToken" : "some string",
"name" : "Kaushal",
"phone" : "+919916xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "google",
},
"354895" : {
"email" : "[email protected]",
"authToken" : "some string",
"name" : "devil",
"phone" : "+919685xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "github"
},
"371298" : {
"email" : "[email protected]",
"authToken" : "I am batman",
"name" : "Bruce Wayne",
"phone" : "+14085xxxxxx",
"serviceProviderId" : "firebase",
"signInServiceType" : "shield"
}
},
"user_prefs": {
"key1":{
"data": "for key one"
},
"key2":{
"data": "for key two"
},
"key3":{
"data": "for key three"
}
},
//other structures
}
Questo mostra chiaramente come i dati che abbiamo usato per memorizzare nei database relazionali possono essere archiviati in formato JSON. Ora vediamo come leggere questi dati nei dispositivi Android.
Recupero di dati da Firebase
Immagino che tu sappia già aggiungere il campo base delle dipendenze di gradle in Android Studio. Se non segui semplicemente la guida da qui . Aggiungi la tua app nella console di Firebase, gradle sync android studio dopo aver aggiunto le dipendenze. Tutte le dipendenze non sono necessarie solo il database di Firebase e l'autenticazione di Firebase.
Ora che sappiamo come vengono archiviati i dati e come aggiungere le dipendenze gradle, vediamo come utilizzare l'SDK Android firebase importato per recuperare i dati.
creare un riferimento al database Firebase
DatabaseReference userDBRef = FirebaseDatabase.getInstance().getReference();
// above statement point to base tree
userDBRef = DatabaseReference.getInstance().getReference().child("user_base")
// points to user_base table JSON (see previous section)
da qui puoi concatenare più chiamate di metodo child () per puntare ai dati a cui sei interessato. Ad esempio, se i dati sono memorizzati come illustrato nella sezione precedente e vuoi puntare all'utente di Bruce Wayne puoi usare:
DatabaseReference bruceWayneRef = userDBRef.child("371298");
// 371298 is key of bruce wayne user in JSON structure (previous section)
O semplicemente passa l'intero riferimento all'oggetto JSON:
DatabaseReference bruceWayneRef = DatabaseReference.getInstance().getReference()
.child("user_base/371298");
// deeply nested data can also be referenced this way, just put the fully
// qualified path in pattern shown in above code "blah/blah1/blah1-2/blah1-2-3..."
Ora che abbiamo il riferimento dei dati che vogliamo recuperare, possiamo usare gli ascoltatori per recuperare i dati nelle app Android. A differenza delle chiamate tradizionali in cui si attivano le chiamate API REST utilizzando retrofit o volley, qui è necessario un semplice listener di callback per ottenere i dati. Firebase sdk chiama i metodi di callback e il gioco è fatto.
Esistono fondamentalmente due tipi di listener che è possibile collegare, uno è ValueEventListener e l'altro è ChildEventListener (descritto nella sezione successiva). Per qualsiasi modifica dei dati nel nodo abbiamo riferimenti e aggiunti listener, i listener di eventi value restituiscono l'intera struttura JSON e il listener di eventi figlio restituisce child specifico in cui è avvenuta la modifica. Entrambi sono utili a modo loro. Per recuperare i dati da Firebase possiamo aggiungere uno o più listener a un database di Firebase (elenco userDBRef creato in precedenza).
Ecco alcuni esempi di codice (spiegazione del codice dopo il codice):
userDBRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
User bruceWayne = dataSnapshot.child("371298").getValue(User.class);
// Do something with the retrieved data or Bruce Wayne
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.e("UserListActivity", "Error occured");
// Do something about the error
});
Hai notato il tipo di classe passato. DataSnapshot può convertire i dati JSON nei POJO definiti, passare semplicemente il tipo di classe corretto.
Se il tuo caso d'uso non richiede l'intero dato (nel nostro caso la tabella user_base) ogni volta che si verifica qualche piccolo cambiamento o dici di voler recuperare i dati solo una volta , puoi usare il metodo addListenerForSingleValueEvent () del riferimento Database. Questo spara il callback solo una volta.
userDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Do something
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Do something about the error
});
Sopra i campioni verrà fornito il valore del nodo JSON. Per ottenere la chiave basta chiamare:
String myKey = dataSnapshot.getKey();
Ascolto di aggiornamenti secondari
Prendi un caso d'uso, come un'app di chat o un'app per la lista della spesa collaborativa (in pratica richiede un elenco di oggetti da sincronizzare tra gli utenti). Se si utilizza il database Firebase e si aggiunge un listener di eventi di valore al nodo padre della chat o al nodo padre dell'elenco generi alimentari, si terminerà con l'intera struttura della chat dall'inizio (intendevo l'inizio della chat) ogni volta che viene aggiunto un nodo di chat ( cioè qualcuno dice ciao). Che non vogliamo fare, ciò a cui siamo interessati è solo il nuovo nodo o solo il vecchio nodo che è stato cancellato o modificato, quelli non modificati non dovrebbero essere restituiti.
In questo caso possiamo usare ChildEvenListener . Senza ulteriori adieu, ecco un esempio di codice (vedere le sezioni precedenti per dati JSON di esempio):
userDBRef.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
//If not dealing with ordered data forget about this
}
@Override
public void onCancelled(DatabaseError databaseError) {
});
I nomi dei metodi sono auto esplicativi. Come puoi vedere ogni volta che viene aggiunto un nuovo utente o se alcune proprietà dell'utente esistente vengono modificate o l'utente viene eliminato o rimosso, viene richiamato il metodo di callback appropriato del listener di eventi figlio con i dati rilevanti. Quindi se stai mantenendo l'interfaccia utente aggiornata per dire l'app di chat, prendi il JSON da onChildAdded () analizza in POJO e inseriscilo nell'interfaccia utente. Ricordati di rimuovere l'ascoltatore quando l'utente lascia lo schermo.
onChildChanged () fornisce l'intero valore figlio con proprietà modificate (nuove).
onChiledRemoved () restituisce il nodo figlio rimosso.
Recupero dei dati con impaginazione
Quando hai un enorme database JSON, aggiungere un listener di eventi di valore non ha senso. Restituirà l'enorme JSON e la sua analisi richiederebbe molto tempo. In questi casi possiamo usare l'impaginazione e recuperare parte dei dati e visualizzarli o elaborarli. Un po 'come caricare pigro o come recuperare vecchie chat quando l'utente fa clic su mostra chat più vecchia. In questo caso, è possibile utilizzare Query .
Prendiamo il nostro vecchio esempio nelle sezioni precedenti. La base utenti contiene 3 utenti, se cresce fino a dire trecentomila utenti e si desidera recuperare l'elenco utenti in gruppi di 50:
// class level
final int limit = 50;
int start = 0;
// event level
Query userListQuery = userDBRef.orderByChild("email").limitToFirst(limit)
.startAt(start)
userListQuery.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Do something
start += (limit+1);
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Do something about the error
});
Qui è possibile aggiungere e ascoltare eventi value o child. Chiama nuovamente la query per recuperare il prossimo 50. Assicurati di aggiungere il metodo orderByChild () , questo non funzionerà senza. Firebase deve conoscere l'ordine con cui stai impaginando.