Skip to main content

Prepopulate Room with data.


There are times, when we just need the data when the app starts, and all the functionality is just a matter of work. Or we just need the app to be independent from the network and we have the data. A simple dog-race database or cat-race database doesn't actually need online interaction at all (if there are not too many data of course). So, Room comes with a nice solution about this. The docs are pretty clear and short when it comes to this topic.
We just write:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
    .createFromAsset("database/myapp.db")
    .build()
And our data are ready to be instantiated when the app starts. One more thing to note is that its extremely fast to do it this way. However, here are some problems that the docs don't even bother to mention (which I think are important for some).

1 - A .db file, is not a .sql file.

The file we are supposed to hold in assets folder which contains a query like: CREATE TABLE IF NOT EXISTS has nothing to do with the file in the code above, that we are supposed to import. We need to create a database like this. I actually used SqliteBrowser, to execute the query, but there should be other ways too.

2 - Null values and naming

Our data model needs to be precisely as the data we are supposed to prepopulate. For example if we have a [name] TEXT NULL we should make sure that the representation of it must meet the same conditions, including nullability and naming (this also includes table names):
@ColumnInfo(name = "name")
val name: String? = "",
Otherwise, we would feel some serious trouble. If we have the correct set up, let Room to connect the rest (the Sqlite table and it's Kotlin/Java object representation).

3- Must have incremental processor enabled:

Don't forget to have the correct setup in build.gradle (or in my case build.gradle.kts), because this feature is only available after Room 2.2.0:
  javaCompileOptions {
            annotationProcessorOptions {
                arguments = mapOf(
                    "room.schemaLocation" to "$projectDir/schemas",
                    "room.incremental" to "true",
                    "room.expandProjection" to "true"
                )
            }
        }

How to know if we are doing it correctly?

There are two cases to spot here:

1 - We have the incorrect file

In this case Room would automatically throw a RuntimeException yelling: "Cannot copy database file".

2 - The file is correct but the table in SQL has nothing to do with our model representation

In this case Room would throw a SqliteException with some error like: "Database file is corrupt". Trust me whatever you google on this case, cannot help 😅.

Note: This article is just about importing a .db file from assets folder. You can also import a db file from the device after download. Please check the docs for more.

A wish I made.

Well, I would like to see an option from Room to import a JSON as a file. Should be cool to have it as a feature.

Conclusion:

I hope I helped anyone who is willing to write an app with totally offline data. It's pretty good solution, when we don't have too much.

Stavro Xhardha

Popular posts from this blog

Modularizing your Android app, breaking the monolith (Part 1)

Inspired by a Martin Fowlers post about Micro Frontends, I decided to break my monolithic app into a modular app. I tried to read a little more about breaking monolithic apps in Android, and as far as I got, I felt confident to share my experience with you. This will be some series of blog posts where we actually try to break a simple app into a modularized Android app.

Note: You should know that I am no expert in this, so if there are false statements or mistakes please feel free to criticize, for the sake of a better development. 

What do you benefit from this approach:
Well, people are moving pretty fast nowadays and delivery is required faster and faster. So, in order to achieve this, modularising Android apps is really necessary.You can share features across different apps. Independent teams and less problems per each.Conditional features update.Quicker debugging and fixing.A feature delay doesn't delay the whole app. As per writing tests, there is not too much difference about…

What I learned from Kotlin Flow API

I used to check the docs and just read a lot about flows but didn't implement anything until yesterday. However, the API tasted really cool (even though some operations are still in Experimental state).Prerequisites: If you don't know RxJava it's fine. But a RxJava recognizer would read this faster.Cold vs Hot streamsWell, I really struggled with this concept because it is a little bit tricky. The main difference between cold and hot happened to be pretty simple: Hot streams produce when you don't care while in cold streams, if you don't collect() (or RxJava-s equivalent subscribe()) the stream won't be activated at all. So, Flows are what we call cold streams. Removing the subscriber will not produce data at all, making the Flows one of the most sophisticated asynchronous stream API ever (in the JVM world). I tried to make a illustration of hot and cold streams: Since I mentioned the word asynchronous this implies that they do support coroutines also. Flows vs…

From Gson to Moshi, what I learned

There is no doubt that people are getting away from GSON and I agree with those reasons too. The only advantage GSON has over other parsing libraries is that it takes a really short amount of time to set up. Furthermore, the most important thing is that Moshi is embracing Kotlin support.

First let's implement the dependency:
implementation("com.squareup.moshi:moshi:1.8.0") It's not a struggle to migrate to Moshi. It's really Gson look-a-like. The only thing to do is annotate the object with @field:Json instead of @SerializedName (which is Gsons way for JS representation):

data class User( //GSON way @SerializedName("name") val name: String, @SerializedName("user_name") val userName: String, @SerializedName("last_name") val lastName: String, @SerializedName("email") val email: String ) data class User( //Moshi way @field:Json(name = "name") val name: String, @field:Json(name = "user_name…