During Google I/O 2017, Google introduced the Android Architecture Components – a collection of libraries that helps developers creating applications. It includes:
- Room – SQLite object mapping library,
- LiveData – data holder class that allows to observe stored values,
- ViewModel – configuration changes proof class to store data,
- LifecycleObserver and LifecycleOwner – classes and interfaces to build lifecycle-aware components.
The next two parts of this article will include an introduction to the first library mentioned above – Room. Firstly, I’ll show you how to configure project, model entities and create DAOs. In the next parts, we will bring together Room, RxJava and the rest of Android Architecture Components libraries in order to create more flexible and maintainable app.
Room gives us a great alternative for standard, boilerplate SQLite operations. Room isn’t an ORM such as for example GreenDAO. It only provides extra layer over SQLite. The main benefits of Room are:
- compiler-time verification of SQL queries,
- there’s no need to write boilerplate code to convert SQL queries and Java data objects.
While developing app with Room, we need to implement all of three main components:
Communication between these three components is shown on figure below:
Since we know what is Room and what does it consist of, it’s time to start creating our application. In this tutorial we’ll try to create an application for saving our tasks and setting reminders to them. For every task we are going to save some reminders with different priorities, because there will be only one reminder notification per minute. So, after this short introduction – let’s start coding!
Adding Android Architecture Components libraries
Firstly, Room isn’t a part of Android environment, so we need to add a dependency in gradle. Android Architecture Components are available on the Maven repository. To add it to your project, open your project build.gradle and paste the maven repository url:
Open app build.gradle and paste the latest* Room dependencies in dependencies section:
Now, synchronise your project and voilà – you can use Room.
- Latest versions of all Android Architecture Components can be found here.
Before we get started with modelling our entities, we need to know some useful annotations and their attributes.
- @Entity – every model class with this annotation will have a mapping table in DB
- foreignKeys – names of foreign keys
- indices – list of indicates on the table
- primaryKeys – names of entity primary keys
- @PrimaryKey – as its name indicates, this annotation points primary key of the entity:
- autoGenerate – if set to true, then SQLite will be generating a unique id for the column
- @ColumnInfo – allows to specify custom informations about column
- @Ignore – field will not be persisted by Room
- @Embeded – nested fields can be referenced directly in the SQL queries.
Now we can start coding. First of all, we need to model three classes – User, Task and Reminder as shown below:
Secondly, we can create the User class.
Let’s auto generate ids for each user. Set the autogenerate property to true. We also allow users to create one account per email For this we’ve added @Index annotation and unique = true property.
Next, let’s create Task class.
We’ve got an autogenerated primary key. However, the most important is the declaration of @ForeignKey. It allows us to specify, what to do with all user tasks, when the user deletes his account.
Now, we need to create Reminder class, which is very similar to Task, except the new annotation – @TypeConverters. This annotation allows us to convert our custom data types to types which Room understands – all primitives and their boxed alternatives.
Converter class is just a simple class containing all converters. In our example it’s Date to long and vice-versa.
Room does not allow to add object references between entities (for more see: https://developer.android.com/topic/libraries/architecture/room.html#no-object-references). A nice workaround is to create additional classes for resolving relations In our case these are user tasks and task reminders. We’ll use two annotations – @Embedded and @Relation.
As I mentioned before – @Embedded allows nested fields to be referenced directly in the SQL queries. @Relation describes relations between two columns. We have to specify the parent column name, entity column name and entity class. We don’t use @Entity annotation here. In the next step we will use these classes to fetch data in our DAOs.
After hard work with entities, we can do something easier. Writing DAOs is really simple if we know the SQL syntax. I’ll show you only UserDao for brevity, because Task and Reminder DAOs are very similar.
Every DAO interface must have @Dao annotations. For making queries we can use one of bellows:
- @Insert – inserts our entity class. We can insert single entity, array or list of longs passed by arguments. Method can return inserted ids as long, array or list of longs. This annotation provides one parameter:
- onConflict – specifies what to do, if UNIQUE constraint failed (REPLACE, ROLLBACK, ABORT – default, FAIL, IGNORE).
- @Update – updates entity, have onConflict as @Insert.
- @Delete – deletes entity.
- @Query – executes query passed by parameter. If we make SELECT, method can return a single entity or array / list of entities.
We are now using relations class – UserAllTasks. Due to the fact that we specify relations, we can get user and all of his tasks in a single query.
Creating a DB
Finally, the last stage – creating a db (even easier than creating DAOs). All we need to do is to create an abstract class, which extends RoomDatabase and annotate this class by @Database. Furthermore, we have to pass which entities we want to use, specify the version of our db, create methods to return DAOs and build database. All of these steps are shown below:
In the code above, I’ve implemented a simple singleton, but better solution is to inject database by Dagger or different DI library.
NOTE: Two marked methods are added temporarily. The fallbackToDestructiveMigration() to avoid writing migrations and the allowMainThreadQueries() to allow making queries in main thread. Ignoring this method will cause an exception (if query will be executed on main thread). In the next part of this Android Architecture Components tutorial, I’ll show you how to write migrations and integrate Room with RxJava to avoid main thread queries.
It’s just a simple example how to use our db now:
The first part of articles about Android Architecture Components is a simple example of Room. Next time we’ll use Room, LiveData, RxJava and ViewModel at once to create “robust, testable, and maintainable app”. If you have any questions feel free to ask in comments.
See the Android Data Binding tutorial to learn more about Android Architecture Components.
- Room tutorial:
- Modelling relations:
- Room — Getting Started by Tony Owen: