In this android tutorial, we are going to learning how to implement Firebase Real-time database with a simple android ToDo List application.
We have looked at hot to implement Firebase Cloud Messaging but today we will look at using Firebase database.
According to Google Firebase user guide – Store and sync data with our NoSQL cloud database. Data is synced across all clients in realtime, and remains available when your app goes offline. The Firebase Realtime Database is a cloud-hosted database. Data is stored as JSON and synchronized in realtime to every connected client. When you build cross-platform apps with our iOS, Android, and JavaScript SDKs, all of your clients share one Realtime Database instance and automatically receive updates with the newest data.
I will suggest you read more about Firebase realtime database in other to understand all the features it offers and how it can reduce you development time.
For the android ToDo app, we are going to use a RecyclerView to list all the task that are to be carried out.
An EditText widget is used to add new task when a button is clicked.
All the task store in Firebase real-time database is retrieved and bind to the RecyclerView through a RecyclerView adapter.
Once a task has been done, the delete icon is used to delete the task from Firebase database.
Before we start, we are going to create a new Firebase project and add Firebase SDK to our android project.
Setup Firebase Project
Follow the steps below to register and integrate Firebase SDK to your android project.
Go the Firsbase webite and register if you don’t have an account already. The registration is very simple. After registration, go to the console dashboard and click on create new project button.
In the window modal, enter the name of your project and country as shown in the image below
Click on the create project button to continue. In the console, click the button add Firebase to your android app as shown below
In the next window, add the Package name of your application and click the Add App button as shown below.
Click the continue button to download a Json configuration file which will be added in the root App directory of your project. See the image below
Following the instructions on how to add the Google Services plugin and the Firebase SDK in build.gradle as shown below.
We have successfully added Firebase to our project. We will now focus on our android project.
Setup Firebase Database
Go to your Firebase Console and click on the database menu link
Click on the RULES menu tab and change the current rules to the code below
{
“rules”: {
“.read”: true,
“.write”: true
}
}
This is a public access to the database. Please be sure that you read the security and rules sections of Firebase database. With the above rules, it means that anybody can get access to your database.
Make sure you click on the publish button after the changes. Also note that this rule is use for development and it is not advice to be used for production application.
Create Android Client Application
Before we dive into more details, it is important for us to understand what we are planning to achieve. Below is the screen-shot of the application we will be creating.
Lets start to soil our hands in code. Start up your IDE. For this tutorial, I am using the following tools and environment, feel free to use what works for you.
Windows 10
Android Studio
Sony Xperia ZL
Min SDK 14
Target SDK 23
To create a new android application project, follow the steps as stipulated below.
Go to File menu
Click on New menu
Click on Android Application
Enter Project name: AndroidFirebaseRealtimeDatabase
Package: com.inducesmile.androidfirebaserealtimedatabase
Select Blank Activity
Name your activity : MainActivity
Keep other default selections
Continue to click on next button until Finish button is active, then click on Finish Button.
Build.gradle
We are going to complete Firebase integration in our android project by modifying the project build.gradle file.
Open the Project build.gradle file and add a classpath for google services as shown below.
classpath 'com.android.tools.build:gradle:2.0.0'
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.0.0' classpath 'com.google.gms:google-services:3.0.0' } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
Open the app build.gradle and add Firebase ad dependency and google services plugin as shown below.
apply plugin: 'com.android.application' android { compileSdkVersion 24 buildToolsVersion "23.0.3" defaultConfig { applicationId "com.inducesmile.androidfirebaseadmob" minSdkVersion 14 targetSdkVersion 24 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.0.0', compile 'com.google.firebase:firebase-database:9.0.2' } apply plugin: 'com.google.gms.google-services'
Manifest.xml
Since Google is serving the advertisement from their server, it will require network call behind the hood.
We are going to add use permission for internet in Manifest.xml file.
The modify version of Manifest.xml file is shown below
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.inducesmile.androidfirebaseadmob"> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Strings.xml
We are going to update our project strings.xml file located in the values folder inside the res folder. Open the file and add the code below to it.
<resources> <string name="app_name">Firebase RealTime Database</string> <string name="add_task_button">Add Task</string> </resources>
Colors.xml
Open the colors.xml file in the same location as the strings.xml file and add the code below to the file.
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color> <color name="colorBlack">#000000</color> <color name="colorWhite">#ffffff</color> <color name="colorDivider">#ebe9e7</color> </resources>
activity_main.xml
As stated, the main layout file will contain a RecyclerView, EditText and Button controls. Open the activity_main.xml file located in the layout folder and add the code below
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.inducesmile.androidfirebaserealtimedatabase.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/task_list" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentTop="true" android:layout_above="@+id/add_task_wrapper" android:layout_marginBottom="12dp" android:orientation="vertical" android:scrollbars="none"/> <LinearLayout android:id="@+id/add_task_wrapper" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:orientation="horizontal"> <EditText android:id="@+id/add_task_box" android:layout_width="0dp" android:inputType="text" android:layout_height="match_parent" android:background="@drawable/bottom_border" android:layout_weight="7"/> <Button android:id="@+id/add_task_button" android:layout_width="0dp" android:layout_height="match_parent" android:text="@string/add_task_button" android:padding="12dp" android:layout_margin="0dp" android:layout_weight="3"/> </LinearLayout> </RelativeLayout>
MainActivity.java
In the MainActivity class, we are going to get the instances of the View controls used in the main layout file.
When the Button widget is click, the text content of the EditText is obtained and send to the Firebase database through Push() and setValue() methods of the DatabaseReference class.
The addChildEventListener of the DatabaseReference class is used to monitor data change in the Firebase database.
Finally, we have created two methods getAllTask(DataSnapshot dataSnapshot) and taskDeletion(DataSnapshot dataSnapshot) that will be called when a new task is added or delete from the Firebase database.
Open the MainActivity class and add the code below.
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; 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 java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); private RecyclerView recyclerView; private LinearLayoutManager linearLayoutManager; private RecyclerViewAdapter recyclerViewAdapter; private EditText addTaskBox; private DatabaseReference databaseReference; private List<Task> allTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); allTask = new ArrayList<Task>(); databaseReference = FirebaseDatabase.getInstance().getReference(); addTaskBox = (EditText)findViewById(R.id.add_task_box); recyclerView = (RecyclerView)findViewById(R.id.task_list); linearLayoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); Button addTaskButton = (Button)findViewById(R.id.add_task_button); addTaskButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String enteredTask = addTaskBox.getText().toString(); if(TextUtils.isEmpty(enteredTask)){ Toast.makeText(MainActivity.this, "You must enter a task first", Toast.LENGTH_LONG).show(); return; } if(enteredTask.length() < 6){ Toast.makeText(MainActivity.this, "Task count must be more than 6", Toast.LENGTH_LONG).show(); return; } Task taskObject = new Task(enteredTask); databaseReference.push().setValue(taskObject); addTaskBox.setText(""); } }); databaseReference.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(DataSnapshot dataSnapshot, String s) { getAllTask(dataSnapshot); } @Override public void onChildChanged(DataSnapshot dataSnapshot, String s) { getAllTask(dataSnapshot); } @Override public void onChildRemoved(DataSnapshot dataSnapshot) { taskDeletion(dataSnapshot); } @Override public void onChildMoved(DataSnapshot dataSnapshot, String s) { } @Override public void onCancelled(DatabaseError databaseError) { } }); } private void getAllTask(DataSnapshot dataSnapshot){ for(DataSnapshot singleSnapshot : dataSnapshot.getChildren()){ String taskTitle = singleSnapshot.getValue(String.class); allTask.add(new Task(taskTitle)); recyclerViewAdapter = new RecyclerViewAdapter(MainActivity.this, allTask); recyclerView.setAdapter(recyclerViewAdapter); } } private void taskDeletion(DataSnapshot dataSnapshot){ for(DataSnapshot singleSnapshot : dataSnapshot.getChildren()) { String taskTitle = singleSnapshot.getValue(String.class); for(int i = 0; i < allTask.size(); i++){ if(allTask.get(i).getTask().equals(taskTitle)){ allTask.remove(i); } } Log.d(TAG, "Task tile " + taskTitle); recyclerViewAdapter.notifyDataSetChanged(); recyclerViewAdapter = new RecyclerViewAdapter(MainActivity.this, allTask); recyclerView.setAdapter(recyclerViewAdapter); } } }
RecyclerViewAdapter.java
Create a new java file in the project package folder and name it RecyclerViewAdapter.java. Let this class inherits from the RecyclerView.Adapter<RecyclerViewHolders>.
Pass the application context and List of Task data object as parameters. Open the class and add the code below.
import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.List; public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewHolders> { private List<Task> task; protected Context context; public RecyclerViewAdapter(Context context, List<Task> task) { this.task = task; this.context = context; } @Override public RecyclerViewHolders onCreateViewHolder(ViewGroup parent, int viewType) { RecyclerViewHolders viewHolder = null; View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.to_do_list, parent, false); viewHolder = new RecyclerViewHolders(layoutView, task); return viewHolder; } @Override public void onBindViewHolder(RecyclerViewHolders holder, int position) { holder.categoryTitle.setText(task.get(position).getTask()); } @Override public int getItemCount() { return this.task.size(); } }
RecyclerViewHolders.java
Follow the same procedure and create another java file. Name this file RecyclerViewHolders.java. Open this file and add the code below to it.
import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; 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.List; public class RecyclerViewHolders extends RecyclerView.ViewHolder{ private static final String TAG = RecyclerViewHolders.class.getSimpleName(); public ImageView markIcon; public TextView categoryTitle; public ImageView deleteIcon; private List<Task> taskObject; public RecyclerViewHolders(final View itemView, final List<Task> taskObject) { super(itemView); this.taskObject = taskObject; categoryTitle = (TextView)itemView.findViewById(R.id.task_title); markIcon = (ImageView)itemView.findViewById(R.id.task_icon); deleteIcon = (ImageView)itemView.findViewById(R.id.task_delete); deleteIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(v.getContext(), "Delete icon has been clicked", Toast.LENGTH_LONG).show(); String taskTitle = taskObject.get(getAdapterPosition()).getTask(); Log.d(TAG, "Task Title " + taskTitle); DatabaseReference ref = FirebaseDatabase.getInstance().getReference(); Query applesQuery = ref.orderByChild("task").equalTo(taskTitle); applesQuery.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { for (DataSnapshot appleSnapshot: dataSnapshot.getChildren()) { appleSnapshot.getRef().removeValue(); } } @Override public void onCancelled(DatabaseError databaseError) { Log.e(TAG, "onCancelled", databaseError.toException()); } }); } }); } }
to_do_list.xml
Create a layout file in the layout folder and name this file to_do_list.xml. Open the layout file and add the code below.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="18dp" android:paddingBottom="18dp" android:orientation="horizontal"> <ImageView android:id="@+id/task_icon" android:layout_width="0dp" android:layout_height="wrap_content" android:src="@drawable/mark" android:layout_weight="15" android:contentDescription="@string/app_name" /> <TextView android:id="@+id/task_title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="70" android:text="@string/app_name" android:textSize="16sp" android:textColor="@color/colorBlack"/> <ImageView android:id="@+id/task_delete" android:layout_width="0dp" android:layout_height="wrap_content" android:src="@drawable/delete" android:layout_weight="15" android:contentDescription="@string/app_name" /> </LinearLayout>
Task.java
Create a new java file and name it Task.java. Paste the code below to the file.
public class Task { private String task; public Task() {} public Task(String task) { this.task = task; } public String getTask() { return task; } }
This brings us to the end of this tutorial. I hope that you have learn something. Run your app and see for yourself.
You can download the code for this tutorial below. If you are having hard time downloading the tutorial, kindly contact me.
Remember to subscribe with your email address so that you will be among the first to receive my new android blog post once it is published.
Please if you love this tutorial, kindly download my android app – Complete Mathematics – in Google Play Store and let me know what you think about it.
Can you please update the code with the new FireBaseUI ?
nobody use the regular adapter anymore
That is not the right way to sound. There is FireBaseUI which is optional to use. You can still work with Firebase backend service without using FirebaseUI. It is better you request a tutorial on Firebase UI rather than saying “nobody use the regular adapter anymore”.
Thanks @Henry , but am getting an Error on allTask.add(new Task(taskTitle)); , theres a redline under taskTitle , isnt there another way of implementing that .
Please kindly tell me what the error is about so that I can help you fix it. Besides I hope you have created your own database because I have deleted my Firebase database. May be that is the problem.
one thing you forget to add recyclerview dependencies is gradle …
my question is what if i have multiple variables in task.java and can you please update recyclerview adapter …
Thanks
hiii…
A Simple Android ToDo List App with RecyclerView and Firebase Real-time Database can you please send me the source code for this article.
I am getting error as Task cannot be instantiated, can you help me with that asap.
Thanks in advance.
@PS, did you add the Task class in your application?
My Error : java.lang.NoSuchMethodError: No static method zzb(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
Please help
I need to see the whole error message to help me figure out what your issue might be.
Hello Henry. Thanks for writing the tutorial.
Gradle is complaining that the drawable symbols aren’t found. ie @drawable/bottom_border, @drawable/mark and @drawable/mark.
Do I have to create the drawables on my own and import into the project or have I messed up the set up of the project and so Gradle can’t find them automatically?
Thats is some image and drawable resources I used in the project. You can use different images in the drawable folder. But make sure there are same name. Thanks
HI,
This is damn good to understand but showing error with getTask() and new task (entered Task) and new Task(taskTitle) says Task is abstract and cannot be instantiated plz help
Also says getTask() method cannot be resolved plz help i want this code in my project
Hey thanks for the great tutorial!
I get the “cannot resolve method ‘getTask’ ” error too and also this ->
RecyclerViewAdapter (Context, java.util.List)
in RecyclerViewAdapter cannot be applied to (QuestionsActivity, java.util.List)
(where QuestionsActivity is the MainActivity i just named it different)
Can you please help me or give me a hint of what is going on ?
Thank you
Where is the download code?
i can’t download the code