当前位置: 首页 > article >正文

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

三、面试高频问题

  1. Kotlin 在 MVVM 中的优势

    • 协程简化异步代码,数据类减少样板代码,扩展函数提升开发效率
  2. 协程与 LiveData 的结合

    • 使用LiveData包装协程结果,或通过FlowLiveData
      viewModelScope.launch { flow.collect { data -> liveData.value = data } }
  3. DataBinding 的高级用法

    • 自定义 BindingAdapter 处理复杂逻辑(如图片加载)
    • BR类动态更新绑定变量
  4. 如何避免内存泄漏

    • ViewModel 自动绑定生命周期,协程使用viewModelScope,避免在 View 层持有长生命周期引用

四、最佳实践

  1. 单一数据源原则

    • StateFlow 作为唯一数据源,通过collectAsState()在 View 层订阅
  2. 依赖注入

    • Hilt 管理 ViewModel 和 Repository 的依赖,避免手动创建对象
  3. 单元测试

    • 使用 MockK 模拟依赖,协程测试库验证异步逻辑
    • ViewModel 测试独立于 UI 层,验证 StateFlow 的输出
  4. 状态管理

    • 密封类定义 UI 状态(如 Loading/Success/Error),结合when表达式处理

演示代码:

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 的数据类简洁地定义了用户数据结构。
    • UserRepository:模拟了数据的获取过程,使用 suspend 关键字和 delay 函数模拟网络请求的异步操作。
  • ViewModel 层
    • MutableStateFlow 和 StateFlow:用于存储和暴露用户数据,实现数据的响应式更新。
    • viewModelScope.launch:在 ViewModel 中使用协程进行异步操作,确保在 ViewModel 的生命周期内执行。
  • View 层
    • lifecycleScope.launch:在 Fragment 中使用协程来收集 StateFlow 的数据,确保在 Fragment 的生命周期内执行。
    • userViewModel.fetchUser(1):触发数据获取操作。

总结

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

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

谢谢观看!!! 

相关文章:

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

一、Kotlin 与 MVVM 结合的核心优势 代码简洁性 数据类&#xff08;data class&#xff09;简化 Model 层定义&#xff0c;自动生成equals/hashCode/toString扩展函数简化 View 层逻辑&#xff08;如点击事件扩展&#xff09;lateinit/by lazy优化 ViewModel 属性初始化 异步处…...

累计完工数量达到了xxxx超过了最大可完工数量xxxx

之前解决过一次&#xff0c;没有记录下来&#xff0c;不记得发生什么事情。又浪费几个小时去分析问题。这次的经历有点痛苦&#xff0c;碰上多表关连数据的勾稽。分析是河南用户的非法操作造成的。没有领料记录入不了库&#xff0c;跨月了。财务要求删单处理。删单之后&#xf…...

飞鸟与鱼不同路

看&#xff0c;好美的太阳。 正是因为有人看才会觉得美&#xff0c;若无人问津&#xff0c;美又从何而来。 嘿嘿&#xff0c;今天提出辞去综合教研室主任一职&#xff0c;不想在这个管理上废时间啦~ 把时间用来考试.........用来做自己的事情&#xff0c;花在自己的身上&…...

若依RuoYi-Cloud-Plus微服务版(完整版)前后端部署

一.目标 在浏览器上成功登录进入 二.源码下载 后端源码&#xff1a;前往Gitee下载页面(https://gitee.com/dromara/RuoYi-Cloud-Plus)下载解压到工作目录。 前端源码&#xff1a; 前往Gitee下载页面(https://gitee.com/JavaLionLi/plus-ui)下载解压到工作目录。 文档地址&a…...

【redis】list类型:基本命令(下)

文章目录 LLENLREMLTRIMLSET阻塞版本命令BLPOP 和 BRPOP区别使用方式 命令小结内部编码 LLEN 获取 list 的长度 语法&#xff1a; LLEN key时间复杂度&#xff1a; O ( 1 ) O(1) O(1)返回值&#xff1a; list 长度 LREM 删除 count 个 key 中的元素 语法&#xff1a; LREM…...

【数据挖掘】知识蒸馏(Knowledge Distillation, KD)

1. 概念 知识蒸馏&#xff08;Knowledge Distillation, KD&#xff09;是一种模型压缩和知识迁移技术&#xff0c;旨在将大型复杂模型&#xff08;称为教师模型&#xff09;中的知识传递给一个较小的模型&#xff08;称为学生模型&#xff09;&#xff0c;以减少计算成本&…...

VSCode 搭建C++编程环境 2025新版图文安装教程(100%搭建成功,VSCode安装+C++环境搭建+运行测试+背景图设置)

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、VScode下载及安装二、安装 MinGW-w64 工具链三、Windows环境变量配置四、检查 M…...

Ubuntu24.04 LTS 版本 Linux 系统在线和离线安装 Docker 和 Docker compose

一、更换软件源并更新系统 在 Ubuntu 24.04 LTS 中&#xff0c;系统引入了全新的软件源配置格式。现在的源配置文件内容更加结构化且清晰&#xff0c;主要包含了软件类型 (Types)、源地址 (URIs)、版本代号 (Suites) 以及组件 (Components) 等信息。 # cat /etc/apt/sources.li…...

从 pip 到 Poetry:开启高效 Python 包管理新时代

目录 从 pip 到 Poetry&#xff1a;开启高效 Python 包管理新时代 一、pip 与 Poetry 的基本区别 二、Poetry 相对于 pip 的优势 1. 依赖管理与版本锁定 2. 内置虚拟环境管理 3. 统一的项目管理流程 4. 精细的依赖解析器 5. 更友好的 CLI 工具 三、如何快速上手 Poetry…...

MTK Android12 最近历史任务 最左侧的清除历史任务改到页面底部

Android最近历史任务页面 -清除所有- 功能按钮放到底部 文章目录 需求需求原因 修改的核心文件实现方案最近历史任务基本UI结构了解代码实现思路实现方案RecentsViewTaskOverlayFactory在overview_actions_containerOverviewActionsView 实际效果 总结 需求 最近历史任务重&am…...

TCP协议支持全双工原因TCP发送接收数据是生产者消费者模型

一、TCP支持全双工的原因 TCP协议支持全双工&#xff0c;即使用TCP协议进行通信时&#xff0c;服务端和客户端可以同时进行数据的发送和接收&#xff0c;互不干扰&#xff0c;实现同时双向传输数据。 这是因为使用TCP协议通信时&#xff0c;读写套接字的文件描述符既用来发送…...

文件操作2

7. ⽂件读取结束的判定 7.1 被错误使用的 feof 牢记&#xff1a;在文件读取过程中&#xff0c;不能用 feof 函数的返回值直接来判断文件的是否结束。 feof 的作用是&#xff1a;当文件读取结束的时候&#xff0c;判断读取结束的原因是否是&#xff1a;遇到文件尾结束。 1. …...

linux中yum和wget指令的区别

yum 和 wget 都是 Linux 上的下载工具&#xff0c;但它们的用途、下载方式和适用场景不同。以下是它们的 主要区别&#xff1a; 1. yum 是软件包管理器&#xff0c;wget 是文件下载工具 功能yumwget用途安装、更新和管理 RPM 软件包从 HTTP/HTTPS/FTP 下载文件工作方式通过 yu…...

《又是二叉树?递归与回溯的经典应用》

“ 我喜欢晴天&#xff0c;你恰好是最好的太阳” 226.翻转二叉树 力扣题目链接(opens new window) 翻转一棵二叉树。 这道题我们可以通过递归法解决&#xff0c;我们只要递归的把每一个节点的左右孩子反转一下就能解决了。 代码如下&#xff1a; var invertTree function(ro…...

Qt/C++音视频开发82-系统音量值获取和设置/音量大小/静音

一、前言 在音视频开发中&#xff0c;音量的控制分两块&#xff0c;一个是控制播放器本身的音量&#xff0c;绝大部分场景都是需要控制这个&#xff0c;这个不会影响系统音量的设置。还有一种场景是需要控制系统的音量&#xff0c;因为播放器本身的音量是在系统音量的基础上控…...

从零到精通文本指令:打造个人AI助理的完整指令库(Prompt 指令实操)

文章目录 从零到精通文本指令&#xff1a;打造个人AI助理的完整指令库(Prompt 指令实操)创作指令创作指令**润色指令****扩写指令** 问答指令直接问答材料问答时间逻辑问答 总结、摘要、翻译指令总结信息抽取翻译 从零到精通文本指令&#xff1a;打造个人AI助理的完整指令库(Pr…...

C# NX二次开发:获取模型中所有的草图并获取草图中的对象

大家好&#xff0c;今天接着讲NX二次开发获取草图相关。 获取草图的方法是从workPart中获取&#xff0c;如下面的例子所示&#xff1a; List<Tag> tags new List<Tag>(); SketchCollection sketchCollection workPart.Sketches; …...

基于SpringBoot和MybatisPlus实现通用Controller

基于SpringBoot和MybatisPlus实现通用Controller&#xff0c;只需要创建实体类和mapper接口&#xff0c;单表增删改查接口就已经实现&#xff0c;提升开发效率 1.定义通用controller package com.xian.controller;import cn.hutool.core.map.MapUtil; import com.baomidou.my…...

锤头线和倒锤头线

1、锤头线 是指一根没有上影线或上影线很短,而下影线很长,实体却很小的K线。其K线实体可以是阴线或是阳线,类似于T字。 锤头线的特征有以下三点: 实体很小,下影线长度大于或等于实体的两倍。下影线越长时,如股价处于低位,则上涨的可能性越大。 如股价处于高位,则下跌…...

蓝桥杯嵌入式组第十二届省赛题目解析+STM32G431RBT6实现源码

文章目录 1.题目解析1.1 分而治之&#xff0c;藕断丝连1.2 模块化思维导图1.3 模块解析1.3.1 KEY模块1.3.2 LED模块1.3.3 LCD模块1.3.4 TIM模块1.3.5 UART模块1.3.5.1 uart数据解析 2.源码3.第十二届题目 前言&#xff1a;STM32G431RBT6实现嵌入式组第十二届题目解析源码&#…...

STM32上实现简化版的AUTOSAR DEM模块

文章目录 摘要摘要 在一些可以不使用AUTOSAR的项目中,往往也有故障检测和DTC存储的需求,开发一套类似于AUTOSAR DEM模块的软件代码,能够满足DTC的检出和存储,使用FalshDB代替Nvm模块,轻松构建持久化存储,如果你也有这样的需求,请阅读本篇,希望能够帮到你。 /*********…...

如何用终端运行一个SpringBoot项目

在项目开发阶段&#xff0c;为了能够快速测试一个SpringBoot项目的执行结果&#xff0c;就可以采用终端&#xff08;黑窗&#xff09;运行查看&#xff0c;因为我们不能要求每一个客户都安装idea并且适配我们的项目版本。 下面将展示打包运行这两个方面的过程&#xff1a; 创建…...

多线程与并发编程 面试专题

多线程与并发编程 面试专题 线程的基础概念基础概念线程的创建线程的状态线程的终止方式start 与 run 区别线程的常用方法 锁锁的分类深入synchronized深入ReentrantLock死锁问题 阻塞队列线程池 线程的基础概念 基础概念 进程与线程 进程&#xff1a;指运行中的程序。 比如我…...

米尔电子-LR3568-烧录鸿蒙

最近开始搞鸿蒙&#xff0c;用的是米尔的LR3568。 开贴记录。 首先要在LR3568上烧录鸿蒙 一、安装准备 1.从米尔电子上下载资料 网址:米尔开发者中心 注册完成后&#xff0c;进入页面&#xff0c;选择我的产品&#xff0c;添加PN和SN PN和SN可以在包装盒上找到 添加到这里…...

Redis Sentinel 及 Redisson 连接问题全解析

在 Kubernetes (k8s) 环境下使用 Redis Sentinel 进行高可用部署时&#xff0c;可能会遇到 failover 超时、Sentinel 误判、Spring Boot 连接失败 以及 Redisson 配置错误等问题。本文将对这些问题进行汇总分析&#xff0c;并提供详细的解决方案。 1️⃣ Redis Sentinel 介绍 …...

基于Flink SQL的实时指标多维分析模型

数据流程介绍 1.创建源表kafka接入消息队列数据&#xff0c;定义字段映射规则&#xff1b; 2.创建目标表es_sink配置Elasticsearch输出&#xff1b; 3.通过多级视图&#xff08;tmp→tmp_dedup→tmp1/tmp2→tmp3→tmp_groupby&#xff09;实现数据清洗、去重、状态计算&#x…...

算法刷题整理合集(一)

算法刷题整理合集&#xff08;一&#xff09; 本篇博客旨在记录自已的算法刷题练习成长&#xff0c;里面注有详细的代码注释以及和个人的思路想法&#xff0c;希望可以给同道之人些许帮助。本人也是算法小白&#xff0c;水平有限&#xff0c;如果文章中有什么错误或遗漏之处&am…...

C++ STL—— String库

在C编程中&#xff0c;字符串操作是几乎每个项目都会涉及的基础功能。C标准模板库&#xff08;STL&#xff09;中的string类为我们提供了强大而灵活的工具&#xff0c;使得字符串的处理变得简单高效。无论是字符串的创建、修改、查找&#xff0c;还是复杂的文本处理&#xff0c…...

【从零开始学习计算机科学】数据库系统(二)关系数据库 与 关系代数

【从零开始学习计算机科学】数据库系统(二)关系数据库 与 关系代数 关系数据库结构化查询语言SQL数据定义语言(DDL)数据查询语言(Data Query Language, DQL)数据操纵语言(Data Manipulation Language, DML)数据控制语言(Data Control Language, DCL)关系型数据库的优…...

DoS攻击防范

一、网络架构优化 使用CDN或反向代理 通过内容分发网络&#xff08;CDN&#xff09;或反向代理&#xff08;如Nginx&#xff09;分散流量&#xff0c;将请求分发到多个服务器节点&#xff0c;减轻单点压力&#xff0c;同时过滤异常请求。 负载均衡技术 部署负载均衡设备&#…...