Android Room Database With Co Routines, ViewModel and LiveData in kotlin.
In this tutorial we will learn how to use Room data base in android kotlin. We will use MVVM (Model View ViewModel) design pattern. To run room db quries in separate thread, we will take advantage of Coroutines of kotlin. To avoid memory leaks and crashes we will use LiveData. It will provide us up to date data.
Lets explore all of these with simple example.
MainActivity.kt
package com.example.coroutienpractice
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.coroutienpractice.databinding.ActivityMainBinding
import com.example.coroutienpractice.view_models.ProductViewModel
import com.example.models.Product
class MainActivity : AppCompatActivity() {
lateinit var model: ProductViewModel
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
model = ViewModelProvider(this).get(ProductViewModel::class.java)
model.products.observe(this, {
if (it.isNotEmpty()) {
it.forEach {
binding.textView.append("\n${it.id.toString()} ${it.name}\n")
}
}else{
binding.textView.text=null
}
})
binding.buttonInsert.setOnClickListener {
val product = Product(null, "Creamd", 23.0)
model.insert(product)
}
binding.buttonClear.setOnClickListener {
model.clear()
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/button_insert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Insert" />
<Button
android:id="@+id/button_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:text="Clear" />
</LinearLayout>
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Product.kt
package com.example.coroutienpractice.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.example.coroutienpractice.utils.Constants
@Entity(tableName = Constants.TABLE_PRODUCTS)
data class Product(
@PrimaryKey
var id: Long?,
@ColumnInfo(name = "name") var name: String,
@ColumnInfo(name = "price") var price: Double,
)
ProductDao.kt
package com.example.coroutienpractice.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.coroutienpractice.utils.Constants
import com.example.coroutienpractice.models.Product
@Dao
interface ProductDao {
@Query("select * from "+Constants.TABLE_PRODUCTS)
fun getProducts() : LiveData<List<Product>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(product: Product)
@Query("Delete from "+Constants.TABLE_PRODUCTS)
suspend fun clear()
}
Constants.kt
package com.example.coroutienpractice.utils
class Constants {
companion object{
const val TABLE_PRODUCTS="products"
}
}
RoomSingleton.kt
package com.example.coroutienpractice
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.coroutienpractice.dao.ProductDao
import com.example.coroutienpractice.models.Product
@Database(entities = [Product::class], version = 1)
abstract class RoomSingleton : RoomDatabase() {
abstract fun productDao(): ProductDao
companion object {
var INSTANCE: RoomSingleton? = null
fun getInstance(context: Context): RoomSingleton {
if (INSTANCE == null) {
INSTANCE =
Room.databaseBuilder(context, RoomSingleton::class.java, "roomdb").build()
}
return INSTANCE as RoomSingleton
}
}
}
ProductViewModel.kt
package com.example.coroutienpractice.view_models
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import com.example.coroutienpractice.RoomSingleton
import com.example.coroutienpractice.models.Product
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
public class ProductViewModel(application: Application) : AndroidViewModel(application) {
private val db = RoomSingleton.getInstance(application)
internal val products : LiveData<List<Product>> = db.productDao().getProducts();
fun insert(product: Product) {
viewModelScope.launch(Dispatchers.IO) {
db.productDao().insert(product)
}
}
fun clear() {
viewModelScope.launch(Dispatchers.IO) {
db.productDao().clear()
}
}
}