APP架构设计_2.用MVVM架构实现一个具体业务
2.MVVM架构图

3.MVVM 实现一个具体业务
3.1 界面层的实现
界面层实现时,需要遵循以下几点。
1)选择实现界面的元素
界面元素可以用 view 或 compose 来实现,这里用 view 实现。
2)提供一个状态容器
这里使用 ViewModel 作为状态容器;状态容器用来存放界面状态变量;ViewModel 是官方推荐的状态容器,而不是必须使用它作为状态容器。
3)定义界面状态
这个需求中我们根据业务描述,定义出多个界面状态。
/**
* 加载失败 UI 状态,显示失败图
* 首屏获取的数据为空、首屏请求数据失败时展示失败图
* 初始值:隐藏
*/
val loadingError: StateFlow<Boolean>get() = _loadingError
private val _loadingError = MutableStateFlow<Boolean>(false)/**
* 正在加载 UI 状态,显示加载中图
* 首屏时请求网络时展示加载中图
* 初始值:展示
*/
val isLoading: StateFlow<Boolean>get() = _isLoading
private val _isLoading = MutableStateFlow<Boolean>(true)/**
* 加载成功后回来的列表 UI 状态,将 list 数据展示到列表上
*/
val newsList: StateFlow<MutableList<News>>get() = _newsList
private val _newsList = MutableStateFlow<MutableList<News>>(mutableListOf())/**
* 加载完成 UI 状态
*/
val loadingFinish: StateFlow<Boolean>get() = _loadingFinish
private val _loadingFinish = MutableStateFlow<Boolean>(false)/**
* 界面 toast UI 状态
*/
val toastMessage: StateFlow<String>get() = _toastMessage
private val _toastMessage = MutableStateFlow<String>("")
4)公开界面状态
这里选择数据流 StateFlow 公开界面状态。当然也可以选择 LiveData 公开界面状态。
5)使用/订阅界面状态
我这里使用的是数据流 StateFlow 公开的界面状态,所以在界面层相对应的使用 flow#collect 订阅界面状态。
数据模型驱动界面
结合上面几点,界面层的实现代码为:
3.1.1界面元素的实现:
class NewsActivity: ComponentActivity() {private var mBinding: ActivityNewsBinding? = nullprivate var mAdapter: NewsListAdapter? = nullprivate val mViewModel = NewsViewModel()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mBinding = ActivityNewsBinding.inflate(layoutInflater)setContentView(mBinding?.root)initView()initObserver()initData()}private fun initView() {mBinding?.listView?.layoutManager = LinearLayoutManager(this)mAdapter = NewsListAdapter()mBinding?.listView?.adapter = mAdaptermBinding?.refreshView?.setOnRefreshListener {mViewModel.refreshNewsData()}}private fun initData() {mViewModel.getNewsData()}private fun initObserver() {lifecycleScope.launch {lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {launch {mViewModel.isLoading.collect {if (it) {mBinding?.loadingView?.visibility = View.VISIBLE} else {mBinding?.loadingView?.visibility = View.GONE}}}launch {mViewModel.loadingError.collect {if (it) {mBinding?.loadingError?.visibility = View.VISIBLE} else {mBinding?.loadingError?.visibility = View.GONE}}}launch {mViewModel.loadingFinish.collect {if (it) {mBinding?.refreshView?.isRefreshing = false}}}launch {mViewModel.toastMessage.collect {if (it.isNotEmpty()) {showToast(it)}}}launch {mViewModel.newsList.collect {if (it.isNotEmpty()) {mBinding?.loadingError?.visibility = View.GONEmBinding?.loadingView?.visibility = View.GONEmBinding?.refreshView?.visibility = View.VISIBLEmAdapter?.setData(it)}}}}}}}
3.1.2状态容器的实现:
class NewsViewModel : ViewModel() {private val repository = NewsRepository()/*** 加载失败 UI 状态,显示失败图* 首屏获取的数据为空、首屏请求数据失败时展示失败图* 初始值:隐藏*/val loadingError: StateFlow<Boolean>get() = _loadingErrorprivate val _loadingError = MutableStateFlow<Boolean>(false)/*** 正在加载 UI 状态,显示加载中图* 首屏时请求网络时展示加载中图* 初始值:展示*/val isLoading: StateFlow<Boolean>get() = _isLoadingprivate val _isLoading = MutableStateFlow<Boolean>(true)/*** 加载成功后回来的列表 UI 状态,将 list 数据展示到列表上*/val newsList: StateFlow<MutableList<News>>get() = _newsListprivate val _newsList = MutableStateFlow<MutableList<News>>(mutableListOf())/*** 加载完成 UI 状态*/val loadingFinish: StateFlow<Boolean>get() = _loadingFinishprivate val _loadingFinish = MutableStateFlow<Boolean>(false)/*** 界面 toast UI 状态*/val toastMessage: StateFlow<String>get() = _toastMessageprivate val _toastMessage = MutableStateFlow<String>("")fun getNewsData() {viewModelScope.launch(Dispatchers.IO) {val list = repository.getNewsList()if (list.isNullOrEmpty()) {_loadingError.emit(true)} else {_newsList.emit(list)}}}fun refreshNewsData() {viewModelScope.launch(Dispatchers.IO) {val list = repository.getNewsList()_loadingFinish.emit(true)if (list.isNullOrEmpty()) {_toastMessage.emit("暂时没有更新数据")} else {_newsList.emit(list)}}}
}
3.2 数据层的实现
这里的数据层只有一个新闻列表数据结构的存储仓库 NewsRepository,另外获取新闻信息属于一次性操作,根据数据层架构设计,直接使用 suspend 就好。
class NewsRepository {suspend fun getNewsList(): MutableList<News>? {delay(2000)val list = mutableListOf<News>()val news = News("标题", "描述信息")list.add(news)list.add(news)list.add(news)list.add(news)return list}
}
3.3网域层的实现
网域层是可选的,是否具备网域层,跟架构是否为 MVVM 无关,这个案例中不适用网域层。
相关文章:
APP架构设计_2.用MVVM架构实现一个具体业务
2.MVVM架构图 3.MVVM 实现一个具体业务 3.1 界面层的实现 界面层实现时,需要遵循以下几点。 1)选择实现界面的元素 界面元素可以用 view 或 compose 来实现,这里用 view 实现。 2)提供一个状态容器 这里使用 ViewModel 作为状态容…...
安恒信息总裁宋端智,辞职了!活捉一枚新鲜出炉的餐饮人!
吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》http://mp.weixin.qq.com/s?…...
《javaEE篇》--定时器
定时器概念 当我们不需要某个线程立刻执行,而是在指定时间点或指定时间段之后执行,假如我们要定期清理数据库里的一些信息时,如果每次都手动清理的话就太麻烦,所以就可以使用定时器。定时器就可以比作一个闹钟,可以让…...
OpenLayers3, 缩放、平移、复位操作
文章目录 一、前言二、代码示例 一、前言 本文基于OpenLayers3实现地图缩放、平移和复位操作 二、代码示例 <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><htm…...
进程与线程(7)
IPC通信方式: 一、共享内存 system v : 共享内存 是一块,内核预留的空间 最高效的通信方式 (避免了用户空间 到 内核空间的数据拷贝) 二、IPC对象操作通用框架: key值 > 申请 》读写 》关闭 》卸载 1.ftok函数:…...
传知代码-自动化细胞核分割与特征分析(论文复现)
代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 引言 细胞核分割和分类在医学研究和临床诊断中具有重要意义。精准的细胞核分割能够帮助医生更好地识别和分析细胞核的形态学特征,从而辅助疾病诊断、癌症检测以及药物研发。HoverNet是一种基于深度学…...
Vue UI - 可视化的Vue项目管理器
概述 Vue CLI 3.0 更新后,提供了一套全新的可视化Vue项目管理器 —— Vue UI。所以要想使用它,你的 Vue CL I版本必须要在v3.0以上。 一、启动Vue UI 1.1 环境准备 1.1.1 安装node.js 访问官网(外网下载速度较慢)或 http://nod…...
团队管理之敏捷开发
一、敏捷实践 敏捷开发中一直秉承的理念和宣言是:我们正在通过亲身实践以及帮助他人实践,揭示更好的软件开发方法。通过这项工作,我们认为:个体和交互胜过过程和工具、可以工作的软件胜过面面俱到的文档、客户合作胜过合同谈判、…...
Hive3:表的常用修改语句
1、表重命名 ALTER TABLE old_table_name RENAME TO new_table_name;如: ALTER TABLE score4 RENAME TO score5;2、修改表属性值 ALTER TABLE table_name SET TBLPROPERTIES table_properties; table_properties:: (property_name property_value, property…...
MidJourney付费失败的原因以及失败后如何取消或续订(文末附MidJourney,GPT-4o教程)
MidJourney付费失败的原因 MidJourney付费失败的原因可能包括支付方式无效、支付信息错误、网络问题、账户设置问题等。 支付方式无效或信息错误:如果用户提供的支付方式(如信用卡)信息不正确,或者支付方式本身不支持该地区…...
PHP安全开发
安全开发 PHP 基础 增:insert into 表名(列名 1, 列名 2) value(‘列 1 值 1’, ‘列 2 值 2’); 删:delete from 表名 where 列名 ‘条件’; 改:update 表名 set 列名 数据 where 列名 ‘条件’; 查:select * from 表名 wher…...
【大模型从入门到精通32】开源库框架LangChain RAG 系统中的问答技术2
这里写目录标题 探索高级问答链类型MapReduce 和 Refine 技术 实用建议和最佳实践解决 RetrievalQA 限制结论进一步阅读和探索理论问题实践问题 探索高级问答链类型 MapReduce 和 Refine 技术 MapReduce 和 Refine 是设计用来规避由语言模型 (LM) 上下文窗口大小所导致的限制…...
MySQL 数据库管理
在 MySQL 中,数据库管理是非常基础但又至关重要的技能。无论是创建新的数据库、选择当前使用的数据库,还是查看数据库的相关信息,这些操作都是日常数据库管理中不可或缺的一部分。本文将详细介绍 MySQL 数据库管理的基本操作,包括…...
屏幕录制了一个视频,发现有些部分是不需要的,那么我们就用到视频剪辑的工具,利用必剪去删除中间的一部分视频,并且导出,然后利用格式工厂去压缩mp4文件的过程。
1、我们经常会去做一些视频教程或者软件的使用说明等等,做完了以后,会有增加字幕,或者去掉不需要一段视频。 2、打开必剪软件 3、点击【开始制作】 先将视频拖动到1的位置,然后将播放区中的视频,拖到2的区域ÿ…...
代码随想录跟练第六天——LeetCode
第454题.四数相加II 力扣题目链接(opens new window) 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] B[j] C[k] D[l] 0。 为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤…...
【Qt】常用控件QCalendarWidget的使用
常用控件QCalendarWidget的使用 QCalendarWidget表示一个日历 核心属性 属性说明 selectDate 当前选中的⽇期 minimumDate 最⼩⽇期 maximumDate 最⼤⽇期 firstDayOfWeek 每周的第⼀天(也就是⽇历的第⼀列) 是周⼏. gridVisible 是否显⽰表格的边框 selectionMode…...
Nginx: 配置项之main段核心参数用法梳理
概述 我们了解下配置文件中的一个全局段,有哪些配置参数,包括后面的 events 字段,有哪些配置参数这里面也有一些核心参数, 对于我们Nginx运行的性能也是有很重要的帮助我们现在首先关注整个 main 段的一个核心参数用法所谓 main 段ÿ…...
密码学之RSA算法
文章目录 1. RSA算法介绍1.2 算法历史与发展1.3 算法应用场景 2. RSA密钥生成2.1 选择素数2.2 计算公钥和私钥2.3 密钥长度与安全性 3 算法原理3.1 加密原理3.2 加密方法3.3 加密示例3.4 代码实现 4. 总结 1. RSA算法介绍 1.2 算法历史与发展 RSA算法由Ron Rivest、Adi Shami…...
教你学习企业高性能web服务器-nginx
一、web服务介绍 1、Apache的三种模型 (1)Apache prefork 预派生模式,有一个主控制进程,然后生成多个子进程,使用select模型,最大并发1024每个子进程有一个独立的线程响应用户请求相对比较占用内存&…...
封装通用第三方平台用户表(微信开放平台)
文章目录 一. 注册微信开放平台1.1 开发者资质认证1.2 应用申请1.3 配置应用 二.通用数据库表设计三.入库实体类四. 对接第三方平台4.1 微信开放平台VO对象4.2 通用方法 我们的系统可能要对接很多第三方系统,为了便利用户授权使用和对多平台账户的管理。有必要设计通…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
HTML 列表、表格、表单
1 列表标签 作用:布局内容排列整齐的区域 列表分类:无序列表、有序列表、定义列表。 例如: 1.1 无序列表 标签:ul 嵌套 li,ul是无序列表,li是列表条目。 注意事项: ul 标签里面只能包裹 li…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
以太网PHY布局布线指南
1. 简介 对于以太网布局布线遵循以下准则很重要,因为这将有助于减少信号发射,最大程度地减少噪声,确保器件作用,最大程度地减少泄漏并提高信号质量。 2. PHY设计准则 2.1 DRC错误检查 首先检查DRC规则是否设置正确,然…...
