Room kotlin android livedata

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$$\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

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> @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> = db.productDao().getProducts(); fun insert(product: Product) < viewModelScope.launch(Dispatchers.IO) < db.productDao().insert(product) >> fun clear() < viewModelScope.launch(Dispatchers.IO) < db.productDao().clear() >> > 

Источник

Читайте также:  Ячейка как ссылка

Android — Kotlin

A blog to explore things in Android App Development using Kotlin.

Room with LiveData, ViewModel — Android Architecture Components — Kotlin

  • Get link
  • Facebook
  • Twitter
  • Pinterest
  • Email
  • Other Apps

Introduction:

We have already learnt about Room in last post, Room-Kotlin. In this post, lets learn about how to use ViewModel and LiveData with Room to improve usablity. Using LiveData with Room, allows views to be notified about data changes automatically.

LiveData:

LiveData is nothing but observable data holder class. It allows us to observer changes in data across multiple components of app. It is also aware of and respects lifecycle of Activities/Fragments. It also ensures that LiveData only updates app component observers that are in an active lifecycle state.

The following image depicts the Lifecycle and States:

Lifecycle and its states

Here, CREATED, STARTED, RESUMED — Active States
INTIALIZED, DESTROYED — InActive States

So, LiveData will remove the updates automatically while in InActive States, i.e., INTIALIZED and DESTROYED.

ViewModel:

ViewModel is an entity that is free of Activity/Fragment’s lifecycle. So it allows us to store and manage UI-related data in lifecycle conscious way. For example, it can retain its state/data even when the configuration changed. It doesn’t contain any code related to UI.

Implementing ViewModel:

Every ViewModel class should extends ViewModel class. If it needs to use the ApplicationContext, then it should extends AndroidViewModel.

class MyViewModel : ViewModel() < var users: MutableLiveDataListUser>>? =null fun getUsers(): LiveDataListUser>> < if (users == null) < users = MutableLiveDataListUser>>() loadUsers() > return users > private fun loadUsers() < // Do an asyncronous operation to fetch users. > >

The created ViewModel can be accessed in Activity like below:

class MyActivity : AppCompatActivity() < public override fun onCreate(savedInstanceState: Bundle?) < // Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. val model = ViewModelProviders.of(this).get(MyViewModel::class.java) model.getUsers().observe(this, < users -> // update UI >) > >

Code:

build.gradle:

 apply plugin: 'kotlin-kapt'
 compile 'com.android.support:cardview-v7:27.0.2'
 compile "android.arch.persistence.room:runtime:1.0.0"
 kapt "android.arch.persistence.room:compiler:1.0.0"
 compile "android.arch.lifecycle:extensions:1.0.0"
 kapt "android.arch.lifecycle:compiler:1.0.0"

Book.kt (Entity — Class)

@Entity data class Book(var bookName : String, var author : String, var genre : String)< @PrimaryKey(autoGenerate = true) var id : Long? = null >

BookDao.kt (Dao — Interface)

@Dao interface BookDao < @Query("SELECT * FROM Book") fun getBookInfo() : LiveDataMutableListBook>> @Insert fun addBook(book : Book) @Update(onConflict = REPLACE) fun updateBook( book: Book) @Delete fun deleteBook( book: Book?) > 

MyDatabase.kt (Database — Class)

Every Database operation calls should be in Background thread. Otherwise, the app will crash.

@Database(entities = arrayOf(Book::class), version = 1) abstract class MyDatabase : RoomDatabase() < abstract fun bookDao(): BookDao companion object < var INSTANCE: MyDatabase? = null fun getInstance(context: Context): MyDatabase < if (INSTANCE == null) < INSTANCE = Room.databaseBuilder(context.applicationContext, MyDatabase::class.java, "Books.db").build() > return INSTANCE as MyDatabase; > @SuppressLint("StaticFieldLeak") fun insertData(mydata: MyDatabase, book: Book) < object : AsyncTaskVoid, Void, Void>() < override fun doInBackground(vararg voids: Void): Void? < mydata.bookDao().addBook(book) return null > >.execute() > @SuppressLint("StaticFieldLeak") fun getData(mydata: MyDatabase): LiveDataMutableListBook>> < lateinit var lists: LiveDataMutableListBook>> return object : AsyncTaskVoid, Void, LiveDataMutableListBook>>>() < override fun doInBackground(vararg voids: Void): LiveDataMutableListBook>>? < lists = mydata.bookDao().getBookInfo() return lists > >.execute().get() > @SuppressLint("StaticFieldLeak") fun deleteData(mydata: MyDatabase, book: Book?) < object : AsyncTaskVoid, Void, Void>() < override fun doInBackground(vararg voids: Void): Void? < mydata.bookDao().deleteBook(book) return null > >.execute() > > >

BookViewModel.kt (ViewModel — Class)

Here, we are using AndroidViewModel. It will handle the data fetching from Database and it have to be get and return as LiveData for observing.

class BookViewModel(application: Application) : AndroidViewModel(application) < var list: LiveDataMutableListBook>> init < list = MyDatabase.getData(MyDatabase.getInstance(this.getApplication())) > fun fetchAllData() : LiveDataMutableListBook>> = list >

RecyclerAdapter.kt:

class RecyclerAdapter(val context: Context, var data : MutableListBook>?) : RecyclerView.AdapterRecyclerAdapter.Holder>() < override fun onBindViewHolder(holder: Holder?, position: Int) < holder?.bindItems(data?.get(position)) > override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): Holder < val v = LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false) return Holder(v) > override fun getItemCount(): Int = data?.size?:0 class Holder(itemView: View?) : RecyclerView.ViewHolder(itemView)< fun bindItems(book: Book?)< itemView.name.text = book?.bookName itemView.author.text = book?.author itemView.genre.text = book?.genre itemView.setOnLongClickListener(object :View.OnLongClickListener< override fun onLongClick(v: View?): Boolean < MyDatabase.deleteData(MyDatabase.getInstance(itemView.context), book) return true > >) > > fun addItems(t: MutableListBook>?) < data = t notifyDataSetChanged() > >

DbActivity.kt

ViewModel class should be registered in this Activity. Data should be fetched and observed using LiveData. If the changes detected in Database, it will invoke onChanged(), there the adapter can be notified of those changes.

class DbActivity : AppCompatActivity() < lateinit var adapter: RecyclerAdapter lateinit var viewModel: BookViewModel var list: MutableListBook>? = null override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setContentView(R.layout.db_layout) recyclerView.layoutManager = LinearLayoutManager(this) adapter = RecyclerAdapter(this, list) recyclerView.adapter = adapter viewModel = ViewModelProviders.of(this).get(BookViewModel::class.java) viewModel.fetchAllData().observe(this, object : ObserverMutableListBook>> < override fun onChanged(t: MutableListBook>?) < Log.v("OnChanged","OnChanged!!") adapter.addItems(t) > >) add.setOnClickListener < openDialog() >> private fun openDialog() < val dialog = Dialog(this) dialog.requestWindowFeature(Window.FEATURE_NO_TITLE) dialog.setContentView(R.layout.dialog) val lp: WindowManager.LayoutParams = WindowManager.LayoutParams().apply < copyFrom(dialog.window.attributes) width = WindowManager.LayoutParams.MATCH_PARENT height = WindowManager.LayoutParams.WRAP_CONTENT > val submit = dialog.findViewByIdView>(R.id.submit) as TextView val name = dialog.findViewByIdView>(R.id.name) as EditText val author = dialog.findViewByIdView>(R.id.author) as EditText val genre = dialog.findViewByIdView>(R.id.genre) as EditText submit.setOnClickListener < when < name.length() == 0 || author.length() == 0 || genre.length() == 0 -> Toast.makeText(this@DbActivity, "Please fill all the fields" , Toast.LENGTH_SHORT).show() else -> < val book = Book(name.text.toString(), author.text.toString(), genre.text.toString()) MyDatabase.insertData(MyDatabase.getInstance(this), book) dialog.dismiss() > > > dialog.show() dialog.getWindow().setAttributes(lp) > >

Источник

Оцените статью