Skip to main content

Basics of Room Persistence Library in Android #RP1



Room is a library built on top of SQLite that provides a more convenient and structured way to manage your app's local database. It lets you work with your data as Kotlin objects (data classes) instead of manually formulating them using SQL queries, which itself is a tedious task prone to many errors. 

It also simplifies interactions with the database with many great features like validating the queries, seamless integration with Kotlin Flow, paging, LiveData, and many more. These features make Room the most preferred option for offline-first apps, as it expedites the process and helps us to avoid issues early in the development cycle.

Since Google has the best article for setting up, I will be skipping the setup

Dependency setup: Official Guide on Setting up Room Dependencies and plugins 

Please do ensure you follow the above guide and then sync and build your project successfully, as it will help you to try out the components yourself. In this article, my intention is to explore the key components of Room, a bare minimum you need to know to implement a simple offline-first app. For a detailed and more complex use case, check out my upcoming article.

Key Components

Primarily, there are three important components: Entity, DAO's, and Database, The entity represent a table. Dao's are an interface to write queries and define your operations. The database is where you associate your entities and include the dao's you would like to access from outside; hence, it's the entry point of your database. Let's check it out one by one:

Entity

Any class annotated with @Entity is an entity, and those classes represent a table in the database. Here is an example of what a simple entity would look like:

An entity must have a minimum of one parameter that is annotated with @PrimaryKey(), and it would be the primary key for uniquely identifying a single row; the value of the parameter must be unique; otherwise the app will crash.

You can set the autogenerate to true just like the above code snippet. Ensure the type is primitive; I prefer Long, but if you are expecting very few entries, you can use Short; otherwise, you decide it according to your needs. If you have no clue, then stick with Long.

The entity can be further modified with a custom name for the table, indexing, custom column names, etc. We can discuss those in the upcoming articles. 

DAOs

Short for Data Access Object, as the name suggests, this is the interface between the developer and Room for accessing the tables in a safe and simplified way. It's basically an interface with an annotation with @Dao; inside you can use Room query annotations to access and modify the tables as per your requirements. Here is a sample code snippet of how a DAO for our case will look like:

As you can see in the above snippet, Room simplifies the process of implementing an offline-first app. Though it looks simple, don't assume that Room is only for simple use cases; I have kept it minimal to not overcomplicate it. We will definitely go through the complex use cases in the upcoming articles.

The interface annotated with @Dao is typically called Dao's; here our Dao is called HabitTrackerDao.''It is preferred to name your Dao with a postfix 'Dao.'.

I would highly recommend you access only one entity (table) in a single DAO, but Room does not restrict you from accessing multiple entities (tables) in the same DAO, but trust me, this will be helpful as your product grows. We will talk about how to handle the complex cases in the next article on Room.

 Do note There is no restriction on the number of Dao's you can have; just ensure the names don't conflict with each other, and the minimum target can handle it (no need to worry if your entities are less than 50).

Database 

Now we have tables or entities and the DAOs that enable us to access and interact with the tables. Now, there can be multiple databases in an Android app, so how does Room know that the entity and DAO are part of which database? That's exactly what we are going to define next.

Take a look at the following code snippet of our simple database:

This might be a little tricky, but it's pretty simple. We are creating an abstract class that extends the RoomDatabase and also annotating it with @Database to represent the class as a database and letting the room compiler know that this is one of the databases and the declared entities inside the array are to be associated with this database, and this serves as an entry point to the database.

The entities array as of now has only one entity for simplicity, so don't forget to add all the entities in this array. Another thing to note is that whenever you are modifying the entities, ensure you upgrade the version of the database to avoid a crash. The reason for this crash is that Room does not know how to handle the schema changes unless a migration strategy is provided; we will talk about this in the next article. In a real-world application, the table could contain user data, and we usually want to preserve this data during schema updates, making migrations a crucial part of database version management.

Manually Creating a Database and Interacting

Hooray! Finally, we are here. You can build it for accessing the database and interacting with it easily. Do check out the code below; it shows a simple manual creation of the database. 

We are using the DatabaseBuilder function from the Room Companion object to build our database; the result of the build is our own implementation of the requested database, with which we can get the implementation of our DAO's from Room and do our interactions... 

Do ensure you don't use the UI thread for accessing the database, as it can cause issues; otherwise, you should not have any other issue, but if you face any issue, feel free to start a discussion below:

This is a simple implementation, but a typical app developer would use a DI. Whether to use DI or not is your decision,, depending on the scale of your project. but I will be using it in the final project after this series of articles.

Thanks for reading so far. While this already feels like a lot, these could be enough for your first simple projects, but there are a lot of other interesting cases we have not yet talked about, like @RawQuery, Junctions, TableViews, and many more, which we shall discuss in the next article.

feel free to share your feedacks and suggestions below

Comments

Popular posts from this blog

Implementing In-app and pre-locale language selection in Android

Modern Android App Architecture One of the essential features for a globalized app is the ability to provide users with the option to choose their preferred language within the application. In this blog post, we will guide you through the process of implementing in-app language selection in an Android app. Step 1: Create Locale String Resource Create a string resource file for each respective locale by navigating to res > new > Android Resource File . Select Locale and create a string.xml file. Copy and paste the translations of your app content into these files. Step 2: Configure Locale Preferences In the res/xml folder, create a new file called locales_config.xml and specify the locales of your choice. for example look at the following code: <?xml version="1.0" encoding="utf-8"?> <locale-config xmlns:android="http://schemas.android.com/apk/res/android"> <locale andro...

Chocolate Feast - Problem Solving - Hacker Rank Solution.

The expectation is to find the total number of choclate one can consume by taking full advantage of the offer, Here there are 3 inputs n which holds the value of initial amount of money for buying choclate, c is the cost price of each candy if paid by cash and m is the exchange rate for the candy. Inputs n Initial cash to buy candy. c Coast of each candy if paid by cas.h m Exchange rate for a new candy in offer. The initial count of choclate will be the cash / coast and the wrappers in hand will be the same value of choclate, and from there we loop through until the wrap count is less than the exchange rate, inside the loop the choclate count will still hold the same fourmula as before but divided with exchange rate. The wrap count is the tricky part... the wrap will be wrap/ exchange rate(the no. choclate) + the remainder of this division(THIS IS VERY IMPORTANT) because for example if the count of wrapper is 3 and the exchange rate is 2 you can only buy 1 c...

Search Textfield with options and clear button ideal for app TopBar- Jetpack Compose Component

In this blog post, we'll explore a simple yet effective implementation of a search bar in Jetpack Compose. The provided SearchTextField composable offers a basic text field with a placeholder, suitable for building the app top bar. Screen Shot of the Component from my Jot-app The following code can be used to your project directly and it should work as expected, the idea was to have a search bar for a top app which can also hst the buttons on either ends,do check the code below If you have any suggestions or improvements for the code, let's have a conversation below. Your feedback is valuable to me