Introduction to Android App Development With Kotlin: Store Data in Database (Part 8)
Learn how to store data in your database using Kotlin.
Join the DZone community and get the full member experience.
Join For FreeIn this tutorial, we will instantiate the Room database using the singleton pattern. We will use our first coroutine and finally persist the data.
Having set up our Room database in the previous tutorial, it’s the time to insert some data into it. Before we can use the database, however, there’s just one more step we must carry out before it’s fully functional — we must instantiate it.
Instantiate Room
In order to instantiate the Room database, we will implement the singleton pattern. If you’re not familiar with what the singleton pattern is, in a few words, it ensures that there’s only ever one instance of a certain object for the entire app. Since we only have one database in our app, it’s appropriate that we only have one instance of it. Anything more than that would be a waste of resources.
Inside the AppDatabase
class, insert the following code:
companion object {
private var INSTANCE: AppDatabase? = null
fun getInstance(context: Context): AppDatabase? {
if (INSTANCE == null) {
synchronized(AppDatabase::class) {
INSTANCE = Room.databaseBuilder(context.applicationContext,
AppDatabase::class.java, "movie-database")
.build()
}
}
return INSTANCE
}
fun destroyInstance() {
INSTANCE = null
}
}
Whenever we call the method getInstance()
, the instance will be created if it hasn’t been created yet. Otherwise, it will return the already created instance.
The reason you see the synchronized method there is to be extra certain that no more than one instance gets created. If we didn’t use synchronize, it would be possible for two different threads to call the getInstance()
method at almost the same time and successfully create two instances of the database, which would not be desirable.
With the singleton of our database available for our use, let’s gain access to it inside the NewMovieViewModel
.
class NewMovieViewModel(application: Application) : AndroidViewModel(application) {
private val mDb: AppDatabase? = AppDatabase.getInstance(application)
}
Notice that the mDb member variable is set to private. The reason for this is that we do not want to expose the database directly to the view. The view will call an appropriate method inside the ViewModel
, which will then interact with the database.
Store Data
To store the data, we need a method that will accept user input of String type (movie title) and store it in the database. Let’s try it:
fun storeMovie(title: String) {
val movie = Movie()
movie.name = title
mDb?.movieDao()?.insert(movie)
}
Now, let’s call this method in our NewMovieFragment
inside the onViewCreated
method after the validation is done and see what happens.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button.setOnClickListener {
val input = editText.text.toString().trim()
if (input.isEmpty()) {
Toast.makeText(activity, "Title required", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (input.length > 30) {
Toast.makeText(activity, "Title too long", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
mViewModel.storeMovie(input)
Toast.makeText(activity, "$input entered", Toast.LENGTH_SHORT).show()
listener?.goToMovieListFragment()
}
}
The only new line here is the mViewModel.storeMovie(input)
. If you run the app now, type in a "test" movie title and hit the “Add” button. Unfortunately, our app is going to crash. You should receive the following stack:
As per the message in the exception, Android will tell us that we “Cannot access database on the main thread since it may potentially lock the UI for a long period of time.”
This is a good thing. Room prevents us from running queries on the main thread to protect the user from potentially slow apps. Because of this, we must spin up a background thread, which will not lock the UI, and carry out the operation there.
To achieve this, we will use Kotlin’s coroutines.
Coroutines
Coroutines are super simple to use once you know what you’re doing. I will show you an easy way to spin up a coroutine and execute our database call without a crash.
To use coroutines, we must import them into our project, in build.gradle (app) add:
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'
Then, go back to the NewMovieViewModel
and surround the database insert operation with GlobalScope.launch { }
.
Run the app and see what happens! Yes, no crash. We have successfully stored a movie title in a database. How do you know? Well, unfortunately, we haven’t implemented a fetch from the database function just yet, so we can’t tell for sure. You just have to take my word for it.
Conclusion
We have finally used our database and persisted some data for later use. Unfortunately, there’s no visible results of us storing the data yet. Therefore, in the next tutorial, we will fetch the data from the database to prove that we really are saving the data.
Complete working code can be found in this GitHub repo where each lesson is tagged appropriately.
Opinions expressed by DZone contributors are their own.
Comments