Android 架构模式

MVC

MVC是 Model-View-Controller 的简称。

  • M:模型层(Model) 负责与数据库和网络层通信,并获取和存储应用的数据;
  • V:视图层(View) 负责将 Model 层的数据做可视化的处理,同时处理与用户的交互;
  • C:控制层(Controller) 用于建立Model层与View层的联系,负责处理主要的业务逻辑,并根据用户操作更新 Model 层数据。

在 Android 项目的 MVC 架构中由于 Activity 同时充当了 View 层与 Controller 层两个角色,所以 Android 中的 MVC 更像下面的这种结构:

Model层

LogingListener是一个接口:

  public interface LoginListener {void onSuccess(User data);void onFailed(String msg);
}
public class LoginModel {public void login(String username, String password, LoginListener listener) {RetrofitManager.getApiService().login(username, password).enqueue(new Callback<User>() {@Overridepublic void onResponse(Call<User> call, Response<User> response) {listener.onSuccess(response.data);}@Overridepublic void onFailed(Exception e) {listener.onFailed(e.getMessage());}});}
}

Controller/View层 

public class MainActivity extends AppCompactActivity implements LoginListener {private LoginModel loginModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);model = new LoginModel();findViewbyId(R.id.btn_fetch_data).setOnClickListener(view -> {String username = findViewById(R.id.et_username).getText().toString();String password = findViewById(R.id.et_password).getText().toString();loginModel.login(username, password, this);});}@Overridepublic void onSuccess(User data) {// Update UI}@Overridepublic void onFailed(String msg) {// Update UI}
}

不足: 

  • Android中的MVC代码对于分层并不明确,导致了Controller层与View层混为一体。
  • Model 层与 View 层存在耦合,存在相互依赖关系。
  • 开发时不注重分层,Model层代码也被塞进了Activity/Fragment,使得代码层次更加混乱。

MVP

针对以上MVC架构中存在的问题,我们可以在MVC的基础上进行优化解决。

  • 即从Activity中剥离出控制层的逻辑,并阻断Model层与View层的耦合。
  • Model层不直接与View通信,而是在数据改变时让 Model通知控制控制层,控制层再通知View层去做界面更新,这就是MVP的架构思想。
  • M:模型层(Model) 与MVC中的一致,同样是负责与数据库和网络层通信,并获取和存储应用的数据,区别在于Model层不再与View层直接通信,而是与Presenter层通信。
  • V:视图层(View) 负责将 Model 层的数据做可视化的处理,同时与Presenter层交互。跟MVC相比,MVP的View层与Model层不再耦合。
  • P:控制层(Presenter) 主要负责处理业务逻辑,并监听Model层数据改变,然后调用View层刷新UI。

MVP 的结构图如下图所示: 

 

从上图中可以看出:

  • View直接与Presenter层通信,当View层接收到用户操作后会调用 Presenter层去处理业务逻辑。
  • Presenter层通过Model去获取数据,Model层获取到数据后又将最新的数据传回 Presenter。
  • 而Presenter层又持有View层的引用,进而将数据传给View层进行展示。

Model层

LogingListener是一个接口:

  public interface LoginListener {void onSuccess(User data);void onFailed(String msg);
}
public class LoginModel {public void login(String username, String password, LoginListener listener) {RetrofitManager.getApiService().login(username, password).enqueue(new Callback<User>() {@Overridepublic void onResponse(Call<User> call, Response<User> response) {listener.onSuccess(response.data);}@Overridepublic void onFailed(Exception e) {listener.onFailed(e.getMessage());}});}
}

Presenter 层代码

  • 由于 Presenter 需要通知 View 层更新UI,因此需要持有View。
  • Presenter也需要与Model层通信,因此Presenter层也会持有Model层。
  • 在用户触发登录操作后,调用Presenter的登录逻辑,Presenter通过Model进行登录操作,根据登录结果反馈给View层做不同的更新操作。

此时可将,view中的更新方法抽象出一个 View 接口 ,在View中实现这个接口。

public interface ILoginView {void showLoading();void dismissLoading();void loginSuccess(User data);void loginFailer(String errorMsg);
}
public class LoginPresenter {// Model 层 private LoginModel model;// View 层 private ILoginView view;public LoginPresenter(LoginView view) {this.model = new LoginModel();this.view = view;}public void login(String username, String password) {view.showLoading();model.login(username, password, new LoginListener() {@Overridepublic void onSuccess(User user) {view.loginSuccess(user);view.dismissLoading();}@Overridepublic void onFailed(String msg) {view.loginFailer(msg);view.dismissLoading();}});}@Overridepublic void onDestory() {// recycle instance.}
}

View 层代码

  • 在View中实现更新View的接口
  • 在View中持有Presenter来处理业务逻辑
public class MainActivity extends AppCompatActivity implements ILoginView {private LoginPresenter presenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);presenter = new LoginPresenter(this);findViewById(R.id.button).setOnClickListener(v -> {String username = findViewById(R.id.et_username).getText().toString();String password = findViewById(R.id.et_password).getText().toString();presenter.login(username, password);});}@Overridepublic void loginSuccess(User user) {// Update UI.}@Overridepublic void loginFailer(String msg) {// Update UI.}@Overrideprotected void onDestroy() {super.onDestroy();presenter.onDestroy();}@Overridepublic void showLoading() {}@Overridepublic void dismissLoading() {}
}

MVP的本质是面向接口编程,实现依赖倒置原则。可以看得出来MVP架构在分层上相比 MVC更加清晰明确,且解耦了Model层与View层。但MVP也存在一些弊端。

不足:

  • 引入大量的 IView、IPresenter接口实现类,增加项目的复杂度。
  • View 层与 Presenter 相互持有,存在耦合。

MVVM

MVVM是 ** Model-View-ViewModel** 的简称。

  • M:模型层(Model) 与MVP中的Model层一致,负责与数据库和网络层通信,获取并存储数据。与MVP的区别在于Model层不再通过回调通知业务逻辑层数据改变,而是通过观察者模式实现。
  • V:视图(View) 负责将Model层的数据做可视化的处理,同时与ViewModel层交互。
  • VM:视图模型(ViewModel) 主要负责业务逻辑的处理,同时与 Model 层 和 View层交互。与MVP的Presenter相比,ViewModel不再依赖View,使得解耦更加彻底。

 

MVVM架构的本质是数据驱动,它的最大的特点是单向依赖。MVVM架构通过观察者模式让ViewModel与View解耦,实现了View依赖ViewModel,ViewModel依赖Model的单向依赖。

观察者模式

自定义观察者

  • 实现观察者接口
public interface Observer<T> {// 成功的回调void onSuccess(T data);// 失败的回调void onFailed(String msg);
}
  • 抽象被观察者
public abstract class Observable<T> {// 注册观察者void register(Observer observer);// 取消注册void unregister(Observer observer);// 数据改变(设置成功数据)protected void setValue(T data) {}// 失败,设置失败原因void onFailed(String msg);
}
  • 被观察者具体实现
public class LoginObservable implements Observable<User> {private final List<Observer<User>> list = new ArrayList<>();private User user;@Overridepublic void register(Observer<User> observer) {list.add(observer);}@Overridepublic void unregister(Observer<User> observer) {liset.remove(observer);}@Overridepublic void setValue(User user) {this.user = user;for (Observer observer : list) {observer.onSuccess(user);}}@Overridepublic void onFailed(String msg) {for (Observer observer : list) {observer.onFailed(msg);}}
}

Model层

Model层最大的特点是被赋予了数据获取的职责,与我们平常Model层只定义实体对象的行为截然不同。

实例中,数据的获取、存储、数据状态变化都是Model层的任务。Model包括实体模型(Bean)、Retrofit的Service ,获取网络数据接口,本地存储(增删改查)接口,数据变化监听等。

Model提供数据获取接口供ViewModel调用,经数据转换和操作并最终映射绑定到View层某个UI元素的属性上。

public class LoginModel {public void login(String username, String password, LoginObservable observable) {RetrofitManager.getApiService().login(username, password).enqueue(new Callback<User>() {@Overridepublic void onResponse(Call<User> call, Response<User> response) {observable.setValue(response.data);}@Overridepublic void onFailed(Exception e) {observable.onFailed(e.getMessage());}});}
}

ViewModel层

ViewModel层需要持有Model层,并且ViewModel层持有一个LoginObservable,并开放一个getObservable的方法供View层使用。

public class LoginViewModel {private LoginObservable observable;private LoginModel loginModel;public LoginViewModel() {loginModel = new LoginModel();observable = new LoginObservable();}public void login(String username, String password) {loginModel.login(username, password, observable);}// 这里返回值是父类Observable,即View层得到的Observable无法调用setValuepublic Observable getObservable() {return observable;}
}

View层

View层持有ViewModel,用户触发登录事件通过View层交给Model处理,Model层在登录成后将数据交给ViewModel中的观察者。

因此,View层只需要获取到ViewModel层的观察者并观察到数据改变后更新UI即可。

public class MainActivity extends AppCompatActivity implements Observer<User> {private LoginViewModel viewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);viewModel = new LoginViewModel();viewModel.getObservable().register(this);findViewById(R.id.button).setOnClickListener(v -> {String username = findViewById(R.id.et_username).getText().toString();String password = findViewById(R.id.et_password).getText().toString();viewModel.login(username, password);});}@Overridepublic void onDestroy() {super.onDestory();viewModel.getObservable().unregister(this);}@Overridepublic void onSuccess(User user) {// fetch data success,update UI.}@Overridepublic void onFailed(String message) {// fetch data failed,update UI.}
}

Jetpack组件实现 MVVM模式

为了实现MVVM架构,Google提供了一套强大的Jetpack组件,包括LiveData、ViewModel和Data Binding等。这些组件可以帮助开发人员更轻松地实现MVVM架构,并提供了许多现代化的开发工具和技术。

LiveData

Android --- LiveData-CSDN博客

DataBinding

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

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

相关文章

vue3使用vant4的列表vant-list点击进入详情自动滚动到对应位置,踩坑日记(一天半的踩坑经历)

1.路由添加keepAlive <!-- Vue3缓存组件&#xff0c;写法和Vue2不一样--><router-view v-slot"{ Component }"><keep-alive><component :is"Component" v-if"$route.meta.keepAlive"/></keep-alive><component…

本地Navicat/客户端连接阿里云RDSMySQL时遇到过的问题及解决

1.之前开发的RDS MySQL版本和本地MySQL版本最好接近&#xff0c;比如8.0.28和8.0.20好像都是可以兼容的&#xff0c;他们里面都有那个utf8的字符编码&#xff0c;但是后面我选的RDS MySQL版本有点新&#xff0c;是8.0.30甚至更新的版本&#xff0c;之前用C#语言写的连接MySQL以…

艺术家电gorenje x 设计上海丨用设计诠释“生活的艺术”

2024年6月19日—22日&#xff0c;艺术家电gorenje亮相“设计上海”2024&#xff0c;以“gorenje是家电更是艺术品”为题&#xff0c;为人们带来融入日常的艺术之美。设计上海2024不但汇集了国内外卓越设计品牌和杰出独立设计师的家具设计作品&#xff0c;还联合国内外多名设计师…

每日一题(6.22-6.28)

(&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨&#xff0c;中途考电路分析去了&#xff0c;空了几天的题没有练&#xff0c;为什么三相电路他都没讲过的都要考啊&#xff1f;我服了&#xff0c;什么在Y型三相电路&#xff0c;线电压和相电压的比值都考&…

Spark join数据倾斜调优

Spark中常见的两种数据倾斜现象如下 stage部分task执行特别慢 一般情况下是某个task处理的数据量远大于其他task处理的数据量&#xff0c;当然也不排除是程序代码没有冗余&#xff0c;异常数据导致程序运行异常。 作业重试多次某几个task总会失败 常见的退出码143、53、137…

2095.删除链表的中间节点

给你一个链表的头节点 head 。删除链表的中间节点 &#xff0c;并返回修改后的链表的头节点 head。 长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点&#xff08;下标从 0 开始&#xff09;&#xff0c;其中 ⌊x⌋ 表示小于或等于 x 的最大整数。 对于 n 1、2、3、4 和…

【机器学习】机器学习重要方法——迁移学习:理论、方法与实践

文章目录 迁移学习&#xff1a;理论、方法与实践引言第一章 迁移学习的基本概念1.1 什么是迁移学习1.2 迁移学习的类型1.3 迁移学习的优势 第二章 迁移学习的核心方法2.1 特征重用&#xff08;Feature Reuse&#xff09;2.2 微调&#xff08;Fine-Tuning&#xff09;2.3 领域适…

matlab仿真 通信信号和系统分析(上)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第三章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; 一、求离散信号卷积和 主要还是使用卷积函数conv&#xff0c;值得注意的是&#xff0c;得到的卷积和长度结果为81&#xff0…

Windows USB设备驱动开发 - 常见概念的解释

我们听到许多 USB 术语几乎交替抛出。 它们都是什么意思&#xff1f;假设我们看到类似 “多亏了 USB 3.0&#xff0c;我可以将 SuperSpeed U 盘连接到电脑的 xHCI 主机控制器&#xff0c;并更快地复制文件。” 让我们了解该句子中的 USB 术语。 USB 3.0、USB 2.0 和 USB 1.0 请…

[深度学习] 自编码器Autoencoder

自编码器&#xff08;Autoencoder&#xff09;是一种无监督学习算法&#xff0c;主要用于数据的降维、特征提取和数据重建。自编码器由两个主要部分组成&#xff1a;编码器&#xff08;Encoder&#xff09;和解码器&#xff08;Decoder&#xff09;。其基本思想是将输入数据映射…

Redis 7.x 系列【9】数据类型之自动排重集合(Set)

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 前言2. 常用命令2.1 SADD2.2 SCARD2.3 SISMEMBER2.4 SREM2.5 SSCAN2.6 SDIFF2.7 SU…

海云安参编《数字安全蓝皮书 》正式发布并入选《2024中国数字安全新质百强》荣膺“先行者”

近日&#xff0c;国内数字化产业第三方调研与咨询机构数世咨询正式发布了《2024中国数字安全新质百强》&#xff08;以下简称百强报告&#xff09;。海云安凭借在开发安全领域的技术创新力及市场影响力入选百强报告“新质百强先行者” 本次报告&#xff0c;数世咨询经过对国内8…

以算筑基,以智赋能 | Gooxi受邀出席2024中国智算中心全栈技术大会

6月25日&#xff0c;2024中国智算中心全栈技术大会暨展览会、第5届中国数据中心绿色能源大会暨第10届中国&#xff08;上海&#xff09;国际数据中心产业展览会在上海新国际博览中心隆重召开。Gooxi受邀参与并携最新服务器产品以及解决方案亮相展会&#xff0c;吸引众多行业领袖…

C++学习/复习18----迭代器/反向迭代器及在list/vector中的应用、list与vector模拟实现复习

迭代器是一个对象&#xff0c;可以循环访问 C 标准库容器中的元素&#xff0c;并提供对各个元素的访问。 C 标准库容器全都提供迭代器&#xff0c;以便算法可以采用标准方式访问其元素&#xff0c;而不必考虑用于存储元素的容器类型。 一、反向迭代器类 基于普通迭代器构建反…

亚太杯赛题思路发布(中文版)

导读&#xff1a; 本文将继续修炼回归模型算法&#xff0c;并总结了一些常用的除线性回归模型之外的模型&#xff0c;其中包括一些单模型及集成学习器。 保序回归、多项式回归、多输出回归、多输出K近邻回归、决策树回归、多输出决策树回归、AdaBoost回归、梯度提升决策树回归…

Java进阶-try-with-resources

Java进阶-try-with-resources try-with-resources 是什么传统使用try-catch-finally关闭资源使用try-with-resources什么时候用 try-with-resources 是什么 try-with-resources 是 Java 7 中引入的一个新特性&#xff0c;用于简化资源管理&#xff0c;一般是用于处理实现了 Au…

大模型+多模态合规分析平台,筑牢金融服务安全屏障

随着金融市场的快速发展&#xff0c;金融产品和服务日趋多样化&#xff0c;消费者面临的风险也逐渐增加。 为保护消费者权益&#xff0c;促进金融市场长期健康稳定发展&#xff0c;国家监管机构不断加强金融监管&#xff0c;出台了一系列法律法规和政策文件。对于金融从业机构…

设计模式-状态模式和策略模式

1.状态模式 1.1定义 当一个对象的内在状态改变时允许根据当前状态作出不同的行为&#xff1b; 1.2 适用场景 (1)一个对象的行为取决于它的状态,并且它必须在运行时根据状态来决定其行为. (2)代码中包含了大量的与状态有关的条件语句,例如:一个操作含有庞大的多分值语句(if…

对原生input加上:当前输入字数/最大输入字数

源码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>/* 样式…

商城自动化测试实战 —— 登录+滑块验证

hello大家好&#xff0c;我是你们的小编&#xff01; 本商城测试项目采取PO模型和数据分离式架构&#xff0c;采用pytestseleniumjenkins结合的方式进行脚本编写与运行&#xff0c;项目架构如下&#xff1a; 1、创建项目名称&#xff1a;code_shopping&#xff0c;创建所需项目…