Android 架构模式

news2024/11/28 16:03:16

1.三个基本架构

①MVC(Model-View-Controller)

Model:代表数据模型,管理数据状态。

View:视图,即呈现给用户的UI,包括布局文件及Activity。

Controller:控制者,负责处理用户与app之间的交互,包含业务逻辑。是Model与View的桥梁,用来控制程序的流程,比如Activity/Fragment。

缺点:View与Model之间还存在依赖关系,在View中容易直接操作Model。Controller很‘重’很复杂,Activity既是View又是Controller,有时候一个Activity会有成千甚至上万行代码,很复杂。

②MVP(Model-View-Presenter)

Model:代表数据模型,包括数据和一些业务逻辑,即数据的结构定义、数据的存储和获取等。

View:视图,即呈现给用户的UI,并且负责与客户进行交互。比如XML/Activity/Fragment。

Presenter:主持者,Presenter通过View接收用户的输入,然后在Model的帮助下处理用户的数据并将结果传递回View。Presenter通过接口与View进行通信。接口在presenter类中定义,它传递所需的数据。Activity/Fragment及其他View视图组件实现此接口获得他们想要的数据并呈现数据。

优点:Presenter和View层之间通过定义接口实现通信,将View与Model解耦,方便进行单元测试。

缺点:业务场景比较复杂时,接口会定义很多,使Presenter依旧很‘重’很复杂。Presenter如果持有Activity等的引用,容易出现内存泄露问题。

③MVVM(Model-View-ViewModel)

Model:代表数据模型,包括数据和一些业务逻辑,即数据的结构定义、数据的存储和获取等。

View:视图,即呈现给用户的UI,并且负责与客户进行交互。比如XML/Activity/Fragment。

ViewModel:解决了MVP的问题,使ViewModel和View之间不再依赖接口通信,而是通过LiveData、RxJava、Flow等响应式开发的方式来通信,即将数据以可观察对象的形式提供给View,View和ViewModel层分离,ViewModel不应该知道与之交互的View是什么。

在MVVM中View持有ViewModel,但ViewModel得不到任何关于View的信息。所以View与ViewModel之间存在着一对多的关系,一个View可以持有多个ViewModel。MVVM是MVP模式的一个优化。

 

2.三个架构模式举例

分别用MVC、MVP、MVVM模式实现一个用户登录的功能。

①MVC

59d96930fc52455fb20d0dc02d3c8ecd.png

 (1)Controller层:

public class MvcLoginActivity extends AppCompatActivity {

    private EditText userNameEt;

    private EditText passwordEt;

    private User user; //view层依赖了Model层

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView( R.layout.activity_login);

        user = new User();

        userNameEt = findViewById(R.id.name);

        passwordEt = findViewById(R.id.pwd);

        Button loginBtn = findViewById(R.id.lgin);

        loginBtn.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                login( userNameEt.getText().toString(), passwordEt.getText().toString());

            }

        });

    }

    private void login(String userName, String password) {

        if (userName.equals("admin") && password.equals("123")) { //业务逻辑

            user.setUserName(userName);

            user.setPassword(password);

            Toast.makeText(this, userName + " Login Successful", Toast.LENGTH_SHORT ).show(); //UI更新

        } else {

            Toast.makeText(this, "Login Failed", Toast.LENGTH_SHORT).show();

        }

    }

}

可以看到,所有的数据处理逻辑和界面更新都写在了Controller层里,耦合严重,不方便单元测试。

MVC一般的交互流程是:

1)用户操作View, 比如说产生了一个点击事件。

2)Controller接收事件,对其作出反应。比如说是点击登录事件,它会校验用户输入是否为空,若为空则直接返回View让其提示用户;若不为空则请求Model层。

3)Model作出处理后,需要把登录用户的数据通知到相关成员,这里是View层。View收到后作出相关展示。

②MVP

4b7f920a708d4b68a297d0222bcedc92.png

(1)Model层:处理业务逻辑

public interface IUserBiz {  //业务逻辑接口

    boolean login(String userName, String password); 

}

//业务逻辑实现

public class UserBiz implements IUserBiz {

    @Override

    public boolean login(String userName, String password) {

        if (userName.equals("admin") && password.equals("123")) {

            User user = new User();

            user.setUserName(userName);

            user.setPassword(password);

            return true;

        }

        return false;

    }

}

(2)Presenter层:

public class LoginPresenter{

    private UserBiz userBiz; //持有Model

    private IMvpLoginView iMvpLoginView; //持有View引用

    public LoginPresenter(IMvpLoginView iMvpLoginView) {

        this.iMvpLoginView = iMvpLoginView;

        this.userBiz = new UserBiz();

    }

    public void login() {

        //1.从view获取数据

        String userName = iMvpLoginView.getUserName();

        String password = iMvpLoginView.getPassword();

        //2.调用Model处理业务逻辑

        boolean isLoginSuccessful = userBiz.login(userName, password);

        //3.根据结果,使用接口的方式更新view

        iMvpLoginView.onLoginResult( isLoginSuccessful);

    }

 }

(3)View层:

public interface IMvpLoginView {

    String getUserName();

    String getPassword();

    void onLoginResult(Boolean isLoginSuccess);

}

public class MvpLoginActivity extends AppCompatActivity implements IMvpLoginView{

    private EditText userNameEt;

    private EditText passwordEt;

    private LoginPresenter loginPresenter;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_login);

        userNameEt = findViewById(R.id.name);

        passwordEt = findViewById(R.id.pwd);

        Button loginBtn = findViewById(R.id.logi);

        loginPresenter = new LoginPresenter( this); //view中持有presenter

        loginBtn.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                loginPresenter.login(); //需要处理业务逻辑时,调用presenter去处理(不在view里处理业务逻辑)

            }

        });

    }

    //实现view定义的接口,供presenter调用 

    @Override

    public String getUserName() {

        return userNameEt.getText().toString();

    }

    @Override

    public String getPassword() {

        return passwordEt.getText().toString();

    }

    @Override

    public void onLoginResult(Boolean isLoginSuccess) {

        if (isLoginSuccess) {

            Toast.makeText(this, getUserName() + " Login Successful", Toast.LENGTH_SHORT ).show();

        } else {

            Toast.makeText(this, "Login Failed", Toast.LENGTH_SHORT).show();

        }

    }

}

业务逻辑全部在Model里,view中用户操作时如果需要处理业务逻辑,就通过presenter调用Model里的方法去处理。业务逻辑处理完成后,处理结果在presenter里调用view里的方法去更新view显示。

MVP的交互流程是:

1)用户操作View层,产生了一个事件;

2)Presenter接收事件,并对其作出反应,请求Model层;

3)Model层作出处理后通知给Presenter,Presenter进而再通知到View层。

③MVVM

3d41fae1c91f4bca9d17201651ebab59.png

(1)ViewModel层:

public class LoginViewModel extends ViewModel {

    private User user;

    private MutableLiveData<Boolean> isLoginSuccessfulLD; //该变量表示login业务处理结果,用该结果去驱动界面更新,所以这里使用了LiveData

    public LoginViewModel() {

        this.isLoginSuccessfulLD = new MutableLiveData<>();

        user = new User();

    }

    public MutableLiveData<Boolean> getIsLoginSuccessfulLD() {

        return isLoginSuccessfulLD;

    }

    public void setIsLoginSuccessfulLD(boolean isLoginSuccessful) {

        isLoginSuccessfulLD.postValue( isLoginSuccessful);

    }

    public void login(String userName, String password) { // 暴露login方法,处理业务逻辑

        if (userName.equals("admin") && password.equals("123")) {

            user.setUserName(userName);

            user.setPassword(password);

            setIsLoginSuccessfulLD(true);

        } else {

            setIsLoginSuccessfulLD(false);

        }

    }

    public String getUserName() {

        return user.getUserName();

    }

}

(2)View层:

public class MvvmLoginActivity extends AppCompatActivity {

    private LoginViewModel loginVM;

    private EditText userNameEt;

    private EditText passwordEt;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_login);

        userNameEt = findViewById(R.id.name);

        passwordEt = findViewById(R.id.pwd);

        Button loginBtn = findViewById(R.id.logi);

        loginBtn.setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View view) {

                loginVM.login( userNameEt.getText().toString(), passwordEt.getText().toString()); //需要处理业务逻辑时,调用vm去处理(不在view里处理业务逻辑)

            }

        });

        loginVM = ViewModelProviders.of( this).get(LoginViewModel.class);

        //用ViewModel里的数据驱动界面更新

        loginVM.getIsLoginSuccessfulLD().observ e(this, new Observer<Boolean>() {

        @Override

        public void onChanged(Boolean isLoginSuccessFul) {

            if (isLoginSuccessFul) {

                Toast.makeText(this,  loginVM.getUserName() + " Login Successful", Toast.LENGTH_SHORT).show();

            } else {

                Toast.makeText(this, "Login Failed", Toast.LENGTH_SHORT).show();

            }

        }

    });

}

界面中用户操作时,如果需要处理业务逻辑,就调用ViewModel的方法去处理,处理结果保存在ViewModel中的LiveData里,然后activity通过observe监听该LiveData的数据变化从而自动更新界面。

注意:

我们知道Model层里包括了一些业务逻辑和业务数据模型,而ViewModel层即是视图模型(Model of View),其内是视图的表示数据和逻辑。比如说Model层的业务数据是1, 2, 3, 4, 而翻译到View层,则可能是表示A, B, C, D了。ViewModel除了做这个事情外,还会封装视图的行为动作,如点击某个控件后的行为等。另外注意这里的ViewModel和Jetpack包里提供的ViewModel组件不是一个东西,这里的ViewModel是一个概念,而Jetpack包则提供了一个比较方便的实现方式。

很多讲MVVM的文章示例都会用DataBinding,然而没有DataBinding照样可以使用MVVM架构,比如说借用LiveData、RxJava、Flow等,这些工具都是基于响应式开发的原理来替代基于接口的通信方式。这里的响应式开发强调一种基于观察者模式的开发方式:View订阅ViewModel暴露的响应式接口,接收到通知后进行相应逻辑,而ViewModel不再持有任何形式的View的引用,减少耦合,提高了可复用性。

另外如果使用LiveData的话, ViewModel对View层仅暴露LiveData接口,在View层不允许直接更新LiveData,因为一旦View层拥有直接更新LiveData的能力,就无法约束View层进行业务处理的行为。

 

最后说一下Repository,Repository模式主要思想是通过抽象一个Repository层,对业务(领域)层屏蔽不同数据源的访问细节,业务层(可能是 ViewModel)无需关注具体的数据访问细节。

Repository内部实现了对不同数据源(DataSource)的访问,典型的DataSource包括远程数据、Cache缓存、Database数据库等,可以用不同的Fetcher来实现,Repository持有多个Fetcher引用。

因此上面实例中的LoginModel可以换成LoginRepository类, LoginRepository不暴露具体的数据访问方式,只暴露出这一能力的接口。

 

 

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

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

相关文章

KubeVirt技术介绍及实验部署

虚拟化简介 在云计算发展中&#xff0c;有两类虚拟化平台&#xff1a; openstack&#xff08;iaas&#xff09;&#xff1a;关注于资源的利用&#xff0c;虚拟机的计算&#xff0c;网络和存储Kubernetes&#xff08;pass&#xff09;&#xff1a;关注容器的编排调度&#xff…

设计模式之~代码模式

描述&#xff1a; 代理模式&#xff08;Proxy&#xff09;&#xff0c;为其他对象提供一种代理以控制对这个对象的访问。 结构图&#xff1a; 优点: 代理模式能将代理对象与真实被调用的目标对象分离 一定程度上降低了系统的耦合度&#xff0c;扩展性好 可以起到保护目标对象…

发明专利公开 -- 一种基于 JSON 文件 + Http Header 的支持多项目、多分支、多人协同的 Api Mock/代理 工具

现阶段主流的前后端分离的开发模式下&#xff1a;前后端采用并行开发方式&#xff0c;在前端开发过程中通常需要依附于共同约定的接口格式及数据。 该过程是一个并行过程&#xff0c;因此 Api Mock 模拟接口的返回变成了必要。同时&#xff0c;联调过程中&#xff0c;修改后端…

目标检测笔记(九):详细介绍并实现-如何可视化深度学习中每层特征层的网络训练情况

文章目录 为什么要解析特征层如何可视化特征层可视化结果如何 ❤️ &#x1f9e1; &#x1f49b; &#x1f49a; &#x1f499; &#x1f49c; &#x1f5a4; &#x1f90d; &#x1f90e; &#x1f494; ❣️ &#x1f495; &#x1f49e; &#x1f493; &#x1f497; &#…

随笔-学会和解

上周六&#xff0c;媳妇儿去加班&#xff0c;回到家已经是晚上8点多了。当天的雨淅沥沥地下了一天&#xff0c;气温很低。 看着她情绪不是很高&#xff0c;也没说啥&#xff0c;赶紧安排吃饭。 我&#xff1a;咋的啦&#xff0c;项目不顺利&#xff1f; 她&#xff1a;还行吧…

【深度学习】【人脸检测模型】SCRFD模型的训练与部署实战

文章目录 Linux安装环境pythoninsightface环境 训练数据集准备todo 训练 Linux安装环境 python 我的cuda版本11.6&#xff1a; $ nvcc --version nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2022 NVIDIA Corporation Built on Tue_Mar__8_18:18:20_PST_2022…

gitlab占用内存太大了如何解决?

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号雄雄的小课堂 现在是&#xff1a;2023年5月30日16:58:15 最近在家里自己搞了个服务器&#xff0c;因为这台机器都不用了&#xff0c;从朋友那拿过来&#xff0c;就当服务器用了&#xff0c;看了下&#xff0c;比云服…

python基本操作2(速通版)

关于字符串的基本操作&#xff0c;以实例为主。 目录 一、字符串基本操作 1.基本字符串定义 2.字符串遍历 3字符串切片 二、字符串的常用方法 1.find函数 2.replace函数 3.count函数 4.分割和连接类的函数 5.字符串判断函数 6.去除字符串的 三、元组 1.元组的基本操…

离散数学-集合论

数学基础-离散数学-集合论 集合论是现代各科数学的基础&#xff0c;它起源于十六世纪末期的数集的研究。直到1876-1883年&#xff0c;康托尔发表了一系列有关集合论的文章&#xff0c;奠定了集合论的基础。1904-1908年&#xff0c;策墨罗(Zermelo)提出了集合论的公理系统&…

计算机组成原理-总线-总线的概念、事务和定时

目录 一、总线基本概念 总线特性 二、总线的分类 数据传输格式 2.2按总线的功能 2.2.1片内总线 2.2.2系统总线 2.2.3通信总线 2.3 按时序控制方式 三、系统总线的结构 3.1单总线结构 3.2双总线结构 3.3三总线结构 四、总线的性能指标 五、总线的4个阶段 六、总线的事…

5年测试经验,从月薪9k变1w5,进阶自动化测试真就这么香?

在这个吃技术的IT行业来说&#xff0c;我之前每天做的是最基础的工作&#xff0c;但是随着时间的消磨&#xff0c;我产生了对自我和岗位价值和意义的困惑。 一是感觉自己在浪费时间&#xff0c;另一个就是做了快2年的测试&#xff0c;感觉每天过得浑浑噩噩&#xff0c;薪资也从…

Java --- 云尚办公之微信公众号整合

目录 一、整合微信公众号 1.1、公众号菜单管理 1.2、微信授权登录 1.3、消息推送 一、整合微信公众号 1.1、公众号菜单管理 数据库表&#xff1a; CREATE TABLE wechat_menu (id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 编号,parent_id BIGINT(20) DEFAULT NULL CO…

day42|动态规划4-背包问题分割等和子集

0-1背包和完全背包搞清楚即可。 0-1背包问题-一维 背包有最大重量的限制&#xff0c;物品有重量有价值&#xff0c;那么在最大背包的限制下&#xff0c;能够得到的最大价值是多少&#xff1f; 暴力解法 每个物品都有放和不放两种状态&#xff0c;那么遍历所有的组合就可以…

谷歌浏览器 | Chrome DevTools系统学习篇- Elements面板(上)

大家好&#xff0c;文接上上回谷歌浏览器 | Chrome DevTools系统学习篇-概述&#xff0c;和上文谷歌浏览器 | Chrome DevTools系统学习篇-Device Mode。所谓“工欲善其事&#xff0c;必先利其器”&#xff0c;我们进一步来熟悉谷歌开发者工具。今天分享的是Elements元素&#x…

我在亚马逊云平台的学习成长之路

前言 今年是亚马逊云科技成立的第16个年头&#xff0c;也是云计算行业诞生的16周年。作为云计算时代的“领头羊”&#xff0c;亚马逊2006年推出了名为Amazon Web Services(AWS)的新产品。AWS背后的想法是提供一个平台&#xff0c;同亚马逊内部使用的平台一样&#xff0c;将其作…

Three.js--》实现3d地月模型展示

目录 项目搭建 初始化three.js基础代码 创建月球模型 添加地球模型 添加模型标签 今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目中才能灵活将所学知识运用起来&#xff0c;话不多说直接开始。 项目搭建 本案例还…

230530-论文整理-课题组2

对这些研究有点兴趣颇微。 文章目录 Rethinking Dense Retrieval’s Few-Shot AbilityDecoder-Only or Encoder-Decoder? Interpreting Language Model as a Regularized Encoder-DecoderPLOME: Pre-training with Misspelled Knowledge for Chinese Spelling CorrectionRead…

北邮22信通:复习补充:双向链表的实现

北邮22信通一枚~ 跟随课程进度每周更新数据结构与算法的代码和文章 持续关注作者 解锁更多邮苑信通专属代码~ 获取更多文章 请访问专栏&#xff1a; 北邮22信通_青山如墨雨如画的博客-CSDN博客 **说明** 最近复习看到书后有双向链表的题目&#xff0c;编出来供大家…

Mybatis-Plus 进阶开发-自定义乐观锁插件

文章目录 前言0. OptimisticLockerInnerInterceptor 介绍1. Mybatis-plus 实现乐观锁的原理2. 自定义乐观锁插件1. 创建自定义乐观锁插件2. 配置自定义乐观锁插件 3. 总结 &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是冰点&#xff0c;从业11年&#xff0c;目前在…

jmeter 性能测试工具的使用(Web性能测试)

1、下载 2023Jmeter性能测试项目实战教程&#xff0c;十年测试大佬手把手教你做性能&#xff01;_哔哩哔哩_bilibili2023Jmeter性能测试项目实战教程&#xff0c;十年测试大佬手把手教你做性能&#xff01;共计11条视频&#xff0c;包括&#xff1a;1.什么是性能测试以及性能测…