Hi everyone, in this post, we will learn about another component of Android which is Room Library. In this android room database example, we will learn how to use a room for handling our SQLite database.
What is Room?
The Room library provides an abstraction layer over SQLite to allow for more robust database access the full power of SQLite.
Basically, with the help of the room, we can quickly create SQLite databases and perform the operations like create, read, update and delete. The room makes everything very easy and quick.
Components of Room
- Entity: Instead of creating the SQLite table, we will create the Entity. An entity is nothing but a model class annotated with @Entity. The variables of this class are our columns, and the class is our table.
- Database: It is an abstract class where we define all our entities.
- DAO: Stands for Data Access Object. It is an interface that defines all the operations that we need to perform in our database.
Step 1: First one to Start Android Studio
Step 2 : Seconds step to Create a New Project Project ClickOn ==> File ==> NEW ==> New Project
Adding Dependencies
- Come inside app level build.gradle file and add the following dependencies.
dependencies { //define versions def support_version = "27.0.2" def room_version = "1.1.1" implementation fileTree(include: ['*.jar'], dir: 'libs') implementation "com.android.support:appcompat-v7:$support_version" implementation 'com.android.support.constraint:constraint-layout:1.1.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' //add these libraries //support design implementation "com.android.support:design:$support_version" //card view implementation "com.android.support:cardview-v7:$support_version" //recyclerview implementation "com.android.support:recyclerview-v7:$support_version" //room implementation "android.arch.persistence.room:runtime:$room_version" annotationProcessor "android.arch.persistence.room:compiler:$room_version" testImplementation "android.arch.persistence.room:testing:$room_version" }
Creating Activities
- a MainActivity created, other than this MainActivity we need, AddTaskActivity and UpdateTaskActivity.
- Create 2 more activity for Add Date and Update data.
Step3:- activity_main.xml and add the following xml code.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent” tools:context=".MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerview_tasks" android:layout_width="match_parent" android:layout_height="match_parent" /> <android.support.design.widget.FloatingActionButton android:id="@+id/floating_button_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:backgroundTint="@color/colorPrimaryDark" android:src="@drawable/ic_add" android:tint="@color/colorLight" app:fabSize="normal" /> </RelativeLayout>
AddTaskActivity
- From this activity, we will add a new task to our application. So we need an interface like this.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".AddTaskActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:orientation="vertical" android:padding="16dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="15dp" android:text="Add a Task" android:textAlignment="center" android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline" /> <EditText android:id="@+id/editTextTask" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="task?" /> <EditText android:id="@+id/editTextDesc" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="description..." /> <EditText android:id="@+id/editTextFinishBy" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="finish by?" /> <Button android:id="@+id/button_save" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="15dp" android:background="@color/colorPrimary" android:text="Save" android:textAllCaps="false" android:textColor="@color/colorLight" /> </LinearLayout> </RelativeLayout>
UpdateTaskActivity
- Finally go inside activity_update_task.xml and write the following XML code.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".UpdateTaskActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:orientation="vertical" android:padding="16dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="15dp" android:text="Update Task" android:textAlignment="center" android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline" /> <EditText android:id="@+id/editTextTask" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="task?" /> <EditText android:id="@+id/editTextDesc" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="description..." /> <EditText android:id="@+id/editTextFinishBy" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="finish by?" /> <CheckBox android:id="@+id/checkBoxFinished" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Mark as finished" /> <Button android:id="@+id/button_update" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="15dp" android:background="@color/colorPrimary" android:text="Update" android:textAllCaps="false" android:textColor="@color/colorLight" /> <Button android:id="@+id/button_delete" android:layout_width="200dp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="15dp" android:background="@color/colorRed" android:text="Delete" android:textAllCaps="false" android:textColor="@color/colorLight" /> </LinearLayout> </RelativeLayout>
Tasks RecyclerView Layout
We will display all the tasks added in a RecyclerView, and for this, we need one more layout file. To create a layout file and name it recyclerview_tasks.xml and write the following XML code inside.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="3dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="7dp"> <TextView android:id="@+id/textViewStatus" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:background="@color/colorPrimaryDark" android:text="Completed" android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" android:textColor="@color/colorLight" android:textStyle="bold" /> <TextView android:id="@+id/textViewTask" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Go Bring Eggs" android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline" /> <TextView android:id="@+id/textViewDesc" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Bring 6 eggs from super market" android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" /> <TextView android:id="@+id/textViewFinishBy" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="5pm today" android:textAppearance="@style/Base.TextAppearance.AppCompat.Medium" /> </LinearLayout> </android.support.v7.widget.CardView> </RelativeLayout>
Create a class named Task.java and write the following code.
package com.codeplayon.myapp; import android.arch.persistence.room.ColumnInfo; import android.arch.persistence.room.Entity; import android.arch.persistence.room.PrimaryKey; import java.io.Serializable; @Entity public class Task implements Serializable { @PrimaryKey(autoGenerate = true) private int id; @ColumnInfo(name = "task") private String task; @ColumnInfo(name = "desc") private String desc; @ColumnInfo(name = "finish_by") private String finishBy; @ColumnInfo(name = "finished") private boolean finished; /* * Getters and Setters * */ public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTask() { return task; } public void setTask(String task) { this.task = task; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getFinishBy() { return finishBy; } public void setFinishBy(String finishBy) { this.finishBy = finishBy; } public boolean isFinished() { return finished; } public void setFinished(boolean finished) { this.finished = finished; } }
- As you can see in the above code we have annotated the class with @Entity this is our table, and for the column id we have used @PrimaryKey(autoGenerate = true) this means this id will be auto-increment, for other columns we used @ColumnInfo(name = “columnname”)
Creating Dao
- Now we need the Dao (Data Access Object). To create an interface named TaskDao.java and write the following code.
@Dao public interface TaskDao { @Query("SELECT * FROM task") List<Task> getAll(); @Insert void insert(Task task); @Delete void delete(Task task); @Update void update(Task task); }
- You can see above we defined all the methods needed for the Create, Read, Update and Delete operation.
Creating Database
- Now create one more class and name it AppDatabase, and write the following code inside the class.
package com.codeplayon.myapp; import android.arch.persistence.room.Database; import android.arch.persistence.room.RoomDatabase; @Database(entities = {Task.class}, version = 1) public abstract class AppDatabase extends RoomDatabase { public abstract TaskDao taskDao(); }
- In the above class, we define all the entities and the database version.
Database Client
- Creating AppDatabase’s object is expensive so we will create a single instance of it.
- Create a class named DatabaseClient and write the following code.
package com.codeplayon.myapp; import android.arch.persistence.room.Room; import android.content.Context; public class DatabaseClient { private Context mCtx; private static DatabaseClient mInstance; //our app database object private AppDatabase appDatabase; private DatabaseClient(Context mCtx) { this.mCtx = mCtx; //creating the app database with Room database builder //MyToDos is the name of the database appDatabase = Room.databaseBuilder(mCtx, AppDatabase.class, "MyToDos").build(); } public static synchronized DatabaseClient getInstance(Context mCtx) { if (mInstance == null) { mInstance = new DatabaseClient(mCtx); } return mInstance; } public AppDatabase getAppDatabase() { return appDatabase; } }
Adding a Task
Now let’s perform the create operation, which is adding a task to the database.
- Come inside AddTaskActivity and write the following code.
package com.codeplayon.myapp; import android.content.Intent; import android.os.AsyncTask; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.Toast; public class AddTaskActivity extends AppCompatActivity { private EditText editTextTask, editTextDesc, editTextFinishBy; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add_task); editTextTask = findViewById(R.id.editTextTask); editTextDesc = findViewById(R.id.editTextDesc); editTextFinishBy = findViewById(R.id.editTextFinishBy); findViewById(R.id.button_save).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { saveTask(); } }); } private void saveTask() { final String sTask = editTextTask.getText().toString().trim(); final String sDesc = editTextDesc.getText().toString().trim(); final String sFinishBy = editTextFinishBy.getText().toString().trim(); if (sTask.isEmpty()) { editTextTask.setError("Task required"); editTextTask.requestFocus(); return; } if (sDesc.isEmpty()) { editTextDesc.setError("Desc required"); editTextDesc.requestFocus(); return; } if (sFinishBy.isEmpty()) { editTextFinishBy.setError("Finish by required"); editTextFinishBy.requestFocus(); return; } class SaveTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... voids) { //creating a task Task task = new Task(); task.setTask(sTask); task.setDesc(sDesc); task.setFinishBy(sFinishBy); task.setFinished(false); //adding to database DatabaseClient.getInstance(getApplicationContext()).getAppDatabase() .taskDao() .insert(task); return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); finish(); startActivity(new Intent(getApplicationContext(), MainActivity.class)); Toast.makeText(getApplicationContext(), "Saved", Toast.LENGTH_LONG).show(); } } SaveTask st = new SaveTask(); st.execute(); } }
- the above code is very simple, we created an AsyncTask to perform our operation because if we will try to perform the database operation in the main thread it will crash our application.
- For saving the task we just created the object and called the insert method that we created in our TaskDao
- You can try testing this but first, you need to open this activity and for this, you need to code the open functionality in the MainActivity. So what you have to do to test it is, attach a click listener on the add button that we have created in MainActivity and open AddTaskActivity when the add button is clicked.
Reading All Tasks
Now we will read the saved task from the database, and we will display it on the RecyclerView.
RecyclerView Adapter
- Before going further, let’s first create our adapter. So create a class named TasksAdapter and write the following code
package com.codeplayon.myapp; import android.content.Context; import android.content.Intent; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; public class TasksAdapter extends RecyclerView.Adapter<TasksAdapter.TasksViewHolder> { private Context mCtx; private List<Task> taskList; public TasksAdapter(Context mCtx, List<Task> taskList) { this.mCtx = mCtx; this.taskList = taskList; } @Override public TasksViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(mCtx).inflate(R.layout.recyclerview_tasks, parent, false); return new TasksViewHolder(view); } @Override public void onBindViewHolder(TasksViewHolder holder, int position) { Task t = taskList.get(position); holder.textViewTask.setText(t.getTask()); holder.textViewDesc.setText(t.getDesc()); holder.textViewFinishBy.setText(t.getFinishBy()); if (t.isFinished()) holder.textViewStatus.setText("Completed"); else holder.textViewStatus.setText("Not Completed"); } @Override public int getItemCount() { return taskList.size(); } class TasksViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { TextView textViewStatus, textViewTask, textViewDesc, textViewFinishBy; public TasksViewHolder(View itemView) { super(itemView); textViewStatus = itemView.findViewById(R.id.textViewStatus); textViewTask = itemView.findViewById(R.id.textViewTask); textViewDesc = itemView.findViewById(R.id.textViewDesc); textViewFinishBy = itemView.findViewById(R.id.textViewFinishBy); itemView.setOnClickListener(this); } @Override public void onClick(View view) { Task task = taskList.get(getAdapterPosition()); Intent intent = new Intent(mCtx, UpdateTaskActivity.class); intent.putExtra("task", task); mCtx.startActivity(intent); } } }
- In the above code, we also attached a click listener to our itemView so that we can fire an event when a task is clicked. When a task is clicked we are opening the UpdateTaskActivity and we are also passing the selected task using intent.
Reading Tasks
- Come inside MainActivity and write the following code.
package com.codeplayon.myapp; import android.arch.persistence.room.Room; import android.content.Intent; import android.os.AsyncTask; import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.widget.Toast; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private FloatingActionButton buttonAddTask; private RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = findViewById(R.id.recyclerview_tasks); recyclerView.setLayoutManager(new LinearLayoutManager(this)); buttonAddTask = findViewById(R.id.floating_button_add); buttonAddTask.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity.this, AddTaskActivity.class); startActivity(intent); } }); getTasks(); } private void getTasks() { class GetTasks extends AsyncTask<Void, Void, List<Task>> { @Override protected List<Task> doInBackground(Void... voids) { List<Task> taskList = DatabaseClient .getInstance(getApplicationContext()) .getAppDatabase() .taskDao() .getAll(); return taskList; } @Override protected void onPostExecute(List<Task> tasks) { super.onPostExecute(tasks); TasksAdapter adapter = new TasksAdapter(MainActivity.this, tasks); recyclerView.setAdapter(adapter); } } GetTasks gt = new GetTasks(); gt.execute(); } }
- The code is very simple, we just called the getAll() method that we created inside our TaskDao to get all the stored tasks from the database.
- Then we are displaying the read tasks to a RecyclerView.
Updating and Deleting Task
Now finally the Update and Delete operation we will perform in the UpdateTaskActivity.
- Open UpdateTaskActivity and write the following code.
package com.codeplayon.myapp; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.CheckBox; import android.widget.EditText; import android.widget.Toast; public class UpdateTaskActivity extends AppCompatActivity { private EditText editTextTask, editTextDesc, editTextFinishBy; private CheckBox checkBoxFinished; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_update_task); editTextTask = findViewById(R.id.editTextTask); editTextDesc = findViewById(R.id.editTextDesc); editTextFinishBy = findViewById(R.id.editTextFinishBy); checkBoxFinished = findViewById(R.id.checkBoxFinished); final Task task = (Task) getIntent().getSerializableExtra("task"); loadTask(task); findViewById(R.id.button_update).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(getApplicationContext(), "Clicked", Toast.LENGTH_LONG).show(); updateTask(task); } }); findViewById(R.id.button_delete).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AlertDialog.Builder builder = new AlertDialog.Builder(UpdateTaskActivity.this); builder.setTitle("Are you sure?"); builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { deleteTask(task); } }); builder.setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { } }); AlertDialog ad = builder.create(); ad.show(); } }); } private void loadTask(Task task) { editTextTask.setText(task.getTask()); editTextDesc.setText(task.getDesc()); editTextFinishBy.setText(task.getFinishBy()); checkBoxFinished.setChecked(task.isFinished()); } private void updateTask(final Task task) { final String sTask = editTextTask.getText().toString().trim(); final String sDesc = editTextDesc.getText().toString().trim(); final String sFinishBy = editTextFinishBy.getText().toString().trim(); if (sTask.isEmpty()) { editTextTask.setError("Task required"); editTextTask.requestFocus(); return; } if (sDesc.isEmpty()) { editTextDesc.setError("Desc required"); editTextDesc.requestFocus(); return; } if (sFinishBy.isEmpty()) { editTextFinishBy.setError("Finish by required"); editTextFinishBy.requestFocus(); return; } class UpdateTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... voids) { task.setTask(sTask); task.setDesc(sDesc); task.setFinishBy(sFinishBy); task.setFinished(checkBoxFinished.isChecked()); DatabaseClient.getInstance(getApplicationContext()).getAppDatabase() .taskDao() .update(task); return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Toast.makeText(getApplicationContext(), "Updated", Toast.LENGTH_LONG).show(); finish(); startActivity(new Intent(UpdateTaskActivity.this, MainActivity.class)); } } UpdateTask ut = new UpdateTask(); ut.execute(); } private void deleteTask(final Task task) { class DeleteTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... voids) { DatabaseClient.getInstance(getApplicationContext()).getAppDatabase() .taskDao() .delete(task); return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Toast.makeText(getApplicationContext(), "Deleted", Toast.LENGTH_LONG).show(); finish(); startActivity(new Intent(UpdateTaskActivity.this, MainActivity.class)); } } DeleteTask dt = new DeleteTask(); dt.execute(); } }
- In the above code, again we are doing the same thing, we are calling the methods we created inside TaskDao using an AsyncTask.
- Now the app is complete you can try running it.