<p>This post is about how to use Room Database with MVVM Architecture with the help of a simple application.</p> 
 
 
 
<div class="wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-a89b3969 wp-block-buttons-is-layout-flex"> 
<div class="wp-block-button"><a class="wp-block-button__link has-white-color has-text-color has-background" style="background-color: #250376;" href="https://github.com/arunk7839/MVVMWithRoomDB"><strong>DOWNLOAD CODE</strong></a></div> 
</div> 
 
 
 
<p> ;</p> 
<p><amp-youtube layout="responsive" width="1200" height="675" data-videoid="hOPnXSAyVPg" title="Android Room Database with MVVM Architecture"><a placeholder href="https://youtu.be/hOPnXSAyVPg"><img src="https://i.ytimg.com/vi/hOPnXSAyVPg/hqdefault.jpg" layout="fill" object-fit="cover" alt="Android Room Database with MVVM Architecture"></a></amp-youtube></p> 
<h4> </h4> 
<h4><span style="color: #000080;"><strong>What is MVVM?</strong></span></h4> 
<ul> 
<li>MVVM architecture is a <span style="color: #008000;"><strong>Model-View-ViewModel </strong></span>architecture.</li> 
<li>It allows separating the user interface logic from the business (or the back-end) logic.</li> 
<li>Its target is to keep UI code simple and free of app logic in order to make it easier to manage and test. </li> 
</ul> 
<p>MVVM has mainly the following layers:</p> 
<p><span style="color: #0000ff;"><strong>Model</strong>:</span></p> 
<p>The model represents the data and the business logic of the Application. It consists of the business logic – local and remote data source, model classes, repository.</p> 
<p><span style="color: #0000ff;"><strong>View</strong>:</span></p> 
<p>The view consists of the UI Code(Activity, Fragment), XML. The view role in this pattern is to observe (or subscribe to) a ViewModel observable to get data in order to update UI elements accordingly.</p> 
<p><span style="color: #0000ff;"><strong>ViewModel</strong></span>: </p> 
<p>The ViewModel is a class whose role is to provide data to the UI and survive configuration changes. A ViewModel acts as a communication center between the Repository and the UI.<br />One of the important implementation strategies of this layer is to decouple it from the View, i.e, ViewModel should not be aware of the view who is interacting with.</p> 
<h4><strong><span style="color: #000080;">Room Database</span></strong></h4> 
<p>The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. In particular, Room provides the following benefits:</p> 
<ul> 
<li>Compile-time verification of SQL queries.</li> 
<li>Convenience annotations that minimize repetitive and error-prone boilerplate code.</li> 
<li>Streamlined database migration paths.</li> 
</ul> 
<h4><span style="color: #000080;"><strong>Creating new project</strong></span></h4> 
<p>1 . Create a new project by going to <span style="color: #008000;"><strong>File ⇒ New Android Project</strong></span>, select <span style="color: #008000;"><strong>Empty</strong></span> Activity, provide <span style="color: #008000;"><strong>app</strong></span> name, select language to <span style="color: #008000;"><strong>kotlin</strong></span> and then finally click on <span style="color: #008000;"><strong>finish</strong></span>.</p> 
<p>2 . Open app-level build.gradle file and add the below changes:</p> 
<ul> 
<li>Add the below libraries under the <span style="color: #008000;"><strong>dependencies</strong></span> section<strong>:</strong></li> 
</ul> 
<pre>dependencies {<br /><strong><span style="color: #008000;"> //room library</span></strong><br /> implementation 'androidx.room:room-runtime:2.4.1'<br /> kapt 'androidx.room:room-compiler:2.4.1'<br /><br /><strong><span style="color: #008000;"> // Kotlin Extensions and Coroutines support for Room</span></strong><br /> implementation "androidx.room:room-ktx:2.4.1"<br /><br /><strong><span style="color: #008000;"> //ViewModel and livedata</span></strong><br /> implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"<br /> implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"<br /> implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"<br />}</pre> 
<ul> 
<li>Inside the <span style="color: #008000;"><strong>android</strong></span> block, add <strong><span style="color: #008000;">buildFeatures</span></strong> block with property <strong><span style="color: #0000ff;">viewBinding</span></strong> to <span style="color: #0000ff;"><strong>true</strong></span>.</li> 
</ul> 
<pre>buildFeatures {<br /> viewBinding true<br />}</pre> 
<ul> 
<li>Inside <span style="color: #008000;"><strong>plugins</strong></span> block, add the below plugin:</li> 
</ul> 
<pre>plugins {<br /> id 'com.android.application'<br /> id 'kotlin-android'<br /><strong><span style="color: #008000;"> id 'kotlin-kapt'</span></strong><br />}</pre> 
<ul> 
<li>Finally, click on <strong><span style="color: #008000;">Sync Now</span>.</strong></li> 
</ul> 
<h4><span style="color: #000080;"><strong>Creating Room components (Entity, Dao and Database)</strong></span></h4> 
<h5><strong><span style="color: #0000ff;">Entity:</span></strong></h5> 
<p>Entity is an annotated class that is used to describe a database table when we are working with Room.</p> 
<h5><strong><span style="color: #0000ff;">Database</span></strong>:</h5> 
<p>Database serves as the app&#8217;s main access point to the underlying SQLite database. It uses DAO to issue queries to the SQLite database.</p> 
<h5><strong><span style="color: #0000ff;">DAO:</span> </strong></h5> 
<p>DAO is a Data Access Object which is used for mapping SQL queries to functions.</p> 
<h5><br /><span style="color: #000080;"><strong>Creating an Entity</strong></span></h5> 
<p>The following code defines an <span style="color: #008000;"><strong>Event</strong></span> entity. Each instance of Event represents a row in an <strong><span style="color: #008000;">eventTable</span></strong> in the app&#8217;s database. Each row represents one entity (or record), and each column represents a value in that entity. </p> 
<p><strong><span style="color: #0000ff;">Event.kt</span></strong></p> 
<pre>@Entity(tableName = "eventTable")<br />data class Event(<br /> @PrimaryKey(autoGenerate = true)<br /> @ColumnInfo(name = "event_id")<br /> val id: Int?,<br /><br /> @ColumnInfo(name = "event_title")<br /> val title: String?,<br /><br /> @ColumnInfo(name = "event_description")<br /> val description: String?<br />)</pre> 
<h5><br /><span style="color: #000080;"><strong>Creating a DAO(Data access object) class</strong></span></h5> 
<p>The following code defines a DAO called <span style="color: #008000;"><strong>EventsDao</strong></span>. EventsDao provides the methods that the rest of the app uses to interact with data in the <strong><span style="color: #008000;">eventTable</span></strong>.</p> 
<p><strong><span style="color: #0000ff;">EventsDao.kt</span></strong></p> 
<pre>@Dao<br />interface EventsDao {<br /> <strong><span style="color: #008000;">// adds a new entry to our database.</span></strong><br /><strong><span style="color: #008000;"> // if some data is same/conflict, it'll be replace with new data</span></strong><br /> @Insert(onConflict = OnConflictStrategy.REPLACE)<br /> suspend fun insertEvent(event :Event)<br /><br /><strong><span style="color: #008000;"> // deletes an event</span></strong><br /> @Delete<br /> suspend fun deleteEvent(event: Event)<br /><br /><strong><span style="color: #008000;"> // updates an event.</span></strong><br /> @Update<br /> suspend fun updateEvent(event: Event)<br /><br /> <span style="color: #008000;"><strong>// read all the events from eventTable</strong></span><br /><span style="color: #008000;"><strong> // and arrange events in ascending order</strong></span><br /><span style="color: #008000;"><strong> // of their ids</strong></span><br /> @Query("Select * from eventTable order by event_id ASC")<br /> fun getAllEvents(): LiveData<;List<;Event>;>;<br /> <strong><span style="color: #008000;">// why not use suspend ? because Room does not support LiveData with suspended functions.</span></strong><br /><strong><span style="color: #008000;"> // LiveData already works on a background thread and should be used directly without using coroutines</span></strong><br /><br /><strong><span style="color: #008000;"> // delete all events</span></strong><br /> @Query("DELETE FROM eventTable")<br /> suspend fun clearEvent()<br /><br /><strong><span style="color: #008000;"> //you can use this too, to delete an event by id.</span></strong><br /> @Query("DELETE FROM eventTable WHERE event_id = :id")<br /> suspend fun deleteEventById(id: Int)<br />}</pre> 
<h5><br /><span style="color: #000080;"><strong>Creating Database class</strong></span></h5> 
<p>The following code defines an <strong><span style="color: #008000;">EventDatabase</span></strong> class to hold the database. EventDatabase defines the database configuration and serves as the app&#8217;s main access point to the persisted data.</p> 
<p>The database class must satisfy the following conditions:</p> 
<ul> 
<li>must be annotated with a <strong><span style="color: #008000;">@Database</span></strong> annotation that includes an entities array.</li> 
<li>must be an <strong><span style="color: #008000;">abstract</span></strong> class that extends <strong><span style="color: #008000;">RoomDatabase</span></strong>.</li> 
<li>for each DAO class that is associated with the database, the database class must define an abstract method that has zero arguments and returns an instance of the DAO class.</li> 
</ul> 
<p><span style="color: #0000ff;"><strong>EventDatabase.kt</strong></span></p> 
<pre>@Database(entities = [Event::class], version = 1, exportSchema = false)<br />abstract class EventDatabase : RoomDatabase() {<br /><br /> abstract fun getEventsDao(): EventsDao<br /><br /> companion object {<br /> <strong><span style="color: #008000;">// Volatile annotation means any change to this field</span></strong><br /><strong><span style="color: #008000;"> // are immediately visible to other threads.</span></strong><br /> @Volatile<br /> private var INSTANCE: EventDatabase? = null<br /><br /> private const val DB_NAME = "event_database.db"<br /><br /> fun getDatabase(context: Context): EventDatabase {<br /> <strong><span style="color: #008000;"> // if the INSTANCE is not null, then return it,</span></strong><br /><strong><span style="color: #008000;"> // if it is, then create the database</span></strong><br /><strong><span style="color: #008000;"> // here synchronised used for blocking the other thread</span></strong><br /><strong><span style="color: #008000;"> // from accessing another while in a specific code execution.</span></strong><br /> return INSTANCE ?: synchronized(this) {<br /> val instance = Room.databaseBuilder(<br /> context.applicationContext,<br /> EventDatabase::class.java,<br /> DB_NAME<br /> ).build()<br /> INSTANCE = instance<br /><span style="color: #008000;"><strong> // return instance</strong></span><br /> instance<br /> }<br /> }<br /> }<br />}</pre> 
<h5><span style="color: #000080;"><strong>Setup Data Repository</strong></span></h5> 
<p>A Repository is a class that abstracts access to multiple data sources. </p> 
<p>The below Repository class contains some methods which invoke the methods of the Dao class.</p> 
<p><strong><span style="color: #0000ff;">EventRepositoryt.kt</span></strong></p> 
<pre>import androidx.lifecycle.LiveData<br />import com.c1ctech.mvvmwithroomdb.data.db.EventsDao<br />import com.c1ctech.mvvmwithroomdb.data.db.entity.Event<br /><br />class EventRepository(private val eventDao: EventsDao) {<br /><br /><strong><span style="color: #008000;"> // get all the events</span></strong><br /> fun getAllEvents(): LiveData<;List<;Event>;>; = eventDao.getAllEvents()<br /><br /><strong><span style="color: #008000;"> // adds an event to our database.</span></strong><br /> suspend fun insertEvent(event: Event) {<br /> eventDao.insertEvent(event)<br /> }<br /><br /><strong><span style="color: #008000;"> // deletes an event from database.</span></strong><br /> suspend fun deleteEvent(event: Event) {<br /> eventDao.deleteEvent(event)<br /> }<br /><br /><strong><span style="color: #008000;"> // updates an event from database.</span></strong><br /> suspend fun updateEvent(event: Event) {<br /> eventDao.updateEvent(event)<br /> }<br /><br /><strong><span style="color: #008000;"> //delete an event by id.</span></strong><br /> suspend fun deleteEventById(id: Int) = eventDao.deleteEventById(id)<br /><br /><strong><span style="color: #008000;"> // delete all events</span></strong><br /> suspend fun clearEvent() = eventDao.clearEvent()<br />}</pre> 
<h4><span style="color: #000080;"><strong>Setup the ViewModel</strong></span></h4> 
<p>The <strong><span style="color: #008000;">EventViewModel</span></strong> class extends the AndroidViewModel. </p> 
<p>ViewModel is basically used for providing the data to our UI. It acts as a communication layer between Repository and the UI.</p> 
<p><strong><span style="color: #0000ff;">EventViewModel.kt</span></strong></p> 
<pre>import android.app.Application<br />import androidx.lifecycle.AndroidViewModel<br />import androidx.lifecycle.LiveData<br />import androidx.lifecycle.viewModelScope<br />import com.c1ctech.mvvmwithroomdb.data.db.EventDatabase<br />import com.c1ctech.mvvmwithroomdb.data.db.entity.Event<br />import com.c1ctech.mvvmwithroomdb.data.repository.EventRepository<br />import kotlinx.coroutines.Dispatchers<br />import kotlinx.coroutines.launch<br /><br />class EventViewModel(<br /> application: Application<br />) : AndroidViewModel(application) {<br /><br /> val allEvents: LiveData<;List<;Event>;>;<br /> val repository: EventRepository<br /><br /><strong><span style="color: #008000;"> // initialize dao, repository and all events</span></strong><br /> init {<br /> val dao = EventDatabase.getDatabase(application).getEventsDao()<br /> repository = EventRepository(dao)<br /> allEvents = repository.getAllEvents()<br /> }<br /><br /> fun insertEvent(event: Event) =<br /> viewModelScope.launch(Dispatchers.IO) { repository.insertEvent(event) }<br /><br /> fun updateEvent(event: Event) =<br /> viewModelScope.launch(Dispatchers.IO) { repository.updateEvent(event) }<br /><br /> fun deleteEvent(event: Event) =<br /> viewModelScope.launch(Dispatchers.IO) { repository.deleteEvent(event) }<br /><br /> fun deleteEventById(id: Int) =<br /> viewModelScope.launch(Dispatchers.IO) { repository.deleteEventById(id) }<br /><br /> fun clearEvent() =<br /> viewModelScope.launch(Dispatchers.IO) { repository.clearEvent() }<br />}</pre> 
<h4><span style="color: #000080;"><strong>Creating Activity Layout file</strong></span></h4> 
<p>The below layout file contains a RecyclerView and a FloatingActionButton on click of which we will add an event to the database.</p> 
<p><span style="color: #0000ff;"><strong>activity_main.xml</strong></span></p> 
<pre><;?xml version="1.0" encoding="utf-8"?>;<br /><;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"<br /> xmlns:app="http://schemas.android.com/apk/res-auto"<br /> xmlns:tools="http://schemas.android.com/tools"<br /> android:layout_width="match_parent"<br /> android:layout_height="match_parent"<br /> android:background="@color/white"<br /> tools:context=".MainActivity">;<br /><br /> <;androidx.recyclerview.widget.RecyclerView<br /> android:id="@+id/eventsRV"<br /> android:layout_width="match_parent"<br /> android:layout_height="match_parent"<br /> android:layout_marginTop="5dp"<br /> tools:listitem="@layout/event_rv_item" />;<br /><br /> <;com.google.android.material.floatingactionbutton.FloatingActionButton<br /> android:id="@+id/fab_add"<br /> android:layout_width="wrap_content"<br /> android:layout_height="wrap_content"<br /> android:layout_alignParentEnd="true"<br /> android:layout_alignParentRight="true"<br /> android:layout_alignParentBottom="true"<br /> android:layout_margin="20dp"<br /> android:src="@android:drawable/ic_input_add"<br /> app:tint="@color/white" />;<br /><br /><;/RelativeLayout>;</pre> 
<h4><span style="color: #000080;"><strong>Creating Adapter Layout file</strong></span></h4> 
<p>The below layout file represents the UI of each item of recyclerView.</p>
<!-- WP QUADS Content Ad Plugin v. 2.0.98.1 -->
<div class="quads-location quads-ad2" id="quads-ad2" style="float:none;margin:0px;">

</div>
 
<p><span style="color: #0000ff;"><strong>event_rv_item.xml</strong></span></p> 
<pre><;?xml version="1.0" encoding="utf-8"?>;<br /><;androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"<br /> xmlns:app="http://schemas.android.com/apk/res-auto"<br /> xmlns:tools="http://schemas.android.com/tools"<br /> android:layout_width="match_parent"<br /> android:layout_height="wrap_content"<br /> android:layout_marginStart="8dp"<br /> android:layout_marginTop="4dp"<br /> android:layout_marginEnd="8dp"<br /> android:layout_marginBottom="4dp"<br /> app:cardBackgroundColor="@color/teal_700"<br /> app:cardCornerRadius="3dp"<br /> app:cardElevation="3dp">;<br /><br /> <;androidx.constraintlayout.widget.ConstraintLayout<br /> android:layout_width="match_parent"<br /> android:layout_height="match_parent">;<br /><br /> <;TextView<br /> android:id="@+id/tvTitle"<br /> android:layout_width="0dp"<br /> android:layout_height="wrap_content"<br /> android:layout_marginStart="8dp"<br /> android:layout_marginLeft="8dp"<br /> android:layout_marginTop="8dp"<br /> android:layout_marginEnd="8dp"<br /> android:layout_marginRight="8dp"<br /> android:textColor="@android:color/white"<br /> android:textSize="24sp"<br /> android:textStyle="bold"<br /> app:layout_constraintStart_toStartOf="parent"<br /> app:layout_constraintTop_toTopOf="parent"<br /> tools:text="Title" />;<br /><br /> <;TextView<br /> android:id="@+id/tvDescription"<br /> android:layout_width="0dp"<br /> android:layout_height="wrap_content"<br /> android:layout_marginStart="8dp"<br /> android:layout_marginLeft="8dp"<br /> android:layout_marginEnd="8dp"<br /> android:layout_marginRight="8dp"<br /> android:layout_marginBottom="8dp"<br /> android:textColor="@color/white"<br /> android:textSize="16sp"<br /> app:layout_constraintBottom_toBottomOf="parent"<br /> app:layout_constraintEnd_toEndOf="parent"<br /> app:layout_constraintStart_toStartOf="parent"<br /> app:layout_constraintTop_toBottomOf="@+id/tvTitle"<br /> app:layout_constraintVertical_bias="0.0"<br /> tools:text="Description" />;<br /><br /> <;ImageView<br /> android:id="@+id/imgDelete"<br /> android:layout_width="wrap_content"<br /> android:layout_height="wrap_content"<br /> android:layout_marginTop="8dp"<br /> android:layout_marginEnd="8dp"<br /> android:layout_marginRight="8dp"<br /> android:src="@drawable/ic_delete"<br /> android:textSize="24sp"<br /> android:textStyle="bold"<br /> app:layout_constraintEnd_toEndOf="parent"<br /> app:layout_constraintTop_toTopOf="parent" />;<br /><br /> <;/androidx.constraintlayout.widget.ConstraintLayout>;<br /><br /><;/androidx.cardview.widget.CardView>;</pre> 
<h4><span style="color: #000080;"><strong>Creating Adapter class</strong></span></h4> 
<p>The EventAdapter is an adapter class for the recycler view to set all the items into recycler view.</p> 
<p><span style="color: #0000ff;"><strong>EventAdapter.kt</strong></span></p> 
<pre>import android.view.LayoutInflater<br />import android.view.ViewGroup<br />import androidx.recyclerview.widget.RecyclerView<br />import com.c1ctech.mvvmwithroomdb.data.db.entity.Event<br />import com.c1ctech.mvvmwithroomdb.databinding.EventRvItemBinding<br /><br />class EventAdapter(<br /> val eventDeleteIconClickInterface: EventDeleteIconClickInterface,<br /> val eventClickInterface: EventClickInterface<br />) : RecyclerView.Adapter<;EventViewHolder>;() {<br /><br /> private val allEvents = ArrayList<;Event>;()<br /><br /> override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EventViewHolder {<br /><br /> val inflater = LayoutInflater.from(parent.context)<br /><br /> val binding = EventRvItemBinding.inflate(inflater, parent, false)<br /> return EventViewHolder(binding)<br /> }<br /><br /> override fun onBindViewHolder(holder: EventViewHolder, position: Int) {<br /><strong><span style="color: #008000;"> // sets data to item of recycler view.</span></strong><br /> holder.binding.tvTitle.text = allEvents.get(position).title<br /> holder.binding.tvDescription.text = allEvents.get(position).description<br /><br /><strong><span style="color: #008000;"> // adding click listener to our delete image view icon.</span></strong><br /> holder.binding.imgDelete.setOnClickListener {<br /><strong><span style="color: #008000;"> // call eventDeleteIconClickInterface.onEventDeleteIconClick() and pass position to it.</span></strong><br /> eventDeleteIconClickInterface.onEventDeleteIconClick(allEvents.get(position))<br /> }<br /><br /><strong><span style="color: #008000;"> // adding click listener to our recycler view item.</span></strong><br /> holder.itemView.setOnClickListener {<br /><strong><span style="color: #008000;"> // call eventClickInterface.onEventClick() and pass position to it.</span></strong><br /> eventClickInterface.onEventClick(allEvents.get(position))<br /> }<br /> }<br /><br /> override fun getItemCount(): Int {<br /><strong><span style="color: #008000;"> // return list size.</span></strong><br /> return allEvents.size<br /> }<br /><br /><strong><span style="color: #008000;"> // update the list of events.</span></strong><br /> fun updateList(newList: List<;Event>;) {<br /><br /><strong><span style="color: #008000;"> // clear the allEvents array list</span></strong><br /> allEvents.clear()<br /><br /><strong><span style="color: #008000;"> // adds a new list to our allEvents list.</span></strong><br /> allEvents.addAll(newList)<br /><br /><br /><strong><span style="color: #008000;"> // call notifyDataSetChanged() to notify our adapter.</span></strong><br /> notifyDataSetChanged()<br /> }<br />}<br /><br />interface EventDeleteIconClickInterface {<br /> <strong><span style="color: #008000;"> // creating a method for click</span></strong><br /><strong><span style="color: #008000;"> // action on delete image view.</span></strong><br /> fun onEventDeleteIconClick(event: Event)<br />}<br /><br />interface EventClickInterface {<br /> <strong><span style="color: #008000;">// creating a method for click action</span></strong><br /><strong><span style="color: #008000;"> // on recycler view item for updating it.</span></strong><br /> fun onEventClick(event: Event)<br />}<br /><br />class EventViewHolder(val binding: EventRvItemBinding) : RecyclerView.ViewHolder(binding.root) {}</pre> 
<h4><span style="color: #000080;"><strong>Creating AddEventActivity File</strong></span></h4> 
<p>Inside AddEventActivity, we will add and edit an event.</p> 
<p><span style="color: #0000ff;"><strong>AddEventActivity</strong><strong>.kt</strong></span></p> 
<pre>import android.content.Intent<br />import android.os.Bundle<br />import android.widget.Toast<br />import androidx.appcompat.app.AppCompatActivity<br />import androidx.lifecycle.ViewModelProvider<br />import com.c1ctech.mvvmwithroomdb.data.db.entity.Event<br />import com.c1ctech.mvvmwithroomdb.databinding.ActivityAddEventBinding<br /><br />class AddEventActivity : AppCompatActivity() {<br /> private lateinit var binding: ActivityAddEventBinding<br /> lateinit var viewModal: EventViewModel<br /> var eventID = -1;<br /><br /> override fun onCreate(savedInstanceState: Bundle?) {<br /> super.onCreate(savedInstanceState)<br /> binding = ActivityAddEventBinding.inflate(layoutInflater)<br /> setContentView(binding.root)<br /><br /><span style="color: #008000;"><strong> // initializes the viewmodel.</strong></span><br /> viewModal = ViewModelProvider(<br /> this,<br /> ViewModelProvider.AndroidViewModelFactory.getInstance(application)<br /> ).get(EventViewModel::class.java)<br /><br /><strong><span style="color: #008000;"> // getting data passed via an intent.</span></strong><br /> val eventType = intent.getStringExtra("eventType")<br /> if (eventType.equals("Edit")) {<br /><strong><span style="color: #008000;"> // setting data to edit text.</span></strong><br /> val eventTitle = intent.getStringExtra("eventTitle")<br /> val eventDescription = intent.getStringExtra("eventDescription")<br /> eventID = intent.getIntExtra("eventId", -1)<br /> binding.btnSave.setText("Update Event")<br /> binding.titleET.setText(eventTitle)<br /> binding.descriptionET.setText(eventDescription)<br /> } else {<br /> binding.btnSave.setText("Save Event")<br /> }<br /><br /><strong><span style="color: #008000;"> // adding click listener to our save button.</span></strong><br /> binding.btnSave.setOnClickListener {<br /><br /><strong><span style="color: #008000;"> // getting title and desc from edit text.</span></strong><br /> val eventTitle = binding.titleET.text.toString()<br /> val eventDescription = binding.descriptionET.text.toString()<br /><br /><strong><span style="color: #008000;"> // checking the type and then saving or updating the data.</span></strong><br /> if (eventType.equals("Edit")) {<br /><br /> if (eventTitle.isNotEmpty() &;&; eventDescription.isNotEmpty()) {<br /><br /> val updatedEvent = Event(eventID, eventTitle, eventDescription)<br /> viewModal.updateEvent(updatedEvent)<br /> Toast.makeText(this, "Event Updated", Toast.LENGTH_LONG).show()<br /> }<br /> } else {<br /> if (eventTitle.isNotEmpty() &;&; eventDescription.isNotEmpty()) {<br /><br /><strong><span style="color: #008000;"> // if the string is not empty we are calling</span></strong><br /><strong><span style="color: #008000;"> // add event method to add data to our room database.</span></strong><br /><strong><span style="color: #008000;"> //why id null? because id is auto generate</span></strong><br /> viewModal.insertEvent(Event(null, eventTitle, eventDescription))<br /> Toast.makeText(this, "Event Added", Toast.LENGTH_LONG).show()<br /> }<br /> }<br /><strong><span style="color: #008000;"> // opening the new activity</span></strong><br /> startActivity(Intent(applicationContext, MainActivity::class.java))<br /> this.finish()<br /> }<br /> }<br />}</pre> 
<h4><span style="color: #000080;"><strong>Creating MainActivity File</strong></span></h4> 
<p>Inside MainActivity, we will get an instance of ViewModel and use ViewModel properties which will execute the SQL queries and update the UI accordingly.</p> 
<p><span style="color: #0000ff;"><strong>MainActivity.kt</strong></span></p> 
<pre>import android.content.Intent<br />import android.os.Bundle<br />import android.view.Menu<br />import android.view.MenuItem<br />import android.widget.Toast<br />import androidx.appcompat.app.AlertDialog<br />import androidx.appcompat.app.AppCompatActivity<br />import androidx.lifecycle.Observer<br />import androidx.lifecycle.ViewModelProvider<br />import androidx.recyclerview.widget.LinearLayoutManager<br />import com.c1ctech.mvvmwithroomdb.data.db.entity.Event<br />import com.c1ctech.mvvmwithroomdb.databinding.ActivityMainBinding<br /><br />class MainActivity : AppCompatActivity(), EventClickInterface, EventDeleteIconClickInterface {<br /><br /> private lateinit var binding: ActivityMainBinding<br /> private lateinit var viewModel: EventViewModel<br /> private lateinit var eventAdapter: EventAdapter<br /><br /> override fun onCreate(savedInstanceState: Bundle?) {<br /> super.onCreate(savedInstanceState)<br /> binding = ActivityMainBinding.inflate(layoutInflater)<br /> setContentView(binding.root)<br /><br /> viewModel = ViewModelProvider(<br /> this,<br /> ViewModelProvider.AndroidViewModelFactory.getInstance(application)<br /> ).get(EventViewModel::class.java)<br /><br /> eventAdapter = EventAdapter(this, this)<br /><br /> initView()<br /> observeEvents()<br /> }<br /><br /> private fun initView() {<br /> binding.fabAdd.setOnClickListener {<br /> val intent = Intent(this, AddEventActivity::class.java)<br /> startActivity(intent)<br /> }<br /><br /> binding.eventsRV.apply {<br /> setHasFixedSize(true)<br /> layoutManager = LinearLayoutManager(this@MainActivity)<br /> adapter = eventAdapter<br /> }<br /> }<br /><br /> private fun observeEvents() {<br /> viewModel.allEvents.observe(this, Observer { list ->;<br /> list?.let {<br /><strong><span style="color: #008000;"> // updates the list.</span></strong><br /> eventAdapter.updateList(it)<br /> }<br /> })<br /> }<br /><br /> private fun clearEvent() {<br /> val dialog = AlertDialog.Builder(this, R.style.ThemeOverlay_AppCompat_Dialog)<br /> dialog.setTitle("Clear Event")<br /> .setMessage("Are you sure, you want to delete all event?")<br /> .setPositiveButton(android.R.string.ok) { _, _ ->;<br /> viewModel.clearEvent().also {<br /> Toast.makeText(this, "Event Deleted", Toast.LENGTH_LONG).show()<br /> }<br /> }.setNegativeButton(android.R.string.cancel, null).create().show()<br /> }<br /><br /> override fun onCreateOptionsMenu(menu: Menu?): Boolean {<br /> menuInflater.inflate(R.menu.main_menu, menu)<br /> return true<br /> }<br /><br /> override fun onOptionsItemSelected(item: MenuItem): Boolean {<br /> when (item.itemId) {<br /> R.id.clearEventItem ->; clearEvent()<br /> }<br /> return super.onOptionsItemSelected(item)<br /> }<br /><br /> override fun onEventDeleteIconClick(event: Event) {<br /> viewModel.deleteEvent(event)<br /> Toast.makeText(this, "Event Deleted", Toast.LENGTH_LONG).show()<br /> }<br /><br /> override fun onEventClick(event: Event) {<br /><strong><span style="color: #008000;"> // opening a new intent and passing a data to it.</span></strong><br /> val intent = Intent(this@MainActivity, AddEventActivity::class.java)<br /> intent.putExtra("eventType", "Edit")<br /> intent.putExtra("eventTitle", event.title)<br /> intent.putExtra("eventDescription", event.description)<br /> intent.putExtra("eventId", event.id)<br /> startActivity(intent)<br /> this.finish()<br /> }<br />}</pre> 
<p>When you run the app it will look like this:</p> 
<p><img class="alignnone wp-image-3130" src="https://c1ctech.com/wp-content/uploads/2022/02/Screenshot_20220225-160056_MVVMWithRoomDB-498x1024.jpg" alt="" width="385" height="792" />

