Android

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()
        }
    }
}

Output

Leave a Reply

Your email address will not be published. Required fields are marked *