欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 明星 > Android开发实战班 -应用架构 - MVVM 架构模式

Android开发实战班 -应用架构 - MVVM 架构模式

2025/2/24 11:24:45 来源:https://blog.csdn.net/bravekingzhang/article/details/143913217  浏览:    关键词:Android开发实战班 -应用架构 - MVVM 架构模式

随着 Android 应用复杂性的增加,采用良好的架构模式变得越来越重要。MVVM(Model-View-ViewModel) 是一种流行的架构模式,旨在将应用的 UI 逻辑、数据和业务逻辑分离,使代码更易于维护、测试和扩展。本章节将深入讲解 MVVM 架构模式的原理、组件、Jetpack 组件(如 ViewModel 和 LiveData)的使用,以及如何在 Android 项目中应用 MVVM 架构。

MVVM 架构模式简介

  • MVVM 的起源:

    • MVVM(Model-View-ViewModel)架构模式最初由 Microsoft 提出,用于构建 WPF(Windows Presentation Foundation)和 Silverlight 应用。
    • 随着 Android 开发的复杂化,MVVM 被引入到 Android 开发中,并成为主流架构模式之一。
  • MVVM 的核心思想:

    • 分离关注点: 将应用的 UI 逻辑、数据和业务逻辑分离,使代码更清晰、易于维护。
    • 数据驱动 UI: ViewModel 持有 UI 数据,并暴露数据给 View,View 通过观察数据的变化来更新 UI。
    • 可测试性: ViewModel 不依赖于 View,可以独立进行单元测试。
  • MVVM 的优点:

    • 代码清晰: 各层职责分明,代码更易于理解和维护。
    • 可测试性高: ViewModel 可以独立于 UI 进行单元测试,提高测试覆盖率。
    • 可复用性: ViewModel 可以被多个 View 复用,减少代码重复。
    • 数据绑定: 通过数据绑定机制,View 可以自动响应数据变化,无需手动更新 UI。

10.2 MVVM 架构模式的组成部分

MVVM 架构模式主要由以下三个部分组成:

  1. Model(模型):

    • 负责数据的获取、存储和业务逻辑处理。
    • 例如,数据库、网络请求、数据模型等。
  2. View(视图):

    • 负责 UI 的展示和用户交互。
    • 例如,Activity, Fragment, Composable 等。
  3. ViewModel(视图模型):

    • 充当 View 和 Model 之间的桥梁,负责处理 UI 相关的数据和逻辑。
    • ViewModel 不直接引用 View,可以独立于 View 进行单元测试。

10.3 Jetpack 组件在 MVVM 中的应用

Jetpack 提供了多个组件,可以帮助我们更好地实现 MVVM 架构:

  • ViewModel:

    • ViewModel 是 MVVM 架构中的核心组件,负责存储和管理 UI 相关的数据。
    • ViewModel 生命周期与 View 分离,可以在配置变化(例如屏幕旋转)时保留数据。
    class MyViewModel : ViewModel() {private val _data = MutableLiveData<String>()val data: LiveData<String> get() = _datafun loadData() {// 模拟网络请求_data.value = "Hello, MVVM!"}
    }
    
  • LiveData:

    • LiveData 是一种可观察的数据持有者,可以感知生命周期变化,避免内存泄漏。
    • View 可以观察 LiveData 的变化,并自动更新 UI。
    class MyActivity : AppCompatActivity() {private lateinit var viewModel: MyViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)viewModel = ViewModelProvider(this).get(MyViewModel::class.java)viewModel.data.observe(this) { data ->// 更新 UIfindViewById<TextView>(R.id.textView).text = data}viewModel.loadData()}
    }
    
  • Data Binding:

    • Data Binding 允许将 UI 组件直接绑定到 ViewModel 的数据,减少样板代码。
    • 例如,将 TextView 的文本属性绑定到 ViewModel 的数据。
    <layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variablename="viewModel"type="com.example.app.MyViewModel" /></data><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{viewModel.data}" />
    </layout>
    
  • Room:

    • Room 是 Android 官方提供的持久化库,提供了对 SQLite 数据库的抽象层。
    • 可以与 ViewModel 结合使用,实现数据的存储和查询。
    @Entity
    data class User(@PrimaryKey val id: Int,val name: String
    )@Dao
    interface UserDao {@Query("SELECT * FROM user")fun getAllUsers(): LiveData<List<User>>
    }@Database(entities = [User::class], version = 1)
    abstract class AppDatabase : RoomDatabase() {abstract fun userDao(): UserDao
    }class MyViewModel(application: Application) : AndroidViewModel(application) {private val db = Room.databaseBuilder(application,AppDatabase::class.java, "database-name").build()val users: LiveData<List<User>> = db.userDao().getAllUsers()
    }
    

10.4 MVVM 架构模式的实现步骤

在前面的章节中,我们介绍了 MVVM 架构模式的基本概念和组成部分。接下来,我们将详细介绍如何在 Android 项目中实现 MVVM 架构,包括 ViewModel 的创建与使用、LiveData 的观察、数据绑定以及与 Repository 模式的结合。

10.4.1 创建 ViewModel

ViewModel 是 MVVM 架构的核心组件,负责存储和管理 UI 相关的数据和业务逻辑。ViewModel 不直接引用 View,因此可以独立于 View 进行单元测试。

  • 步骤:

    1. 创建一个继承自 ViewModel 的类。
    2. 在 ViewModel 中定义 UI 相关的数据和逻辑。
    3. 使用 LiveDataStateFlow 暴露数据给 View。
  • 示例:

    // UserRepository.kt
    class UserRepository {fun getUsers(): List<User> {// 模拟网络请求或数据库查询return listOf(User(1, "Alice"), User(2, "Bob"))}
    }// UserViewModel.kt
    class UserViewModel(private val repository: UserRepository) : ViewModel() {private val _users = MutableLiveData<List<User>>()val users: LiveData<List<User>> get() = _usersfun loadUsers() {// 模拟数据加载val userList = repository.getUsers()_users.value = userList}
    }// User.kt
    data class User(val id: Int, val name: String)
    
10.4.2 创建 View

View 负责 UI 的展示和用户交互。在 Android 中,View 通常是 Activity 或 Fragment。在 Jetpack Compose 中,View 可以是 Composable 函数。

  • 步骤:

    1. 在 Activity 或 Fragment 中创建 ViewModel 实例。
    2. 使用 ViewModelProviderViewModel 的构造函数注入 ViewModel。
    3. 观察 ViewModel 中的 LiveData 数据,并更新 UI。
  • 示例:

    // UserActivity.kt
    class UserActivity : AppCompatActivity() {private lateinit var viewModel: UserViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_user)// 创建 ViewModel 实例viewModel = ViewModelProvider(this, ViewModelFactory(UserRepository())).get(UserViewModel::class.java)// 观察 LiveData 数据viewModel.users.observe(this) { users ->// 更新 UI,例如使用 RecyclerView 显示用户列表val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)recyclerView.adapter = UserAdapter(users)}// 加载数据viewModel.loadUsers()}
    }// UserAdapter.kt
    class UserAdapter(private val users: List<User>) : RecyclerView.Adapter<UserAdapter.UserViewHolder>() {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false)return UserViewHolder(view)}override fun onBindViewHolder(holder: UserViewHolder, position: Int) {val user = users[position]holder.bind(user)}override fun getItemCount(): Int = users.sizeclass UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {fun bind(user: User) {itemView.findViewById<TextView>(R.id.textViewName).text = user.name}}
    }
    
10.4.3 使用 Data Binding

Data Binding 允许将 UI 组件直接绑定到 ViewModel 的数据,减少样板代码,提高代码可读性。

  • 步骤:

    1. build.gradle 文件中启用 Data Binding。
      android {...buildFeatures {dataBinding true}
      }
      
    2. 在布局文件中使用 <layout> 标签包裹 UI 组件,并定义 ViewModel 变量。
      <layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variablename="viewModel"type="com.example.app.UserViewModel" /></data><RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="match_parent"android:adapter="@{viewModel.users}" />
      </layout>
      
    3. 在 Activity 或 Fragment 中设置布局和数据绑定。
      class UserActivity : AppCompatActivity() {private lateinit var viewModel: UserViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val binding: ActivityUserBinding = DataBindingUtil.setContentView(this, R.layout.activity_user)viewModel = ViewModelProvider(this, ViewModelFactory(UserRepository())).get(UserViewModel::class.java)binding.viewModel = viewModelbinding.lifecycleOwner = thisviewModel.loadUsers()}
      }
      
  • 示例:

    <!-- activity_user.xml -->
    <layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variablename="viewModel"type="com.example.app.UserViewModel" /></data><RecyclerViewandroid:id="@+id/recyclerView"android:layout_width="match_parent"android:layout_height="match_parent"android:adapter="@{viewModel.users}" />
    </layout>
    
    // UserViewModel.kt
    class UserViewModel(private val repository: UserRepository) : ViewModel() {val users: LiveData<List<User>> = MutableLiveData()fun loadUsers() {val userList = repository.getUsers()(users as MutableLiveData).value = userList}
    }
    

10.4.4 使用 Repository 模式

Repository 模式 是 MVVM 架构的重要组成部分,用于抽象数据源,提供统一的接口给 ViewModel。Repository 模式可以有效地管理数据来源,包括网络请求、数据库、文件存储等,使 ViewModel 更加专注于 UI 逻辑和数据处理,而无需关心数据的具体来源。

10.4.4.1 Repository 模式的优势
  • 数据来源抽象: Repository 模式将数据来源抽象出来,使 ViewModel 不需要关心数据的具体来源(如网络、数据库等)。
  • 数据缓存: 可以通过 Repository 实现数据的缓存机制,提高应用性能。
  • 单一职责: 每个 Repository 类只负责特定的数据源,遵循单一职责原则。
  • 可测试性: Repository 可以独立于 ViewModel 进行单元测试,提高代码的可测试性。
10.4.4.2 实现 Repository 模式

以下是一个典型的 Repository 模式实现步骤:

  1. 定义数据模型:

    • 定义数据模型类,例如 User
    data class User(val id: Int, val name: String)
    
  2. 定义数据源接口:

    • 定义数据源接口,例如 UserDataSource,用于获取用户数据。
    interface UserDataSource {suspend fun getUsers(): List<User>
    }
    
  3. 实现具体的数据源:

    • 实现网络数据源,例如 RemoteUserDataSource,通过 API 获取用户数据。
    class RemoteUserDataSource(private val apiService: ApiService) : UserDataSource {override suspend fun getUsers(): List<User> {return apiService.fetchUsers()}
    }
    
    • 实现本地数据源,例如 LocalUserDataSource,从数据库获取用户数据。
    class LocalUserDataSource(private val userDao: UserDao) : UserDataSource {override suspend fun getUsers(): List<User> {return userDao.getAllUsers()}
    }
    
  4. 实现 Repository 类:

    • 创建 UserRepository 类,负责管理数据源。
    • 可以根据需要实现缓存机制,例如先从网络获取数据,再保存到数据库。
    class UserRepository(private val remoteDataSource: RemoteUserDataSource, private val localDataSource: LocalUserDataSource) {suspend fun getUsers(forceRefresh: Boolean = false): List<User> {if (forceRefresh) {// 从网络获取数据val users = remoteDataSource.getUsers()// 保存到数据库localDataSource.saveUsers(users)return users} else {// 先从数据库获取数据val users = localDataSource.getUsers()if (users.isNotEmpty()) {return users} else {// 如果数据库为空,则从网络获取数据val usersFromNetwork = remoteDataSource.getUsers()// 保存到数据库localDataSource.saveUsers(usersFromNetwork)return usersFromNetwork}}}
    }
    
  5. 在 ViewModel 中使用 Repository:

    • 在 ViewModel 中注入 UserRepository 实例,并调用其方法获取数据。
    class UserViewModel(private val repository: UserRepository) : ViewModel() {private val _users = MutableLiveData<List<User>>()val users: LiveData<List<User>> get() = _usersfun loadUsers(forceRefresh: Boolean = false) {viewModelScope.launch {try {val users = repository.getUsers(forceRefresh)_users.postValue(users)} catch (e: Exception) {// 处理异常,例如显示错误信息}}}
    }
    
  6. 依赖注入:

    • 使用依赖注入框架(例如 Hilt)来管理 Repository 和数据源的依赖关系。
    @Module
    @InstallIn(SingletonComponent::class)
    object RepositoryModule {@Provides@Singletonfun provideUserRepository(remoteDataSource: RemoteUserDataSource,localDataSource: LocalUserDataSource): UserRepository {return UserRepository(remoteDataSource, localDataSource)}@Provides@Singletonfun provideRemoteUserDataSource(apiService: ApiService): RemoteUserDataSource {return RemoteUserDataSource(apiService)}@Provides@Singletonfun provideLocalUserDataSource(userDao: UserDao): LocalUserDataSource {return LocalUserDataSource(userDao)}
    }
    

通过使用 Repository 模式,ViewModel 可以专注于 UI 逻辑和数据处理,而不需要关心数据的具体来源和实现细节。这种分离使得代码更加清晰、可维护,并且易于测试。

作者简介

前腾讯电子签的前端负责人,现 whentimes tech CTO,专注于前端技术的大咖一枚!一路走来,从小屏到大屏,从 Web 到移动,什么前端难题都见过。热衷于用技术打磨产品,带领团队把复杂的事情做到极简,体验做到极致。喜欢探索新技术,也爱分享一些实战经验,帮助大家少走弯路!

温馨提示:可搜老码小张公号联系导师

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词