Android – Firebase realtime database: Basic cruds
In the previous link, we took a quick glance at what firebase is, what are its features and why and why not to use it. Then, we implemented the user authentication feature with email and password. In this tutorial, we’ll follow up with adding the logged user to a database we create. And, we’ll implement all the basic crud operations.
What we’re gonna build
By the end of this tutorial, you’ll build an app that stores a list of people, as you can see in this screenshot, to a firebase real-time database; with the possibility to add, update, or delete items and reload the list. As well as searching items on the list.
Before starting with creating the guis, you need to set up your Android project by adding the following:
1- Create an account on https://firebase.google.com/ then create you first project. Beaware to give the package name of your project, as you’ll be working on it.
2- Paste the google-services.json file to your project’s app folder. This step is very important as your project won’t build without this file.
3- Add the following to your project’s build.gradle:
dependencies { classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.google.gms:google-services:3.0.0' }
and to app’s build.gradle:
dependencies { compile "com.google.firebase:firebase-auth:9.6.1" compile 'com.google.firebase:firebase-database:9.6.1' } apply plugin: 'com.google.gms.google-services'
Now we are ready to go ! Let us just set our minds up to how firebase stores data and how it manages offline data..
How is the data stored?
What if users go offline?
Firebase realtime database is a schemaless noSQL cloud database, in which the data is stored in JSON format. Basically the entire database is a big JSON tree with multiple nodes. You can go through firebase Structure Your Database guide to learn the best practices while defining the database structure.
Firebase data is also available offline, so your app always has access to your data. The data is automatically synced once the app comes back online: You can enable disk persistence in your app so that when your app loses internet connection your users can access the cached data, new user input is cached and that cached data is synced with the database once the internet connection is re-established.
Disk persistence can be enabled by calling below one line code.
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
Here is complete guide about firebase offline capabilities.
Database security and rules
Firebase authentication is required by default for your app to use your Firebase database.
To get started without having to set up Authentication, simply configure your Firebase database rules for public access. Note that this makes your database open to anyone, so be sure to restrict your database again when you set up authentication.
Below rules allow everyone to read & write data without authentication.
{ "rules": { ".read": true, ".write": true } }
For more on how to configure the security rules, check out the documentation here.
Now, we formed a clear idea of what a realtime database is, how it stores data and what preparations do we need before creating the Android project! Let’s create one, and see how things actually work:
Performing crud operations
Let’s first create our ui. Add the following to activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.androidprojects.esprit.firebasedbtutorial.MainActivity"> <ListView android:id="@+id/peopleList" android:layout_width="match_parent" android:layout_height="700px"/> <LinearLayout android:layout_width="match_parent" android:layout_height="150px" android:orientation="horizontal" android:layout_marginTop="40px" android:gravity="center"> <EditText android:id="@+id/fullNameEditText" android:layout_width="500px" android:layout_height="wrap_content" android:textColor="#000000" android:textSize="16dp" android:hint="full name" /> <EditText android:id="@+id/phoneNumberEditText" android:layout_width="500px" android:layout_height="wrap_content" android:hint="phone number" android:textColor="#000000"/> </LinearLayout> <Button android:id="@+id/loadBtn" android:text="Load data" android:textColor="#000000" android:textStyle="bold" android:layout_width="500px" android:layout_height="150px" /> <Button android:id="@+id/addBtn" android:text="Add item" android:textColor="#000000" android:textStyle="bold" android:layout_width="500px" android:layout_height="150px" android:layout_marginLeft="30px" android:layout_toRightOf="@id/loadBtn"/> <Button android:id="@+id/updateBtn" android:text="Update item" android:textColor="#000000" android:textStyle="bold" android:layout_width="500px" android:layout_height="150px" android:layout_marginTop="30px" android:layout_below="@id/loadBtn"/> <Button android:id="@+id/deleteBtn" android:text="Delete item" android:textColor="#000000" android:textStyle="bold" android:layout_width="500px" android:layout_height="150px" android:layout_toRightOf="@id/updateBtn" android:layout_marginTop="30px" android:layout_below="@id/addBtn" android:layout_marginLeft="30px"/> <Button android:id="@+id/findBtn" android:text="Find item" android:textColor="#000000" android:textStyle="bold" android:layout_width="500px" android:layout_height="150px" android:layout_marginTop="30px" android:layout_below="@id/updateBtn"/> </RelativeLayout>
Then, we need to prepare the Person model class. The Firebase Realtime Database stores data as JSON. It therefore makes sense to save your data as Java objects which are then automatically mapped to child locations in the firebase database. Create the Person.java class as follows :
public class Person { private String fullName; private String phoneNumber; public Person(String fullName, String phoneNumber) { this.fullName = fullName; this.phoneNumber = phoneNumber; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } }
In order to perform any operation on to database whether it can be read or write, you need to get the reference to database first. The below code gives you reference to database JSON top node. From here you need to use the child node names to traverse further.
private DatabaseReference mDatabase; mDatabase = FirebaseDatabase.getInstance().getReference();
For example, if we have a products node in our database, we need to call getReference(“products”) to access and manipulate it, and create it if doesnt exist yet.
• Inserting data
There are four methods that you can use to write data to your Firebase database:
- setValue(): writes or replaces data to a given location. We use this method in our tutorial.
- push() :adds to a list of data. A unique ID is generated when you call push(). We make use of this to get an ID when adding a person using setValue() in our tutorial.
- updateChildren(): update some of the keys in the given location without replacing all of the data.
- runTransaction(): updates complex data.
Before adding Person objects to this project’s list, I should mention that if you’ve started with the user authentication tutorial, you can follow up here to add your logged user to a database :
after calling auth.createUserWithEmailAndPassword() under the btnSignUp.setOnClickListener(), add the following :
mFirebaseDatabase = FirebaseDatabase.getInstance().getReference("users"); String userId = mFirebaseDatabase.push().getKey(); User user = new User(emailNameText.getText().toString()); mFirebaseDatabase.child(userId).setValue(user);
This way you stored a new user with a unique id and email, you can update it’s data ( full name, date of birth…) later on.
Now to add a Person object to the database in our tutorial app, we need to create a person node in the database using the getReference(“Person”), and “populate it with data” or add children to it. Add the following method to MainActivity.java, we’ll call it when the addBtn is clicked:
private void addPerson(String name,int phoneNumber){ myDatabaseReference=FirebaseDatabase.getInstance().getReference("Person"); String personId = myDatabaseReference.push().getKey(); Person person = new Person(name,phoneNumber); myDatabaseReference.child(personId).setValue(person); }
Now in onCreate() of MainActivity.java, we’ll make a simple call of addPerson(String,int):
(findViewById(R.id.addBtn)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { addPerson(((EditText)findViewById(R.id.fullNameEditText)).getText().toString(), Integer.parseInt(((EditText)findViewById(R.id.phoneNumberEditText)).getText().toString())); } });
If you run your app then check the database from the firebase console, you’ll find your person added:
• Reading data
To read the data, you need to attach the ValueEventListener() to the database reference. This event will be triggered whenever there is a change in data in realtime. In onDataChange() you can perform the desired operations onto new data.
Let’s add the readData() method that will be called when the loadDataBtn is clicked:
private void readData(){ final ArrayList names = new ArrayList<>(); final ArrayList phoneNumbers = new ArrayList<>(); myDatabaseReference.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Iterable<DataSnapshot> snapshotIterator = dataSnapshot.getChildren(); Iterator<DataSnapshot> iterator = snapshotIterator.iterator(); while((iterator.hasNext())){ Person value = iterator.next().getValue(Person.class); names.add(value.getFullName()); phoneNumbers.add(value.getPhoneNumber()); ((CustomListAdapater)(((ListView)findViewById(R.id.peopleList)).getAdapter())).notifyDataSetChanged(); } } @Override public void onCancelled(DatabaseError databaseError) { } }); ((ListView)findViewById(R.id.peopleList)). setAdapter(new CustomListAdapater(names,phoneNumbers,this)); //setAdapter(new ArrayAdapter<>(getApplicationContext(), R.layout.people_list_row, R.id.personNameTv,names)); }
So in the onDataChange() method we populated an ArrayList<String> called names with fullNames, then we added an ArrayAdapter to the list with the values of names. And we called the listView adapter’s notifyDataSetChanged(), to update the list’s data. Finally, we added the adapter to the list.
• Deleting data
To delete data, you can simply call removeValue() method on to database reference. You can also pass null to setValue() method which do the same delete operation.
You can learn more about performing CRUD operations onto more advanced data like Lists of data here.
private void removePerson(String name){ /*Query deleteQuery = myDatabaseReference.orderByChild("fullName").equalTo(name); deleteQuery.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { } @Override public void onCancelled(DatabaseError databaseError) { } });*/ myDatabaseReference.child(personId).removeValue(); }
• Updating data
All it takes to update a node’s data one line of code. You have to call the same setValue() method by passing new value. You can also use updateChildren() by passing the path to update data without disturbing other child nodes data.
private void updatePerson(String name,int phoneNumber){ myDatabaseReference.child(personId).child("fullName").setValue(name); myDatabaseReference.child(personId).child("phoneNumber").setValue(phoneNumber); }
• Searching data
private void findPerson(String name){ Query deleteQuery = myDatabaseReference.orderByChild("fullName").equalTo(name); deleteQuery.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { Iterable snapshotIterator = dataSnapshot.getChildren(); Iterator iterator = snapshotIterator.iterator(); while((iterator.hasNext())){ Log.d("Item found: ",iterator.next().getValue().toString()+"---"); } } @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) { Log.d("Item not found: ","this item is not in the list"); } }); }
Here is the complete code for MainActivity.java :
package com.androidprojects.esprit.firebasedbtutorial; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import com.google.firebase.database.ChildEventListener; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.Query; import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; import java.util.Iterator; public class MainActivity extends AppCompatActivity { private DatabaseReference myDatabaseReference; private String personId; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // for data persistence FirebaseDatabase.getInstance().setPersistenceEnabled(true); myDatabaseReference=FirebaseDatabase.getInstance().getReference("Person"); personId= myDatabaseReference.push().getKey(); /*((ListView)findViewById(R.id.peopleList)). setAdapter(new ArrayAdapter<>(getApplicationContext(), R.layout.people_list_row, R.id.personNameTv, readData()));*/ (findViewById(R.id.addBtn)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { addPerson(((EditText)findViewById(R.id.fullNameEditText)).getText().toString(), Integer.parseInt(((EditText)findViewById(R.id.phoneNumberEditText)).getText().toString())); } }); (findViewById(R.id.updateBtn)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { updatePerson(((EditText)findViewById(R.id.fullNameEditText)).getText().toString(),Integer.parseInt(((EditText)findViewById(R.id.phoneNumberEditText)).getText().toString())); } }); (findViewById(R.id.deleteBtn)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { removePerson("first added"); } }); (findViewById(R.id.loadBtn)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { readData(); } }); (findViewById(R.id.findBtn)).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { findPerson(((EditText)findViewById(R.id.fullNameEditText)).getText().toString()); } }); } private void addPerson(String name,int phoneNumber){ Person person = new Person(name,phoneNumber); myDatabaseReference.child(personId).setValue(person); } private void updatePerson(String name,int phoneNumber){ myDatabaseReference.child(personId).child("fullName").setValue(name); myDatabaseReference.child(personId).child("phoneNumber").setValue(phoneNumber); } private void removePerson(String name){ /*Query deleteQuery = myDatabaseReference.orderByChild("fullName").equalTo(name); deleteQuery.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { } @Override public void onCancelled(DatabaseError databaseError) { } });*/ myDatabaseReference.child(personId).removeValue(); } private void readData(){ final ArrayList names = new ArrayList<>(); final ArrayList phoneNumbers = new ArrayList<>(); myDatabaseReference.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Iterable snapshotIterator = dataSnapshot.getChildren(); Iterator iterator = snapshotIterator.iterator(); while((iterator.hasNext())){ Person value = iterator.next().getValue(Person.class); names.add(value.getFullName()); phoneNumbers.add(value.getPhoneNumber()); ((CustomListAdapater)(((ListView)findViewById(R.id.peopleList)).getAdapter())).notifyDataSetChanged(); } } @Override public void onCancelled(DatabaseError databaseError) { } }); ((ListView)findViewById(R.id.peopleList)). setAdapter(new CustomListAdapater(names,phoneNumbers,this)); //setAdapter(new ArrayAdapter<>(getApplicationContext(), R.layout.people_list_row, R.id.personNameTv,names)); } private void findPerson(String name){ Query deleteQuery = myDatabaseReference.orderByChild("fullName").equalTo(name); deleteQuery.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { Iterable snapshotIterator = dataSnapshot.getChildren(); Iterator iterator = snapshotIterator.iterator(); while((iterator.hasNext())){ Log.d("Item found: ",iterator.next().getValue().toString()+"---"); } } @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) { Log.d("Item not found: ","this item is not in the list"); } }); } }
That’s all coders! You can find the source code here. Happy coding!
Hi I have a problem in this code
private void updatePerson(String name,int phoneNumber){
myDatabaseReference.child(personId).child(“fullName”).setValue(name);
myDatabaseReference.child(personId).child(“phoneNumber”).setValue(phoneNumber);
}
that it creates a new node not updating why…?