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

Android Mvvm设计模式的详解与实战教程

一、介绍

        在开发设计模式中,模式经历了多次迭代,从MVC到MVP,再到如今的MVVM。发现的过程其实很简单,就是为了项目更好的管理。

        设计模式严格来说属于软件工程的范畴,但是如今在各大面试中或者开发中,设计模式被问的很多。特别是八股文的二十三种设计模式,可分三大类:行为型、结构型、创建型。

二、模式介绍

        模式的设计更多的体现在管理与架构能力,即使在项目中,你不用任何设计模式,代码也可以正常的跑起来,但是通过模式设计以后,在项目管理与质量控制,以及解耦等场景特别方便。

        任何设计模式和手段都是为了项目的更好管理,这种模式更像一种流程。从技术角度来分析,不能作为衡量一个人的技术好坏,但是可以作为参考,来判断一个人的综合能力以及设计、架构能力。

了解设计模式,可以提高一个人的综合能力。

三、MVMM详解与设计

MVVM是什么

        目前在做有UI展示的一些项目或者端,都在说MVVM设计模式。MVVM全程view-viewModel-Model。还是分为三层,View层,viewModel:view与业务层,Model数据业务层

View:是我们fragment或者Activity界面,主要处理UI渲染和交互的

viewModel:介于view与Module之前,处理数据与逻辑上的,将Model请求的结果返回给view层

Model:与viewModel打交道,将view需要的数据通过Model层来请求,然后将请求到的结果返回给viewModel。

Mvvm流程

        在Mvvm设计模式中,view主要就是做数据与UI的绑定,常见的View与Model没有直接交互,需要申明都是通过ViewModel进行交互的。ViewModel从名字就可以看出,是View与Model的拼写,所以肯定是与View和Moel有关,在MVVM中,ViewModel的核心作用就是作为View与Model的桥梁,将View的需求告诉Model,然后从Model中将结果拿到,处理好给View,View进行渲染。

Mvvm的助手databinding:

        通过Google官方我们也了解到,MVVM的推波导致了DataBinding的被很好的推广,在Mvvm中,View中的绑定和页面的view是通过Databinding来完成,很多开发者可能还没体验过DataBinding,甚至也没有使用过,这个也不影响到Mvvm的使用,因为DataBinding只负责View与Data的绑定,即使你不会可以手动处理。

如果想了解DataBinding的小伙伴,可以看博主一下的文档:

Android databinding的接入使用与详解(一)_android databinding使用_蜗牛、Z的博客-CSDN博客

Android databinding之RecycleView使用与讲解(二)_android databinding recyclerview_蜗牛、Z的博客-CSDN博客

Android databinding之数据单向与双向绑定详解与使用(三)_databinding双向绑定_蜗牛、Z的博客-CSDN博客

Android databinding之BindingAdapter与BindingConversion详解与使用(四)_蜗牛、Z的博客-CSDN博客

Android databinding之BindingMethod与BindingMethods介绍与使用(五)_android开发bindingmethods的使用_蜗牛、Z的博客-CSDN博客

Android DataBinding之布局include 和 viewStub详解与使用(六)_databinding viewstub_蜗牛、Z的博客-CSDN博客

Android DataBinding之布局中(layout)事件、运算逻辑、资源、工具类的使用与详解(七)_databinding layout_蜗牛、Z的博客-CSDN博客

新手礼包:

        这几篇文档基本都是从零开始学databinding的文章,只要你跟着看,慢慢实践里面的dmeo,都是没问题的。以上新手礼包可以帮你很好的处理Mvvm中数据的绑定

数据传递MutableLiveData:

通过以上知道ViewModel与View进行数据交互,他们之间如何传递数据?在viewmodel中提供了

MutableLiveData,它继承了LiveData,类似事件订阅,你发送了数据,在view中订阅即可,数据将会回传到订阅处。LiveData也支持生命周期的绑定,防止数据订阅中,当前页面销毁,数据还在订阅,导致页面发生内存泄露。

四、实战

        通过以上的学习,我们大概了解了Mvvm包括哪些?如何架构和搭建MVVM。通过关键模块的处理我们将继续学习实战。

依赖库:

    implementation "androidx.lifecycle:lifecycle-viewmodel:2.0.0"implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

Model模块

model主要是数据处理,在搭建代码的时候,我们最好有良好的编程风格,可以先搭建一个基础、抽象类,后面的model都继承直接使用,即使是空类也要写一个,方便以后扩展。

open abstract class BaseModel {
}
//一般梳理数据类比较多
class MyModel : BaseModel() {//网络数据的获取//数据库的操作//本地的数据缓存:添加与删除public fun getInitData():String{return "init data that info"}}

Model的是和数据打交道,处理好,我们就可以处理搭建ViewModel了

ViewModel层:

ViewModel层夹在View与model之间,是逻辑的中转,view要什么找ViewModel,model通过ViewModel把数据传递给View。

ViewModel这里面主要有两块:搭建和创建

搭建

搭建需要继承public AndroidViewModel(@NonNull Application application),由于ViewModel需要与View合作,所以还需要了解当前的对象生命周期防止内存泄露等发生。

这里需要使用到接口:LifecycleObserver

LifecycleObserver用法:

直接继承,然后通过状态绑定对应的方法,当lifecycle绑定完就可以分发生命周期状态

  @OnLifecycleEvent(Lifecycle.Event.ON_START)public fun onstart(){log("onstart")}
1、生命状态回调

//定义了页面监听生命周期监听
interface BaseViewModelLifecycleObserver : LifecycleObserver {companion object{const val TAG="life"}@OnLifecycleEvent(Lifecycle.Event.ON_START)public fun onstart(){log("onstart")}@OnLifecycleEvent(Lifecycle.Event.ON_STOP)public fun onStop() {log("onStop")}@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)public fun onResume() {log("onResume")}@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)public fun onPause() {log("onPause")}@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)public fun ondestory() {log("ondestory")}public fun log(msg:String){Log.e(TAG,"${msg}")}
}
2、BaseViewModel
abstract open class BaseViewModel(val context: Application) : AndroidViewModel(context),BaseViewModelLifecycleObserver {}
3、定义业务的ViewModel
class MyViewModel(context: Application) : BaseViewModel(context) {private val model: MyModel by lazy { MyModel() }public val initLiveData: MutableLiveData<String> by lazy { MutableLiveData() }public fun showToast(msg: String) {Toast.makeText(getApplication(), msg, Toast.LENGTH_SHORT).show()}public fun initData() {val data=model.getInitData()initLiveData.value=data}}

这里面多了一个MutableLiveData,就是数据回传的订阅,ViewModel发送了数据给View通过LiveData进行订阅。在这里面要创建Model对象

创建

创建这里面涉及到了kotlin的泛型问题,相比Java,kotlin的泛型比较复杂,由于创建是和DataBinding绑在一起的,这边先介绍泛型的创建与生命周期的绑定

    val type = javaClass.genericSuperclassif (type != null && type is ParameterizedType) {val actualTypeArguments = type.actualTypeArgumentsval tClass = actualTypeArguments[1]
viewModel= AndroidViewModelFactory.getInstance(application).create(tClass as Class<V>)}lifecycle.addObserver(viewModel)

    lifecycle.addObserver(viewModel):绑定了当前的生命周期

注意:在需要消费,否则导致内存泄露

    override fun onDestroy() {super.onDestroy()lifecycle.removeObserver(viewModel)}

View模块

View模块主要就是DataBinding的创建与ViewMOdel的创建,ViewModel上面已介绍如何创建,下面将介绍如何去架构这个基础页面,去管理

这边架构的页面分为两个类:第一个是DataBinding类,还有一个是DataBinding与ViewModel继承类,为什么要分开?因为在正常业务中,有些模块不需要ViewModel,但是DataBinding是必须使用,所以只需要继承DataBinding类即可。

DataBinding基础类:
 

open abstract class BaseDataBindModelActivity<T : ViewDataBinding>() : FragmentActivity() {public lateinit var binding: T//配置当前页面布局资源@LayoutRespublic abstract fun getLayoutResId(): Intoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = DataBindingUtil.setContentView<T>(this, getLayoutResId())initViewModel()initObser()initDataBeforInitView()initVie()initDataAfterInitView()}//初始化viewmodelopen protected fun initViewModel() {}//初始化页面abstract fun initVie()//初始化livedata的监听,abstract fun initObser()//获取数据在initview之前abstract fun initDataBeforInitView()//获取数据在initview之后abstract fun initDataAfterInitView()//页面消费之前,解绑override fun onDestroy() {if (binding != null) {binding.unbind()}super.onDestroy()}}

ViewModelDataBinding基础类:

abstract class BaseViewModelBindActivity<T : ViewDataBinding, V : BaseViewModel>() :BaseDataBindModelActivity<T>() {lateinit var viewModel: Voverride fun initViewModel() {super.initViewModel()val type = javaClass.genericSuperclassif (type != null && type is ParameterizedType) {val actualTypeArguments = type.actualTypeArgumentsval tClass = actualTypeArguments[1]viewModel= AndroidViewModelFactory.getInstance(application).create(tClass as Class<V>)}lifecycle.addObserver(viewModel)}override fun onDestroy() {super.onDestroy()lifecycle.removeObserver(viewModel)}}

通过以上的配置,基本已完成了MVVM的搭建。

小时牛刀:

class MyTestViewBindActivity : BaseViewModelBindActivity<TestViewBind, MyViewModel>() {override fun getLayoutResId(): Int {return R.layout.layout_test_view_bind}override fun initVie() {binding.btnTest.setOnClickListener {viewModel.showToast("你好")}binding.btnInit.setOnClickListener {//调用了初始化事件viewModel.initData()}}override fun initObser() {//数据订阅事件viewModel.initLiveData.observe(this) {binding.textInfo.text = it}}override fun initDataBeforInitView() {}override fun initDataAfterInitView() {}
}

绑定的页面生命周期回调:

运行效果

五、总结

1.通过以上学习,完成了MVVM的基础结构与如何架构一个MVVM页面出来。Demo中也基本完成了大家的难点问题。

2.如果不了解生命周期的可以参考Demo中直接拿去用,避免处理不好导致内存泄露

3.MVVM需要databinding的参与,如果不了解的小伙伴直接看我的DataBinding文章,看完直接上项目使用是没任何问题,有问题我得文章中也会提出,这边都是干货,都是博主自己总结与写出来的。希望大家能够受益。

相关文章:

Android Mvvm设计模式的详解与实战教程

一、介绍 在开发设计模式中&#xff0c;模式经历了多次迭代&#xff0c;从MVC到MVP&#xff0c;再到如今的MVVM。发现的过程其实很简单&#xff0c;就是为了项目更好的管理。 设计模式严格来说属于软件工程的范畴&#xff0c;但是如今在各大面试中或者开发中&#xff0c;设计模…...

软考A计划-系统集成项目管理工程师-小抄手册(共25章节)-下

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…...

渗透测试是什么?怎么做?

渗透测试报告 一、什么是渗透测试&#xff1f; 渗透测试是可以帮助用户对目前自己的网络、系统、应用的缺陷有相对直观的认识和了解。渗透测试尽可能地以黑客视角对用户网络安全性进行检查&#xff0c;对目标网络、系统和应用的安全性作深入的探测&#xff0c;发现系统最脆弱的…...

【软件安装】Python安装详细教程(附安装包)

软件简介 Python由荷兰数学和计算机科学研究学会的Guido van Rossum 于1990 年代初设计&#xff0c;作为一门叫做ABC语言的替代品。Python提供了高效的高级数据结构&#xff0c;还能简单有效地面向对象编程。Python语法和动态类型&#xff0c;以及解释型语言的本质&#xff0c…...

微信小程序的form表单提交

获取input有两种方法&#xff1a; 第一&#xff1a;bindsubmit方法 注意&#xff1a; 1.使用form里面定义bindsubmit事件 2.bindsubmit事件需要配合button里面定义的formType“submit” 操作 3.设置input的name值来获取对应的数据 <form bindsubmit"formSubmit"…...

WOFOST模型与PCSE模型应用

实现作物产量的准确估算对于农田生态系统响应全球变化、可持续发展、科学粮食政策制定、粮食安全维护都至关重要。传统的经验模型、光能利用率模型等估产模型原理简单&#xff0c;数据容易获取&#xff0c;但是作物生长发育非常复杂&#xff0c;中间涉及众多生理生化过程&#…...

5-W806-RC522-SPI

main.c #include <stdio.h> #include "wm_hal.h" #include "rc522.h"int main(void) {SystemClock_Config(CPU_CLK_160M);printf("enter main\r\n");HAL_Init();RC522_Init();PcdReset();M500PcdConfigISOType ( A );//设置工作方式IC_te…...

Python实现自动登录+获取数据

前言 Dy这个东西想必大家都用过&#xff0c;而且还经常刷&#xff0c;今天就来用代码&#xff0c;获取它的视频数据 环境使用 Python 3.8 Pycharm 模块使用 requests selenium json re 一. 数据来源分析 1. 明确需求 明确采集网站以及数据内容 网址: https://www.dy.co…...

yolov8热力图可视化

安装pytorch_grad_cam pip install grad-cam自动化生成不同层的bash脚本 # 循环10次&#xff0c;将i的值从0到9 for i in $(seq 0 13) doecho "Running iteration $i";python yolov8_heatmap.py $i; done热力图生成python代码 import warnings warnings.filterwarn…...

【SpringBoot】第一篇:redis使用

背景&#xff1a; 本文是教初学者如何正确使用和接入redis。 一、引入依赖 <!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><depen…...

Springboot profile多环境配置

1. 前言 profile用于多环境的激活和配置&#xff0c;用来切换生产&#xff0c;测试&#xff0c;本地等多套不通环境的配置。如果每次去更改配置就非常麻烦&#xff0c;profile就是用来切换多环境配置的。 2. 配置方法 三种方式。 2.1 多profile文件方式 在resource目录下新…...

(1)进程与线程区别

1.什么是线程、进程 进程&#xff1a;操作系统资源分配的基本单位线程&#xff1a;处理器任务调度和执行的基本单位。 一个进程至少有一个线程&#xff0c;线程是进程的一部分&#xff0c;所以线程也被称为轻权进程或者轻量级进程。 2.并行与并发 一个基本的事实前提&#x…...

学习JAVA打卡第四十天

对象的字符串表示 在此类中我们讲过&#xff0c;所有的类都默认是java.lang包中object类的子类或间接子类。 Object类有一个public String toString&#xff08;&#xff09;方法,一个对象通过调用该方法可以获得该对象的字符串表示。一个对象调用toString法&#xff08;&…...

【跟小嘉学 Rust 编程】十四、关于 Cargo 和 Crates.io

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…...

防关联指纹浏览器:高效地管理你的Facebook账户

Facebook&#xff0c;作为全球最受欢迎社交平台的第一名已经成为我们日常和工作中不可或缺的一部分了。不管是用于日常分享、媒体营销、还是店铺运营&#xff0c;Facebook都占据着重要的位置。多个Facebook账户的优势非常明显&#xff0c;然而&#xff0c;当你需要同时管理他们…...

前端学习记录~2023.8.15~JavaScript重难点实例精讲~第7章 ES6(1)

第 7 章 ES6 前言7.1 let关键字和const关键字7.1.1 let关键字&#xff08;1&#xff09;let关键字的特性&#xff08;2&#xff09;使用let关键字的好处 7.1.2 const关键字&#xff08;1&#xff09;const关键字的特性 7.2 解构赋值7.2.1 数组的解构赋值&#xff08;1&#xff…...

WebSocket详解以及应用

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;websocket、网络、长连接、前端☀️每日 一言&#xff1a;任何一个你不喜欢而又离不开的地方&#xff0c;任何一种你不喜欢而又无法摆脱的生活&#xff0c;都是监狱&#xff01; 一、前言 我们在…...

如何评估开源项目的活跃度和可持续性?

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…...

远程Linux/ubuntu服务器后台不间断运行py文件/sh脚本

通常我们在生产环境中运行一些项目时需要将程序不间断的运行在服务器上&#xff0c;并且将日志文件打印到某个文件中&#xff0c;直到程序运行结束&#xff0c;下面介绍了在Linux服务器上不间断运行py文件的方式&#xff0c;以及如何保存相应的日志信息。 对于 .py 文件&#x…...

记录一个诡异的bug

将对接oa跳转到会议转写的项目oa/meetingtranslate项目发布到天宫&#xff0c;结果跳转到successPage后报错 这一看就是successPage接口名没对上啊&#xff0c;查了一下代码&#xff0c;没问题啊。 小心起见&#xff0c;我就把successPage的方法请求方式从Post改为Get和POST都…...

Xamarin.Android中的Fragment

目录 1、Activity中使用Fragment2、Fragment与Activity通信3、Fragment与其他的Fragment通信 1、Activity中使用Fragment 一般而言&#xff0c;会在activity中添加一个加载fragment的方法。通过点击菜单的按钮&#xff0c;加载不同的fragment。其样子一般是这样的&#xff1a;…...

portainer初体验

官方文档 安装 docker 这里采用的的是国内汉化的一个镜像&#xff0c;版本号2.16.2。 地址 docker run -d --restartalways --name"portainer" -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock 6053537/portainer-ce体验 访问9000端口。 尝试&#x…...

4G数传方案(合宙cat1模块)

一. 合宙Cat1简介 合宙 Air724 模组推出的低功耗&#xff0c;超小体积&#xff0c;高性能嵌入式 4G Cat1 核心版&#xff0c;标准的 2.54 排针、最小成本的进项 2G、4G Cat4 切换&#xff1b;主要功能如下: 实际测试工作环境为-35℃-75℃&#xff1b; 支持 5-12V 供电或者 3.7…...

ElasticSearch - 海量数据索引拆分的一些思考

文章目录 困难解决方案初始方案及存在的问题segment merge引入预排序 拆分方案设计考量点如何去除冗余数据按什么维度拆分&#xff0c;拆多少个最终的索引拆分模型演进历程整体迁移流程全量迁移流程流量回放比对验证异步转同步多索引联查优化效果 总结与思考参考 困难 索引数据…...

【SA8295P 源码分析】83 - SA8295P HQNX + Android 完整源代码下载方法介绍

【SA8295P 源码分析】83 - SA8295P HQNX + Android 完整源代码下载方法介绍 一、高通官网 Chipcode 下载步骤介绍1.1 高通Chipcode 下载步骤1.2 高通 ReleaseNote 下载方法二、高通 HQX 代码介绍2.1 完整的 HQX 代码结构:sa8295p-hqx-4-2-4-0_hlos_dev_qnx.tar.gz2.2 sa8295p-…...

【设计模式--原型模式(Prototype Pattern)

一、什么是原型模式 原型模式&#xff08;Prototype Pattern&#xff09;是一种创建型设计模式&#xff0c;它的主要目的是通过复制现有对象来创建新的对象&#xff0c;而无需显式地使用构造函数或工厂方法。这种模式允许我们创建一个可定制的原型对象&#xff0c;然后通过复制…...

初识 Redis

初识 Redis 1 认识NoSQL1.1 结构化与非结构化1.2 关联和非关联1.3 查询方式1.4. 事务1.5 总结 2 Redis 概述2.1 应用场景2.2 特性 3 Resis 全局命令4 Redis 基本数据类型4.1 String4.1.1 常用命令4.1.2 命令的时间复杂度4.1.3 使用场景 4.2 Hash4.2.1 常用命令4.2.2 命令的时间…...

php灵异事件,啥都没干数据变了?

这篇文章也可以在我的博客查看 搞WordPress&#xff0c;难免跟php打交道 然而这弱类型语言实在坑有点多 这不今儿又踩了个大坑直接时间-1&#x1f605; 问题 话不多说直接上代码 <?php $items [1,2];foreach ($items as &$item) {/*empty loop*/} print_r($items)…...

【ffmpeg】基于需要使用videocapture的opencv编译配置(C++)

目录 配置简介ffmpeg源码编译方法记录gstreamer命令行安装方法opencv的编译项记录 配置简介 opencv使用videocapture读取视频流时&#xff0c;需要借助底层的ffmpeg库。如果不能正确编译&#xff0c;会报错&#xff0c;现记录正确编译配置方法。 ffmpeg源码编译方法记录 ope…...

Redisson分布式锁 原理源码 分析

# 基于setnx实现的分布式锁存在的问题&#xff1a; # 为了解决上面的问题&#xff0c;可以用Redisson # Redisson入门 # Redisson可重入锁原理 获取锁的Lua脚本&#xff1a; 释放锁的Lua脚本&#xff1a; # 锁重试原理分析 tryLock&#xff08;&#xff09;底层代码分析 tim…...

南通做网站ntwsd/中国万网域名注册

2016 Multi-University Training Contest 1 solutions BY HIT 昨天多校第一次&#xff0c;重温了一下好久没体验过的爆零的感觉。 不知道是谁很早就做出来了1007题&#xff0c;和队友看了一个小时的1007。组合数学摆了半天越搞越晕。 我弃了搞1001。谁知道一搞就是“永生”…...

秦皇岛建设网站公司/友情链接交换系统

前因&#xff0c;没有比摸鱼有趣的事了 距离自己被外派(俗称外包)出去&#xff0c;已经过了快五个月&#xff0c;工作的话&#xff0c;很闲。人啊&#xff0c;一定保持好的习惯&#xff0c;懒惰是会上瘾&#xff0c;日常摸鱼&#xff0c;怀疑人生&#xff0c;我是谁&#xff0c…...

网站集群建设方案/上海网站seo诊断

上一篇简单的讲了下Handler的使用&#xff0c;本篇就解析一下Andorid消息处理机制的内部实现&#xff0c;到底是如何为绑定的线程创建looper对象和消息队列的&#xff0c;又是怎么实现消息添加、消息遍历和消息分发的。 首先需要介绍几个非常重要的角色&#xff1a;Looper、Han…...

wordpress快速建站/企业网站制作开发

适用系统&#xff1a;PC(后台回复「243」获取)大概有很多人会和学霸君一样&#xff0c;在一些需要注册的网站上都会使用同一个密码&#xff0c;例如各种论坛&#xff0c;视频网站等等。因为这样会方便记忆&#xff0c;但是有一种情况你们想过没有&#xff1f;如果你某一个网站的…...

曲靖网站制作一条龙/游戏推广怎么找玩家

Atlassian In Action-Jira之指导思想&#xff08;一&#xff09; 太上&#xff0c;不知有之&#xff1b;其次&#xff0c;亲而誉之&#xff1b;其次&#xff0c;畏之&#xff1b;其次&#xff0c;侮之。信不足焉&#xff0c;有不信焉。悠兮&#xff0c;其贵言。功成事遂&#x…...

出名的设计网站/足球世界排名国家

转载于:https://www.cnblogs.com/sheying/p/8509047.html...