Chapters

Hide chapters

Saving Data on Android

First Edition · Android 10 · Kotlin 1.3 · AS 3.5

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Using Firebase

Section 3: 11 chapters
Show chapters Hide chapters

5. Room Architecture
Written by Aldo Olivares

In the previous section, you learned all the basics behind data storage on Android. You learned how to work with permissions, shared preferences, content providers and SQLite.

Shared preferences are very useful when you need to store and share simple data, such as user preferences as key-value pairs. The main drawback is that you can’t store large amounts of data since it’s not efficient and there’s no way to use queries to retrieve information.

SQLite is a fast, lightweight local database natively supported by Android that allows you to store large amounts of data in a structured way. The only downside of SQLite is that its syntax is not very intuitive since the way to interact with it can be very different from platform to platform.

Therefore, in this chapter, you are going to learn about one of the most popular libraries that helps you simplify your interaction with SQLite: Room.

Along the way, you will also learn:

  • How Object Relational Mappers work.
  • About Room’s integration with Google’s architecture components
  • The basics behind entities, DAOs and Room databases.
  • The advantages and disadvantages of Room.
  • The app you are going to build in the rest of this section.

Let’s dive in!

Object Relational Mappers

Before using Room properly in your projects, you first need to learn what Room is.

Room is a data persistence library commonly known as Object Relational Mapper or ORM. ORMs are tools that allow you to retrieve, delete, save and update the contents of a relational database using the programming language of your choice.

ORMs are implemented as libraries or frameworks that provide an additional layer of abstraction called the data layer which allows you to better interact with your database using a syntax similar to the object-oriented world.

To better understand how ORMs work, imagine that you have a Movie class with three properties: An id, a name and a release_date.

This is a class diagram that represents the Movie class. Just like most object-oriented languages, each of these properties has a certain data type such as Int, String or Date

With the help of an ORM, this Movie class can easily be used to create a new table in your database. In an ORM, classes represent a table inside your database and each property a column. For example, our previous Movie class would be translated into a table like this:

Each of the columns would also have the data type that best represents the original data type of the original property. For example, a String would be translated as a varchar and an Integer as an Int.

The way to create new records inside the tables differs from each implementation. For instance, some ORMs automatically create new entries each time a new instance of the class is created. Other ORMs such as Room use Data Access Objects or DAOs to query your tables.

This is a simple example of how you would use a DAO in Room to create new Movie records in the previously mentioned table:

movieDao.insert(Movie(1, "Harry Potter", "10-11-05"))
movieDao.insert(Movie(2, "The Simpsons", "03-10-02"))
movieDao.insert(Movie(3, "Avengers", "08-01-10"))

And your table now look like this:

Easy, right?

Note: Room can autogenerate the primary key, in this case, ID. You will learn how to do that in a later chapter.

Now, let’s take a look at how Room and Google’s architecture components work and interact with each other.

Room and Google’s architecture components

Android has been around for quite some time. In the beginning, Android apps were very simple and most performed trivial tasks, including calculators, calendars and to-do lists. But things have changed and mobile apps are more complex than ever. Now, there are media players, social networks, chat apps and even fast-paced 3D games.

As apps became more complex, the code followed suit. Developers adopted their own practices on how to develop the architecture of their apps. Some programmers preferred to use an MVVM architecture with SugarORM while others preferred to use MVP with greenDAO and Firebase. This led to confusion since there was no recommended or official way of doing things.

Therefore, at the 2018 I/O conference Google announced Jetpack, a set of libraries focused on creating a robust architecture for your apps by eliminating boilerplate code and simplifying complex tasks such as database interactions and background tasks.

Jetpack is focused on four areas of Android Development:

  • Architecture contains components that allow you to create robust apps that are scalable, maintainable and easy to test. It includes libraries such as ViewModel, LiveData, WorkManager and DataBinding.
  • UI contains widgets, helpers, animation, transition and utility components that allow you to design apps with a good, easy-to-use interface.
  • Foundation provides components that bring backwards compatibility with other Android tools and libraries. It includes Android KTX and AppCompat.
  • Behavior helps your app integrate with Android’s native services, such as permissions and notifications. It includes the Download Manager, Media and Preferences APIs.

At this point you might be wondering…What does all of this have to do with Room?

Well, Room is part of the architecture components previously mentioned and it is Google’s ORM meant to replace other libraries such as greenDao or SugarORM.

An app built with Room and Google’s architecture components usually relies on a set of components you’re going to see in detail in the following sections.

Database

On a device, the data is stored on a local SQLite database. Room provides an additional layer on top of the usual SQLite APIs that avoids having to create a lot of boilerplate code using the SQLiteOpenHelper class.

For instance, suppose you want to create a simple database that stores a question table for a quiz app, like the one you are going to be building in the next chapter. A traditional implementation using the standard SQLite APIs would look something like this:

private const val SQL_CREATE_ENTRIES =
        "CREATE TABLE question (" +
                "question_id INTEGER PRIMARY KEY," +
                "text TEXT"

private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS question"

class QuizDbHelper(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DATABASE_VERSION) {
    companion object {
        const val DATABASE_VERSION = 1
        const val DB_NAME = "question_database.db"
    }
    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(SQL_CREATE_ENTRIES)
    }
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        db.execSQL(SQL_DELETE_ENTRIES)
        onCreate(db)
    }
    override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        onUpgrade(db, oldVersion, newVersion)
    }
}

On the other hand, with Room the code becomes much more concise and easier to understand:

@Database(entities = [(Question::class)], version = 1)
abstract class QuestionDatabase : RoomDatabase() {
  abstract fun questionsDao(): QuestionDao
}

As you can see, the amount of boilerplate code was drastically reduced since Room handles most of the database interaction for you under the hood.

Entities

Entities in Room represent tables in your database and are usually defined in Kotlin as data classes. Let’s take a look at the following Entity that is used to define a question table:

@Entity(tableName = "question") //1
data class Question(
    @PrimaryKey //2
    @ColumnInfo(name = "question_id") //3
    var questionId: Int,
    @ColumnInfo(name = "text")
    val text: String)

Room gives you many annotations that allow you to define how your data class is going to be translated into an SQLite table.

Taking each commented section in turn:

  1. The @Entity annotation tells Room that this data class is an Entity. You can use many different parameters to tell Room how this Entity is going to be translated into an SQLite table. In this case, you are using the tableName parameter to define the name for the table.
  2. The @PrimaryKey annotation is mandatory and each Entity must have at least one field annotated as the primary key. You could also use the primaryKeys parameter inside your @Entity annotation to define your primary key.
  3. The @ColumnInfo annotation is optional but very useful since it allows specific customization for your column. For example, you can define a custom name or change the data type.

DAOs

DAO stands for Data Access Object. DAOs are interfaces or abstract classes that Room uses to interact with your database using annotated functions. Your DAOs will typically implement one or more CRUD operations on a particular Entity.

The code for a DAO that interacts with the question Entity defined above would look like this:

@Dao //1
interface QuestionDao {
    
  @Insert(onConflict = OnConflictStrategy.REPLACE) //2
  fun insert(question: Question)

  @Query("DELETE FROM question") //3
  fun clearQuestions()

  @Query("SELECT * FROM question ORDER BY question_id") //4
  fun getAllQuestions(): LiveData<List<Question>>

}

Step-by-Step:

  1. The DAO annotation marks this interface as a Data Access Object. DAOs should always be abstract classes or interfaces since Room will internally create the necessary implementation at compile time for you according to the query methods that you provide.
  2. The @Insert annotation declares that this method is going to perform a create operation by performing an INSERT INTO query using the object that you received as a parameter to create a new record.
  3. The @Query annotation executes the query passed as a parameter. In this case, you are performing a delete operation by executing a DELETE FROM query.
  4. This is another @Query annotated method that retrieves all the questions from your database.

Don’t worry if something does not make sense right now. You will be learning much more about DAOs in the The DAO Pattern chapter.

Repository

This class acts as a bridge between your data sources and your app. The repository class handles the interaction with your Room database and other backend endpoints such as web services and Open APIs.

ViewModel

Just like the Repository acts as a bridge between your data sources and your app, the ViewModels act as a bridge between your repository and your user interface. The ViewModel communicates the data coming from your Repository to your Views and has the advantage of surviving configuration changes since it’s lifecycle-aware.

LiveData

LiveData is a data holder class that implements the Observer pattern. This means it can hold information and be observed for changes. Your views such as Fragments or Activities observe LiveData objects returned from your ViewModels and update the relevant widgets as needed.

The interaction between the above components can be illustrated by the following diagram:

You will be learning much more about the above components in the upcoming chapters, , this is all you need to know.

Room advantages and concerns

Room uses a local SQLite database to store your data. Therefore, most of the advantages and disadvantages of SQLite apply to Room as well.

These are some of the main advantages of using SQLite to store data:

  • Portability: SQLite is available for most platforms and can be used with many popular programming languages such as Java, Kotlin and Python.
  • Lightweight: SQLite is probably the most lightweight database out there, making it suitable for devices with low memory, like smartphones.
  • Good Performance: Compared to reading directly from a file, Sqlite is much faster since you only load the data that you need rather than reading and holding an entire file in memory.

The main disadvantage of using SQLite is that, since it relies on local file storage, your data will be lost if the user decides to delete the app’s data. It is usually recommended that you have a backup on a remote database or service such as Firebase.

Frequently asked Room questions

Are ORMs really necessary? Can’t I just use plain old SQLite?

Of course you can use plain old SQLite! In fact, Android standard libraries include many utilities and classes that help you work directly with SQLite. The only downside with this approach is that you often have to deal with a lot of boilerplate code that can slow down your development.

Are there other ORMs for Android besides Room?

Sure! There many ORMs out there like greenDao or SugarORM.

What are the advantages of using Room vs other ORMs?

The main advantage of using Room vs ORMs is that Room offers the best integration with other architecture components like ViewModel and LiveData and since it is developed by Google you can be sure this library will be maintained and improved for a very long time to come.

Your app

This chapter has been full of theory and concepts and you are probably wondering when you are actually going to start writing some code.

Well, the rest of the following chapters are going to be focusing solely on how to apply the previously mentioned concepts to build a fun quiz app called DroidQuiz. This app will allow your users to test their Android knowledge with a set of questions stored in a Room database:

DroidQuiz will help you learn about many important concepts behind Room:

  • How to add the appropriate dependencies to your build.gradle file for Room and most of the architecture components such as LiveData.
  • How to create a local SQLite database using Room.
  • How to use Database Access Objects or DAOs to interact with your Database.
  • How to use Google’s Android architecture components such as LiveData and ViewModel to interact with your Room database.
  • How to create indices and relationships between your tables.
  • How to test your database, migrations, and ViewModels.
  • And much more!!!

As you can see, there is a lot to learn, but this section will guide you through every, single step needed to build a final version of the app.

Key points

  • Room is an ORM developed by Google as a part of Jetpack’s architecture components to simplify the interaction with your SQLite database and to reduce the amount of boilerplate code.

  • Entities in Room represent tables in your database.

  • DAO stands for Data Access Object.

  • The Repository class handles the interaction with your Room database and other backend endpoints.

  • The ViewModel communicates the data coming from your repository to your views and has the advantage of surviving configuration changes since it’s lifecycle-aware.

  • LiveData is a data holder class that can hold information and be observed for changes.

  • ORM stands for Object Relational Mapper.

  • Shared preferences are very useful when you need to store and share simple data such as user preferences as key-value pairs.

  • The main disadvantage of using shared preferences is that you can’t store large amounts of data since it’s not efficient and there’s no way to use queries to search for information.

  • SQLite is a fast and lightweight local database natively supported by Android that allows you to store large amounts of data in a structured way.

  • SQLite is available for most platforms and can be used with many popular programming languages.

  • Because SQLite is lightweight, it’s suitable for devices with restricted memory such as smartphones and smart TVs.

  • ORMs provide an additional layer of abstraction that allows you to interact with your relational database with an Object-Oriented Language syntax.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.