A Beginner’s Guide to Android Room: A Comprehensive Tutorial with Code Examples
Android Room is a library that simplifies database access by providing an additional layer on top of SQLite. This allows developers to interact with the database in a more robust manner while still taking advantage of the full capabilities of SQLite. In this blog post, we’ll explore the basics of Room using Java and provide some code examples that use a ListView to display data.
What is Room?
Room is part of the Android Architecture Components, a collection of libraries that help you design robust, testable, and maintainable apps. Room provides an abstraction layer over SQLite, making it easier to work with databases in your app.
With Room, you can define your database schema using annotations in your data classes, and Room will generate all the necessary code to create and maintain the database. This means you don’t have to write any SQL code yourself, making it easier to work with databases and reducing the risk of errors.
Getting Started with Room
To get started with Room, you’ll need to add the necessary dependencies to your app’s build.gradle file:
dependencies {
implementation "androidx.room:room-runtime:2.4.0"
annotationProcessor "androidx.room:room-compiler:2.4.0"
}
Once you’ve added the dependencies, you can start defining your database schema. In Room, you do this by creating data classes and annotating them with @Entity
. Here’s an example of a simple User
class that represents a row in a users
table:
package com.example.androidroomexample.entity;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "users")
public class User {
@PrimaryKey(autoGenerate = true)
public int id;
public String firstName;
public String lastName;
public User(String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
}
In this example, we’ve defined a User
class with three properties: id
, firstName
, and lastName
. The @Entity
annotation tells Room that this class represents a table in the database, and the tableName
parameter specifies the name of the table.
The @PrimaryKey
annotation on the id
property tells Room that this property is the primary key for the table. Every entity must have a primary key, and it must be unique for each row in the table.
Once you’ve defined your entities, you can create a database class that extends RoomDatabase
. This class defines the database configuration and serves as the main access point for the underlying connection to your app’s persisted data. Here’s an example:
package com.example.androidroomexample;
import androidx.room.Database;
import androidx.room.RoomDatabase;
import com.example.androidroomexample.dao.UserDao;
import com.example.androidroomexample.entity.User;
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
In this example, we’ve created an AppDatabase
class that extends RoomDatabase
. The @Database
annotation tells Room which entities are part of the database and specifies the database version. If you make changes to your schema in the future, you’ll need to increment the version number and provide a migration strategy.
The userDao()
method is an abstract method that returns an instance of UserDao
. This is a data access object (DAO) that defines the methods for accessing the data in the users
table. We’ll look at how to define a DAO in the next section.
Defining a DAO
A DAO is an interface or abstract class that defines methods for accessing your app’s data. These methods can include queries, inserts, updates, and deletes. Here’s an example of a simple UserDao
that defines methods for inserting users and querying all users:
package com.example.androidroomexample.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import com.example.androidroomexample.entity.User;
import java.util.List;
@Dao
public interface UserDao {
@Query("SELECT * FROM users")
List<User> getAll();
@Insert
void insertAll(User ...users);
}
In this example, we’ve defined two methods: getAll()
and insertAll()
. The @Query
annotation on the getAll()
method specifies the SQL query to run when this method is called. In this case, we’re selecting all rows from the users
table.
The @Insert
annotation on the insertAll()
method tells Room that this method should insert rows into the database. The method takes a variable number of User
objects as arguments and inserts them into the database.
Using Room in Your App
Now that we’ve defined our entities, database, and DAOs, we can start using Room in our app. To do this, we need to create an instance of our database class using the Room.databaseBuilder()
method:
AppDatabase db = Room.databaseBuilder(
getApplicationContext(),
AppDatabase.class,
"database-name"
).build();
Once we have an instance of our database, we can use it to access our DAOs and perform operations on our data. Here’s an example of how we might insert some users into the database and then query all users:
UserDao userDao = db.userDao();
userDao.insertAll(
new User(1, "Alice", "Smith"),
new User(2, "Bob", "Johnson"),
new User(3, "Charlie", "Williams")
);
List<User> users = userDao.getAll();
In this example, we first get an instance of our UserDao
from the database. Then we use the insertAll()
method to insert some User
objects into the database. Finally, we use the getAll()
method to query all users from the database.
Displaying Data with a ListView
Now that we know how to use Room to access our data, let’s look at how we can display that data in a ListView. A ListView is a view that shows a list of items in a vertically scrolling list. To use a ListView, you need to define an XML layout that includes a ListView.
Here’s an example of an XML layout that includes a ListView:
<?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="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
In this example, we’ve defined a simple LinearLayout that contains a ListView. The android:id
attribute specifies the ID of the ListView, which we’ll use to reference it in our code.
Now, let’s see the complete code of MainActivity.java
package com.example.androidroomexample;
import androidx.appcompat.app.AppCompatActivity;
import androidx.room.Room;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.example.androidroomexample.dao.UserDao;
import com.example.androidroomexample.entity.User;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity {
private ExecutorService executor = Executors.newSingleThreadExecutor(); // Executor for background tasks
private Handler mainHandler = new Handler(Looper.getMainLooper()); // Handler for updating UI on main thread
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize ListView and ArrayAdapter
listView = findViewById(R.id.listView);
ArrayAdapter adapter = new ArrayAdapter(
this,
android.R.layout.simple_list_item_1);
listView.setAdapter(adapter);
// Fetch and display user names
fetchAndDisplayNames();
}
private void fetchAndDisplayNames(){
executor.submit(new Runnable() {
@Override
public void run() {
// Build the Room database
AppDatabase db = Room.databaseBuilder(
getApplicationContext(),
AppDatabase.class,
"usersDB"
).build();
// Get UserDao instance
UserDao userDao = db.userDao();
// Insert sample users into the database
userDao.insertAll(
new User("Alice", "Smith"),
new User("Bob", "Johnson"),
new User("Charlie", "Williams")
);
// Retrieve all users from the database
List<User> users = userDao.getAll();
// Process user names for display
ArrayList<String> fullNameList = new ArrayList<>();
for(int i = 0; i < users.size(); i++){
String fullName = users.get(i).firstName + " " + users.get(i).lastName;
fullNameList.add(fullName);
}
// Update UI with user names on the main thread
mainHandler.post(new Runnable() {
@Override
public void run() {
ArrayAdapter adapter = (ArrayAdapter) listView.getAdapter();
adapter.addAll(fullNameList);
}
});
}
});
}
}
This code creates an ExecutorService
and a Handler
for performing background tasks and updating the UI on the main thread, respectively. It also initializes a ListView
and an ArrayAdapter
for displaying data.
In the fetchAndDisplayNames
method, a new thread is submitted to the executor to perform the database operations. The Room database is built using the Room.databaseBuilder
method, and a UserDao
instance is obtained from the database. Sample users are inserted into the database using the insertAll
method of the UserDao
, and all users are retrieved from the database using the getAll
method.
The user names are processed for display by concatenating their first and last names, and the resulting list of full names is passed to the UI thread using the Handler
. The ArrayAdapter
is updated with the new data, causing the ListView
to display the user names.
Output:
Conclusion
In this blog post, we’ve explored the basics of Android Room and provided some code examples to help you get started. Room is a powerful persistence library that makes it easier to work with databases in your app. By defining your schema using annotations and letting Room generate the necessary code, you can reduce the risk of errors and make it easier to maintain your app’s data.