Android
Firebase Realtime DataBase
Zoeken…
Opmerkingen
Firebase Realtime DataBase-gebeurtenishandler
Initialiseer eerst FirebaseDatabase:
FirebaseDatabase database = FirebaseDatabase.getInstance();
Schrijf naar uw database:
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
myRef.setValue("Hello, World!");
Lezen uit uw 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());
}
});
Gegevens ophalen over Android-evenementen:
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);
Snelle installatie
Voltooi het gedeelte Installatie en instellingen om uw app te verbinden met Firebase.
Hiermee wordt het project in Firebase gemaakt.Voeg de afhankelijkheid voor Firebase Realtime Database toe aan uw
build.gradle
bestand opbuild.gradle
:
compile 'com.google.firebase:firebase-database:10.2.1'
- Configureer Firebase-databaseregels
Nu bent u klaar om te werken met de Realtime-database in Android.
U schrijft bijvoorbeeld een Hello World
bericht naar de database onder de message
.
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
myRef.setValue("Hello, World!");
Ontwerpen en begrijpen hoe realtime gegevens uit de Firebase-database kunnen worden opgehaald
In dit voorbeeld wordt ervan uitgegaan dat u al een Firebase Realtime-database hebt ingesteld. Als je een voorgerecht, dan informeer jezelf hier over hoe je Firebase uw Android project toe te voegen.
Voeg eerst de afhankelijkheid van de Firebase-database toe aan het build.gradle- bestand op app-niveau:
compile 'com.google.firebase:firebase-database:9.4.0'
Laten we nu een chat-app maken die gegevens opslaat in de Firebase-database.
Stap 1: Maak een klas met de naam Chat
Maak gewoon een klas met een aantal basisvariabelen die nodig zijn voor de chat:
public class Chat{
public String name, message;
}
Stap 2: Maak enkele JSON-gegevens
Voor het verzenden / ophalen van gegevens van / naar de Firebase-database moet u JSON gebruiken. Laten we aannemen dat sommige chats al op het rootniveau in de database zijn opgeslagen. De gegevens van deze chats kunnen er als volgt uitzien:
[
{
"name":"John Doe",
"message":"My first Message"
},
{
"name":"John Doe",
"message":"Second Message"
},
{
"name":"John Doe",
"message":"Third Message"
}
]
Stap 3: De luisteraars toevoegen
Er zijn drie soorten luisteraars. In het volgende voorbeeld gaan we de 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();
}
});
Stap 4: Voeg gegevens toe aan de database
Maak gewoon een Chat-klasseobject en voeg de waarden als volgt toe:
Chat chat=new Chat();
chat.name="John Doe";
chat.message="First message from android";
Krijg nu een verwijzing naar het chats-knooppunt zoals gedaan in de ophaalsessie:
DatabaseReference chatDb = FirebaseDatabase.getInstance().getReference().child("chats");
Voordat u begint met het toevoegen van gegevens, moet u er rekening mee houden dat u nog een diepere referentie nodig hebt, omdat een chatknooppunt nog meerdere knooppunten heeft en het toevoegen van een nieuwe chat betekent dat u een nieuw knooppunt met de chatdetails toevoegt. We kunnen een nieuwe en unieke naam van het knooppunt genereren met behulp van de functie push()
op het DatabaseReference
object, dat een andere DatabaseReference
retourneert, die op zijn beurt naar een nieuw gevormd knooppunt verwijst om de chatgegevens in te voegen.
Voorbeeld
// The parameter is the chat object that was newly created a few lines above.
chatDb.push().setValue(chat);
De functie setValue()
zorgt ervoor dat alle onDataChanged
functies van de toepassing worden aangeroepen (inclusief hetzelfde apparaat), wat toevallig de gekoppelde luisteraar van het knooppunt 'chats' is.
Denormalisatie: platte databasestructuur
Denormalisatie en een platte databasestructuur zijn noodzakelijk om afzonderlijke oproepen efficiënt te downloaden. Met de volgende structuur is het ook mogelijk om tweerichtingsrelaties te onderhouden. Het nadeel van deze aanpak is dat u de gegevens altijd op meerdere plaatsen moet bijwerken.
Stel je bijvoorbeeld een app voor waarmee de gebruiker berichten voor zichzelf (memo's) kan opslaan.
Gewenste platte database structuur:
|--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"
De gebruikte memoklasse
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;
}
}
De memo's van een gebruiker ophalen
//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) { }
}
Een memo maken
//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);
Na de push ziet de database er als volgt uit:
|--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"
Inzicht in de fire-base JSON-database
Voordat we onze handen vuil maken met code, denk ik dat het noodzakelijk is om te begrijpen hoe gegevens worden opgeslagen in firebase. In tegenstelling tot relationele databases, slaat firebase gegevens op in JSON-indeling. Beschouw elke rij in een relationele database als een JSON-object (dat in feite een ongeordend sleutel / waarde-paar is). Dus de kolomnaam wordt sleutel en de waarde die in die kolom voor een bepaalde rij is opgeslagen, is de waarde. Op deze manier wordt de hele rij weergegeven als een JSON-object en vertegenwoordigt een lijst hiervan een volledige databasetabel. Het directe voordeel dat ik hiervoor zie, is schemamodificatie dat het veel goedkoper werkt in vergelijking met oude RDBMS. Het is gemakkelijker om een paar meer attributen aan een JSON toe te voegen dan een tabelstructuur te wijzigen.
hier is een voorbeeld van JSON om te laten zien hoe gegevens worden opgeslagen 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
}
Dit laat duidelijk zien hoe gegevens die we hebben opgeslagen in relationele databases kunnen worden opgeslagen in JSON-indeling. Laten we nu eens kijken hoe we deze gegevens op Android-apparaten kunnen lezen.
Gegevens ophalen uit firebase
Ik ga ervan uit dat je al weet over het toevoegen van gradle afhankelijkheden firebase in Android Studio. Als u niet alleen de gids vanaf hier volgt . Voeg uw app toe in firebase console, gradle sync android studio na het toevoegen van afhankelijkheden. Alle afhankelijkheden zijn niet nodig, alleen firebase database en firebase auth.
Nu we weten hoe gegevens worden opgeslagen en hoe gradle-afhankelijkheden kunnen worden toegevoegd, laten we kijken hoe we de geïmporteerde firebase android SDK kunnen gebruiken om gegevens op te halen.
maak een referentie voor de firebase-database
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)
vanaf hier kunt u meerdere aanroepen van de methode child () koppelen om te verwijzen naar de gegevens waarin u bent geïnteresseerd. Als gegevens bijvoorbeeld worden opgeslagen zoals afgebeeld in de vorige sectie en u wilt verwijzen naar de Bruce Wayne-gebruiker, kunt u het volgende gebruiken:
DatabaseReference bruceWayneRef = userDBRef.child("371298");
// 371298 is key of bruce wayne user in JSON structure (previous section)
Of geef gewoon de hele verwijzing door naar het JSON-object:
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..."
Nu we de referentie hebben van de gegevens die we willen ophalen, kunnen we luisteraars gebruiken om gegevens op te halen in Android-apps. In tegenstelling tot de traditionele oproepen waarbij u REST API-oproepen activeert met retrofit of volley, is hier een eenvoudige callback-luisteraar vereist om de gegevens op te halen. Firebase sdk roept de callback-methoden aan en u bent klaar.
Er zijn in principe twee soorten luisteraars die u kunt koppelen, een is ValueEventListener en de andere is ChildEventListener (beschreven in de volgende sectie). Voor elke wijziging in de gegevens onder het knooppunt hebben we verwijzingen en toegevoegde luisteraars, value-eventlisteners retourneren de gehele JSON-structuur en child-eventlistener retourneert een specifiek kind waar de wijziging is gebeurd. Beide zijn op hun eigen manier nuttig. Om de gegevens van firebase op te halen, kunnen we een of meer luisteraars toevoegen aan een referentie van de firebase-database (lijst userDBRef die we eerder hebben gemaakt).
Hier is een voorbeeldcode (codeverklaring na code):
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
});
Heb je gemerkt dat het Klasse-type is geslaagd. DataSnapshot kan JSON-gegevens converteren naar onze gedefinieerde POJO's, geef eenvoudig het juiste klasse-type door.
Als uw use case niet de volledige gegevens vereist (in onze tabel user_base) elke keer dat er een kleine wijziging optreedt of als u zegt dat u de gegevens slechts één keer wilt ophalen , kunt u de methode addListenerForSingleValueEvent () gebruiken voor databasereferentie. Hierdoor wordt het terugbellen slechts eenmaal geactiveerd.
userDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Do something
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Do something about the error
});
Bovenstaande voorbeelden geven u de waarde van het JSON-knooppunt. Om de sleutel te krijgen, belt u eenvoudig:
String myKey = dataSnapshot.getKey();
Luisteren naar kind-updates
Neem een use case, zoals een chat-app of een gezamenlijke boodschappenlijst-app (waarvoor in principe een lijst met objecten moet worden gesynchroniseerd tussen gebruikers). Als u een firebase-database gebruikt en een waarde-gebeurtenislistener toevoegt aan het bovenliggende knooppunt van de chat of het bovenliggende knooppunt van de boodschappenlijst, eindigt de hele chatstructuur vanaf het begin van de tijd (ik bedoelde het begin van uw chat) telkens wanneer een chatknooppunt wordt toegevoegd ( dat wil zeggen iedereen zegt hallo). Dat willen we niet doen, we zijn alleen geïnteresseerd in het nieuwe knooppunt of alleen het oude knooppunt dat is verwijderd of gewijzigd. De ongewijzigde exemplaren mogen niet worden geretourneerd.
In dit geval kunnen we ChildEvenListener gebruiken . Zonder verdere adieu, hier is een codevoorbeeld (zie vorige secties voor voorbeeld JSON-gegevens):
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) {
});
Methodenamen spreken voor zich. Zoals u kunt zien wanneer een nieuwe gebruiker wordt toegevoegd of een eigenschap van de bestaande gebruiker wordt gewijzigd of de gebruiker wordt verwijderd of verwijderd, wordt de juiste callback-methode van de child-gebeurtenislistener aangeroepen met relevante gegevens. Dus als u de UI vernieuwd houdt voor bijvoorbeeld de chat-app, haalt u de JSON van onChildAdded () parse in POJO en past u deze in uw UI. Vergeet niet om uw luisteraar te verwijderen wanneer de gebruiker het scherm verlaat.
onChildChanged () geeft de gehele onderliggende waarde met gewijzigde eigenschappen (nieuwe).
onChiledRemoved () retourneert het verwijderde onderliggende knooppunt.
Gegevens ophalen met paginering
Als u een enorme JSON-database hebt, is het toevoegen van een waarde-gebeurtenislistener niet logisch. Het zal de enorme JSON retourneren en het ontleden zou tijdrovend zijn. In dergelijke gevallen kunnen we paginering gebruiken en een deel van de gegevens ophalen en weergeven of verwerken. Een beetje zoals lui laden of zoals het ophalen van oude chats wanneer de gebruiker klikt op Oudere chat weergeven. In dit geval kan Query worden gebruikt.
Laten we het oude voorbeeld in vorige secties nemen. Het gebruikersbestand bevat 3 gebruikers, als het groeit tot 300.000 gebruikers en u wilt de gebruikerslijst in batches van 50 ophalen:
// 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
});
Hier kunnen waarde- of onderliggende gebeurtenissen worden toegevoegd en beluisterd. Roep de query opnieuw op om de volgende 50 op te halen. Zorg ervoor dat u de methode orderByChild () toevoegt , anders werkt dit niet. Firebase moet de volgorde kennen waarin u pagineert.