kotlin与MVVM结合使用总结(一)

一、Kotlin 与 MVVM 结合的核心优势

  1. 代码简洁性

    • 数据类(data class)简化 Model 层定义,自动生成equals/hashCode/toString
    • 扩展函数简化 View 层逻辑(如点击事件扩展)
    • lateinit/by lazy优化 ViewModel 属性初始化
  2. 异步处理优化

    • 协程(Coroutines)替代 RxJava,轻量且代码可读性强
    • withContext(Dispatchers.IO)切换线程,配合LiveData更新 UI
  3. 响应式编程

    • LiveData + StateFlow实现数据双向绑定
    • Flow替代LiveData处理复杂数据流(如网络请求重试)
  4. 生命周期感知

    • ViewModel配合SavedStateHandle保存状态
    • LifecycleOwner简化生命周期监听

二、MVVM 实现细节

  1. ViewModel 层

    • 使用 Kotlin @HiltViewModel注解依赖注入(结合 Hilt)
    • 协程启动任务:viewModelScope.launch { ... }
    • StateFlow封装业务状态,替代可变 LiveData
  2. View 层

    • DataBinding 绑定布局,Kotlin 表达式简化逻辑(如@{user.name ?: "Guest"}
    • ViewBinding替代findViewById,类型安全
    • 协程与lifecycleScope结合处理异步任务
  3. Model 层

    • 数据类定义实体,@SerializedName配合 Retrofit 解析 JSON
    • 仓库(Repository)模式隔离数据源,Kotlin 密封类定义请求状态(如Result.Success/Error

之间的关联:

  • View 持有 ViewModel:View(如 Activity、Fragment 等)会创建并持有 ViewModel 的引用,通过数据绑定机制观察 ViewModel 中的数据变化。
  • ViewModel 持有 Model:ViewModel 持有 Model 的引用,从 Model 获取数据并处理业务逻辑,将处理后的数据暴露给 View。
  • Model 不持有 View 和 ViewModel:Model 专注于数据的存储和获取,不依赖于 View 和 ViewModel。

三、面试高频问题

1、ViewModel 是如何保持数据的

        ViewModel 使用了 Android 架构组件中的 SavedStateHandle 来保持数据。

        SavedStateHandle 是一个键值对集合,用于在配置更改(如屏幕旋转)时保存和恢复数据。

       当 Activity 或 Fragment 因配置更改而销毁重建时,ViewModel 不会被销毁,SavedStateHandle 中的数据会被保留,从而实现数据的保持。

2、 ViewModel 是怎么做到在 Activity 销毁重建新实例之后还能保持不变的

        在 Android 中,ViewModel 的生命周期与 Activity 或 Fragment 的生命周期不同。

        当 Activity 或 Fragment 因配置更改(如屏幕旋转)而销毁重建时,系统会自动保留 ViewModel 的实例。

       这是通过 ViewModelStore 来实现的,ViewModelStore 是一个存储 ViewModel 实例的容器,Activity 或 Fragment 会持有一个 ViewModelStore 的引用。

      当 Activity 或 Fragment 重建时,会从 ViewModelStore 中获取之前的 ViewModel 实例,从而保证 ViewModel 中的数据不会丢失。

四、最佳实践

  • View:对应 Android 中的 Activity、Fragment、View 等,负责界面的绘制和用户交互的处理。
  • ViewModel:对应 Android 中的 ViewModel 类,负责处理业务逻辑和数据的转换,通过 LiveData、StateFlow 等将数据暴露给 View。
  • Model:对应数据的实体类(如 Kotlin 中的数据类)和数据获取的仓库类(Repository),负责数据的存储和获取。

演示代码:

ViewModel:

@HiltViewModel
class MainViewModel @Inject constructor(private val repository: UserRepository
) : ViewModel() {private val _user = MutableStateFlow<User?>(null)val user: StateFlow<User?> = _userfun fetchUser(userId: String) {viewModelScope.launch {try {_user.value = repository.getUser(userId)} catch (e: Exception) {// 处理错误}}}
}

View 层(Fragment):

class MainFragment : Fragment() {private val viewModel by viewModels<MainViewModel>()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)viewModel.user.collectAsState().observe(viewLifecycleOwner) { user ->// 更新UI}}
}

真实操作:

首先,确保在项目的 build.gradle 中添加必要的依赖,如 ViewModel、LiveData、Kotlin 协程等:

dependencies {// ViewModel 和 LiveDataimplementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'// Kotlin 协程implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
}

Model 层

Model 层主要负责数据的定义和数据的获取。这里以一个简单的用户数据为例:

// 定义用户数据类
data class User(val id: Int, val name: String, val age: Int)// 模拟数据获取的仓库类
class UserRepository {// 模拟网络请求,使用协程进行异步操作suspend fun getUser(id: Int): User {// 模拟耗时操作delay(1000)return User(id, "John Doe", 30)}
}

ViewModel 层

ViewModel 层负责处理业务逻辑,并将数据暴露给 View 层。它通过 LiveData 或 StateFlow 来实现数据的响应式更新。

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launchclass UserViewModel(private val userRepository: UserRepository) : ViewModel() {// 使用 MutableStateFlow 来存储和更新用户数据private val _user = MutableStateFlow<User?>(null)// 对外暴露不可变的 StateFlowval user: StateFlow<User?> = _user// 获取用户数据的方法fun fetchUser(id: Int) {viewModelScope.launch {try {// 调用仓库类的方法获取用户数据val user = userRepository.getUser(id)// 更新 StateFlow 的值_user.value = user} catch (e: Exception) {// 处理异常e.printStackTrace()}}}
}

View 层

View 层通常是 Activity 或 Fragment,负责显示数据和处理用户交互。这里以 Fragment 为例:

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launchclass UserFragment : Fragment() {private val userViewModel: UserViewModel by lazy {UserViewModel(UserRepository())}override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.fragment_user, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)// 启动协程来收集 StateFlow 的数据lifecycleScope.launch {userViewModel.user.collect { user ->user?.let {// 更新 UI// 这里可以根据实际情况更新 TextView 等视图组件// 例如:textView.text = "${it.name}, ${it.age}"}}}// 触发数据获取userViewModel.fetchUser(1)}
}

代码解释

  • Model 层
    • data class User:使用 Kotlin 的数据类简洁地定义了用户数据结构,自动生成 equalshashCode 和 toString 方法。
    • UserRepository:模拟了数据的获取过程,使用 suspend 关键字和 delay 函数模拟网络请求的异步操作,使用协程进行异步处理。
  • ViewModel 层
    • MutableStateFlow 和 StateFlow:用于存储和暴露用户数据,实现数据的响应式更新。MutableStateFlow 用于内部数据的更新,StateFlow 用于对外暴露不可变的数据。
    • viewModelScope.launch:在 ViewModel 中使用协程进行异步操作,确保在 ViewModel 的生命周期内执行。当 ViewModel 被销毁时,协程会自动取消。
  • View 层
    • lifecycleScope.launch:在 Fragment 中使用协程来收集 StateFlow 的数据,确保在 Fragment 的生命周期内执行。当 Fragment 被销毁时,协程会自动取消。
    • userViewModel.fetchUser(1):触发数据获取操作,调用 ViewModel 中的方法获取用户数据。

总结

     Kotlin 通过协程、数据类、扩展函数等特性大幅提升了 MVVM 的开发效率和代码质量,

    面试中需重点关注异步处理、数据绑定、依赖注入及生命周期管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/902584.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频分析设备平台EasyCVR安防视频小知识:安防监控常见故障精准排查方法

随着安防监控技术的飞速发展&#xff0c;监控系统已经成为现代安防体系中不可或缺的核心组成部分&#xff0c;广泛应用于安防监控、交通管理、工业自动化等多个领域。然而&#xff0c;监控系统的稳定运行高度依赖于设备的正确配置、线路的可靠连接以及电源的稳定供电。在实际应…

【DeepSeek 学习推理】Llumnix: Dynamic Scheduling for Large Language Model Serving实验部分

6.1 实验设置 测试平台。我们使用阿里云上的16-GPU集群&#xff08;包含4个GPU虚拟机&#xff0c;类型为ecs.gn7i-c32g1.32xlarge&#xff09;。每台虚拟机配备4个NVIDIA A10&#xff08;24 GB&#xff09;GPU&#xff08;通过PCI-e 4.0连接&#xff09;、128个vCPU、752 GB内…

如何利用深度学习进行交通流量预测与疏导

传统的交通管理方法&#xff0c;诸如固定的信号灯配时方案、基于经验的警力部署等&#xff0c;在面对现代城市如此复杂多变的交通状况时&#xff0c;已然显得捉襟见肘&#xff0c;难以满足高效交通管理的需求。 在此背景下&#xff0c;准确的交通流量预测便成为了破解交通拥堵难…

LSTM-GAN生成数据技术

1. 项目概述 本项目利用生成对抗网络&#xff08;GAN&#xff09;技术来填补时间序列数据中的缺失值。项目实现了两种不同的GAN模型&#xff1a;基于LSTM的GAN&#xff08;LSTM-GAN&#xff09;和基于多层感知机的GAN&#xff08;MLP-GAN&#xff09;&#xff0c;并对两种模型…

CMake 入门指南:从零开始配置你的第一个项目

目录 一、CMake 是什么&#xff0c;为什么要使用 CMake 二、CMakeLists.txt 文件结构与简单示例 三、进阶的CMake 四、静态库与动态库生成及其使用 五、注释的语法 六、 set、list、message 三个常用的 CMake 函数与命令 七、CMake 的控制语句以及自定义宏/函数 八、为S…

多线程出bug不知道如何调试?java线程几种常见状态

当你的多线程代码结构很复杂的时候很难找出bug的原因所在&#xff0c;此时我们可以使用getState()方法获取该线程当前的状态&#xff0c;通过观察其状态是阻塞了还是因为没有启动等原因导致的。 状态描述NEW安排了工作&#xff0c;还未开始行动RUNNABLE可工作的&#xff0c;又…

Spark(20)spark和Hadoop的区别

Apache Spark 和 Apache Hadoop 都是广泛使用的开源大数据处理框架&#xff0c;但它们在设计理念、架构、性能和适用场景等方面存在显著区别。以下是它们的主要区别&#xff1a; ### **1. 架构设计** - **Hadoop**&#xff1a; - **HDFS&#xff08;Hadoop Distributed File…

【redis】哨兵模式

Redis主从模式虽然支持数据备份与读写分离&#xff0c;但存在三大核心缺陷&#xff1a;1. 故障切换依赖人工&#xff08;主节点宕机需手动提升从节点&#xff09;&#xff1b;2. 监控能力缺失&#xff08;无法自动检测节点异常&#xff09;&#xff1b;3. 脑裂风险&#xff08;…

Spark-Streaming

找出所有有效数据&#xff0c;要求电话号码为11位&#xff0c;但只要列中没有空值就算有效数据。 按地址分类&#xff0c;输出条数最多的前20个地址及其数据。 代码讲解&#xff1a; 导包和声明对象&#xff0c;设置Spark配置对象和SparkContext对象。 使用Spark SQL语言进行数…

Sentinel源码—9.限流算法的实现对比一

大纲 1.漏桶算法的实现对比 (1)普通思路的漏桶算法实现 (2)节省线程的漏桶算法实现 (3)Sentinel中的漏桶算法实现 (4)Sentinel中的漏桶算法与普通漏桶算法的区别 (5)Sentinel中的漏桶算法存在的问题 2.令牌桶算法的实现对比 (1)普通思路的令牌桶算法实现 (2)节省线程的…

Redis 详解:安装、数据类型、事务、配置、持久化、订阅/发布、主从复制、哨兵机制、缓存

目录 Redis 安装与数据类型 安装指南 Windows Linux 性能测试 基本知识 数据类型 String List&#xff08;双向列表&#xff09; Set&#xff08;集合&#xff09; Hash&#xff08;哈希&#xff09; Zset&#xff08;有序集合&#xff09; 高级功能 地理位置&am…

Docker配置带证书的远程访问监听

一、生成证书和密钥 1、准备证书目录和生成CA证书 # 创建证书目录 mkdir -p /etc/docker/tls cd /etc/docker/tls # 生成CA密钥和证书 openssl req -x509 -newkey rsa:4096 -keyout ca-key.pem \ -out ca-cert.pem -days 365 -nodes -subj "/CNDocker CA" 2、为…

MCP接入方式介绍

上一篇文章&#xff0c;我们介绍了MCP是什么以及MCP的使用。 MCP是什么&#xff0c;MCP的使用 接下来&#xff0c;我们来详细介绍一下MCP的接入 先看官网的架构图 上图的MCP 服务 A、MCP 服务 B、MCP 服务 C是可以运行在你的本地计算机&#xff08;本地服务器方式&#xff…

关于Agent的简单构建和分享

前言&#xff1a;Agent 具备自主性、环境感知能力和决策执行能力&#xff0c;能够根据环境的变化自动调整行为&#xff0c;以实现特定的目标。 一、Agent 的原理 Agent(智能体)被提出时&#xff0c;具有四大能力 感知、分析、决策和执行。是一种能够在特定环境中自主行动、感…

Gitlab runner 安装和注册

Gitlab Runner GitLab Runner是一个用于运行GitLab CI/CD流水线作业的软件包&#xff0c;由GitLab官方开发&#xff0c;完全开源。你可以在很多主流的系统环境或平台上安装它&#xff0c;如Linux、macOS、Windows和Kubernetes。如果你熟悉Jenkins 的话&#xff0c;你可以把它…

精益数据分析(18/126):权衡数据运用,精准把握创业方向

精益数据分析&#xff08;18/126&#xff09;&#xff1a;权衡数据运用&#xff0c;精准把握创业方向 大家好&#xff01;一直以来&#xff0c;我都希望能和大家在创业与数据分析的领域共同探索、共同进步。今天&#xff0c;我们继续深入研读《精益数据分析》&#xff0c;探讨…

Git技术详解:从核心原理到实际应用

Git技术详解&#xff1a;从核心原理到实际应用 一、Git的本质与核心价值 Git是由Linux之父Linus Torvalds在2005年开发的分布式版本控制系统&#xff0c;其核心功能是通过记录文件变更历史&#xff0c;帮助开发者实现以下目标&#xff1a; 版本回溯&#xff1a;随时恢复到项…

Java从入门到“放弃”(精通)之旅——String类⑩

Java从入门到“放弃”&#xff08;精通&#xff09;之旅&#x1f680;——String类⑩ 前言 在Java编程中&#xff0c;String类是最常用也是最重要的类之一。无论是日常开发还是面试&#xff0c;对String类的深入理解都是必不可少的。 1. String类的重要性 在C语言中&#xf…

抓取淘宝数据RPA--影刀

最近用了一下RPA软件&#xff0c;挑了影刀&#xff0c;发现很无脑也很简单&#xff0c;其语法大概是JAVA和PYTHON的混合体&#xff0c;如果懂爬虫的话&#xff0c;学这个软件就快的很&#xff0c;看了一下官方的教程&#xff0c;对于有基础的人来说很有点枯燥&#xff0c;但又不…

docker部署seafile修改默认端口并安装配置onlyoffice实现在线编辑

背景 有很多场景会用到类似seafile功能的需求&#xff0c;比如&#xff1a; 在内网中传输和共享文件个人部署私人网盘文档协同在线编辑写笔记… 这些功能seafile均有实现&#xff0c;并且社区版提供的功能基本可以满足个人或者小型团队的日常需求 问题 由于主机的80和443端…