The Model-View-ViewModel (MVVM) architecture is a popular design pattern for developing Android applications. It helps in separating the concerns of the application, making the code more modular, testable, and maintainable. This article will guide you through the steps of implementing MVVM architecture in an Android application.
1. Introduction to
MVVM
MVVM architecture consists of three main components:
- Model: This represents the data layer of the application. It includes the data models and the business logic.
- View: This represents the UI layer of the application. It displays the data and forwards user actions to the ViewModel.
- ViewModel: This acts as a bridge between the Model and the View. It handles the presentation logic and prepares data for the View.
2. Setting Up the
Project
To get started with MVVM, create a new Android project in Android Studio. Make sure to include the necessary dependencies in your build.gradle
file:
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
3. Creating the Model
The Model represents the data and business logic of the application. In this example, we'll use Room to create a simple database for storing user information. First, define the data entity:
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int,
val name: String,
val email: String
)
Next, create the DAO (Data Access Object) interface:
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
@Dao
interface UserDao {
@Insert
suspend fun insert(user: User)
@Query("SELECT * FROM users")
fun getAllUsers(): LiveData>
}
Finally, create the database class:
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
@Volatile private var instance: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase =
instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
private fun buildDatabase(context: Context) =
Room.databaseBuilder(context.applicationContext,
AppDatabase::class.java, "app_database")
.build()
}
}
4. Creating the
ViewModel
The ViewModel handles the presentation logic and prepares data for the View. Create a ViewModel class to manage user data:
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class UserViewModel(application: Application) : AndroidViewModel(application) {
private val userDao = AppDatabase.getDatabase(application).userDao()
val allUsers: LiveData> = userDao.getAllUsers()
fun insert(user: User) {
viewModelScope.launch {
userDao.insert(user)
}
}
}
5. Creating the View
The View displays the data and forwards user actions to the ViewModel. In this example, we'll create a simple activity to display a list of users and a form to add new users.
5.1 Layout File
Create a layout file for the activity:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.app.UserViewModel" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/nameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Name" /><EditText
android:id="@+id/nameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Name"
android:layout_below="@id/nameEditText" />
<EditText
android:id="@+id/emailEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Email"
android:layout_below="@id/nameEditText"
android:layout_marginTop="10dp" />
<Button
android:id="@+id/addButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add User"
android:layout_below="@id/emailEditText"
android:layout_marginTop="10dp"
android:onClick="@{() -> viewModel.insert(new User(0, nameEditText.text.toString(), emailEditText.text.toString()))}" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/addButton"
android:layout_marginTop="20dp" />
</RelativeLayout>
</layout>
5.2 Activity Class
Create the MainActivity class to bind the ViewModel to the layout and handle user interactions:
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.app.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val userViewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val adapter = UserAdapter()
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)
userViewModel.allUsers.observe(this, Observer { users ->
users?.let { adapter.submitList(it) }
})
binding.addButton.setOnClickListener {
val name = binding.nameEditText.text.toString()
val email = binding.emailEditText.text.toString()
userViewModel.insert(User(0, name, email))
}
}
}
6. Creating the
Adapter
To display the list of users in a RecyclerView, create an adapter:
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.app.databinding.UserItemBinding
class UserAdapter : ListAdapter(UserDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val binding = UserItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return UserViewHolder(binding)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = getItem(position)
holder.bind(user)
}
class UserViewHolder(private val binding: UserItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(user: User) {
binding.userName.text = user.name
binding.userEmail.text = user.email
}
}
class UserDiffCallback : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem == newItem
}
}
}
7. Conclusion
Implementing MVVM architecture in your Android application can greatly improve the separation of concerns and make your code more modular and testable. By following the steps outlined in this article, you can set up MVVM architecture, create a simple database using Room, and connect the data layer with the UI layer using ViewModel and LiveData. This approach will help you build scalable and maintainable applications.
For more information and best practices, refer to the official Android Jetpack Guide.
Post a Comment