Android开发框架模式(MVC、MVP、MVVM)实例解析

article/2025/9/24 11:56:51

Android项目中,尤其是比较大型的项目开发中,模块内部的高聚合和模块间的低耦合性就显得尤为重要了。所以我们一般情况下需要为项目设计一种框架模式,通常情况下我们一般用到的三种MVCMVPMVVM
通过框架模式设计的项目能够极大的提高开发效率,提高项目的可维护性和可扩展性,另外在模块测试及问题定位上也提供了较大的便利。

 

接下来我们就对这三种框架模式一一进行讲解,并会重点讲解下比较常用的MVP框架模式。

一、MVC框架:

                     图片源自网络

MVC全称为Model(模型层)--View(视图层)--Controller(控制层)。
    Model层:主要用于网络请求、数据库、业务逻辑处理等操作。
    View层:用于展示UI,一般采用XML文件进行界面的描述。
    Controller层:控制层的重任落在了众多Activity上,Activity需要交割业务逻辑至Model层处理。

下面结合精简的实例来看下MVC框架模式:

1、View层&Controller层

View层:XML布局文件activity_mvcpattern代表的就是View层,用来显示布局,与用户进行交互。

Controller层:MVCActivity代表的是Controller层,View层会传递请求至Controller,Controller控制Model层进行业务的更新。


/*** @author hongri* @date 2018/9/4** Controller层*/
public class MVCActivity extends AppCompatActivity implements View.OnClickListener {private Button btnRequest;private ImageView iv;private MVCHttpRequestModel mvcHttpRequestModel;private static final String requestUrl = "https://avatar.csdnimg.cn/5/7/C/1_u012440207.jpg";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);/*** xml文件属于理论上的View层*/setContentView(R.layout.activity_mvcpattern);mvcHttpRequestModel = new MVCHttpRequestModel();btnRequest = findViewById(R.id.btnRequest);iv = findViewById(R.id.iv);btnRequest.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btnRequest:/*** Controller层将网络请求业务逻辑交由Model层处理*/mvcHttpRequestModel.onHttpRequest(requestUrl, new MVCRequestCallback() {@Overridepublic void onSuccess(Object successData) {if (successData != null) {iv.setImageBitmap((Bitmap)successData);}}@Overridepublic void onFailure(Object failureData) {Toast.makeText(MVCActivity.this, failureData.toString(), Toast.LENGTH_LONG).show();}});break;default:break;}}
}

2、Model层:

这里的MVCHttpRequestModel便属于Model层,Model层主要用于处理数据库、网络请求等一系列复杂的耗时处理,Model数据请求完成后,通知View进行UI的更新。

/*** @author hongri* @date 2018/9/4** Model层*/public class MVCHttpRequestModel implements MVCHttpRequestInterface {@Overridepublic void onHttpRequest(final String urlString, final MVCRequestCallback listener) {new RequestTask(listener).execute(urlString);}public class RequestTask extends AsyncTask<String, Void, Bitmap> {private MVCRequestCallback listener;public RequestTask(MVCRequestCallback listener) {this.listener = listener;}@Overrideprotected Bitmap doInBackground(String... strings) {Bitmap bitmap = null;InputStream inputStream;try {URL url = new URL(strings[0]);HttpURLConnection conn = (HttpURLConnection)url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(10 * 1000);int responseCode = conn.getResponseCode();if (responseCode == 200) {inputStream = conn.getInputStream();bitmap = BitmapFactory.decodeStream(inputStream);} else {bitmap = null;}} catch (IOException e) {e.printStackTrace();}return bitmap;}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);/***Model层请求完数据后回调给Controller层,Controller层更新UI*/if (bitmap != null) {listener.onSuccess(bitmap);} else {listener.onFailure("获取失败");}}}
}

二、MVP框架

                  图片源自网络

MVP全称为Model(模型层)--View(视图层)--Presenter(协调器/主持者)

MVP是由MVC转化而来的一种框架模式,MVP相比于MVC具有的优点如下:

1、Model层与View层完全分离,我们可以修改视图而不影响模型;

2、可以更高效地使用模型,因为所有的交互都发生在一个地方---Presenter内部;

3、可以将大量的逻辑操作放到Presenter中,避免Activity的臃肿;

4、可以选择将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。

下面根据代码来具体介绍下MVP框架:

1、View层:

View层一般由多个Activity来承担,主要用于显示UI,与用户来进行交互。

/*** MVP(Model--View--Presenter)框架模式--参考:http://www.jcodecraeer.com/a/anzhuokaifa/2017/1020/8625.html** @author hongri** View层*/
public class MVPActivity extends MVPBaseActivity implements MVPViewInterface, OnClickListener {private Button btnName;private TextView tvData;private MVPPresenter mvpPresenter;private String requestUrl = "";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_mvppattern);mvpPresenter = new MVPPresenter();mvpPresenter.attachView(this);btnName = findViewById(R.id.btnName);tvData = findViewById(R.id.tvData);btnName.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btnName:tvData.setText("初始数据");mvpPresenter.requestData(requestUrl);break;default:break;}}@Overridepublic void showSuccessData(String data) {MVPDataInfo dataInfo = ParseJsonUtil.parseJSONData(data);tvData.setText("姓名:" + dataInfo.getmName() + "\n" + "性别:" + dataInfo.getmGender() + "\n" + "年龄:" + dataInfo.getmAge());}@Overridepublic void showFailureData(String data) {tvData.setText(data);}@Overrideprotected void onDestroy() {super.onDestroy();if (mvpPresenter != null) {mvpPresenter.detachView();}}
}

这里为MVPActivity设置了一个顶级父类MVPBaseActivity,父类Activity中可以提炼出公用的UI展示、初始化等操作,减少各子Activity的冗余代码,也方便后续的维护。

/*** @author hongri* BaseActivity用于进行View层的通用的初始化及UI展示工作*/
public class MVPBaseActivity extends AppCompatActivity implements MVPBaseViewInterface {/*** 页面通用LoadingDialog*/private LoadingDialog loadingDialog;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_mvpbase);loadingDialog = new LoadingDialog(this, R.style.LoadingTheme);}@Overridepublic void showLoading() {if (loadingDialog != null) {loadingDialog.showLoading();}}@Overridepublic void dismissLoading() {if (loadingDialog != null) {loadingDialog.dismissLoading();}}
}

接口也采用Base层继承的方式,分别定义了MVPViewInterface,及MVPBaseViewInterface,分别用于MVPActivity及MVPBaseActivity的回调,其中子接口根据业务区别一般是需要建立多个的。

/*** @author hongri* @date 2018/9/4** 记录某个业务方所需的回调方法*/public interface MVPViewInterface extends MVPBaseViewInterface {/*** 业务方数据成功获取后,调用此方法更新UI** @param data*/void showSuccessData(String data);/*** 业务方数据获取失败后,调用此方法展示失败页面UI** @param data*/void showFailureData(String data);}


/*** @author hongri* @date 2018/9/3** 该接口记录所有页面通用的回调方法*/public interface MVPBaseViewInterface {/*** 展示通用页面Loading*/void showLoading();/*** 取消通用页面Loading*/void dismissLoading();
}

2、Presenter层:

  Presenter层主要用于业务逻辑操作,避免了Activity的代码臃肿。

/*** @author hongri* @date 2018/9/3** Presenter层*/public class MVPPresenter extends MVPBasePresenter<MVPViewInterface> implements MVPLoadDataCallback {/*** 请求数据入口** @param url*/public void requestData(String url) {getAttachView().showLoading();MVPDataModelManager.newInstance(MVPDataModel.class.getName()).setParams("").executeGetRequest(url, this);}/*** 数据请求成功--更新UI** @param successData*/@Overridepublic void onSuccess(String successData) {if (isViewAttached()) {getAttachView().dismissLoading();getAttachView().showSuccessData(successData);}}/*** 数据请求失败--更新展示错误页面UI** @param errorData*/@Overridepublic void onFailure(String errorData) {if (isViewAttached()) {getAttachView().dismissLoading();getAttachView().showFailureData(errorData);}}
}
/*** @author hongri* @date 2018/9/4*/public class MVPBasePresenter<V extends MVPBaseViewInterface> {public V mView;/*** 绑定mView,一般在初始化中调用该方法*/public void attachView(V view) {mView = view;}/*** 销毁mView,一般在onDestroy方法中调用*/public void detachView() {if (mView != null) {mView = null;}}/*** 是否与mView建立联系* 每次调用业务方请求的时候都要先调用此方法检查是否与mView建立联系*/public boolean isViewAttached() {return mView != null;}/*** 获取连接的mView*/public V getAttachView() {return mView;}
}

3、Model层:

 该Model层与MVC模式中的Model层类似,项目中所有数据都由该类流入和流出,负责分发所有的请求数据。

/*** @author hongri* @date 2018/9/3** Model层*/public class MVPDataModel extends MVPBaseModel {public String data = "";@Overridepublic void executeGetRequest(String url, final MVPLoadDataCallback callback) {/*** 模拟网络请求耗时操作*/new Handler().postDelayed(new Runnable() {@Overridepublic void run() {/*** 从服务端请求回来的字符串数据:* 为了增加通用性,建议以请求回来的原有数据返回给View层,由View层做具体的数据解析、UI展示处理*/data = "{\n"+ "  \"mData\":\"红日\",\n"+ "  \"gender\": \"男士\",\n"+ "  \"age\":18\n"+ "}";if (!TextUtils.isEmpty(data)) {callback.onSuccess(data);} else {data = "数据获取失败";callback.onFailure(data);}}}, 1000);}
}

这里也封装了一个父类base层--MVPBaseModel,MVPBaseModel是所有Model的顶级父类,负责对外提供数据请求标准,对内为所有Model提供请求的底层支持。

/*** @author hongri* @date 2018/9/5*/public abstract class MVPBaseModel {protected String[] mParams;/*** 设置数据请求参数** @param args 参数数组*/public MVPBaseModel setParams(String... args) {mParams = args;return this;}/**** 执行Get网络请求* @param url* @param callback*/public abstract void executeGetRequest(String url, MVPLoadDataCallback callback);/*** 执行Post网络请求** @param url* @param params* @param callback*/protected void executePostRequest(String url, Map params, MVPLoadDataCallback callback) {}
}

另外这里也定义了一个DataModelManager,是针对多有Model的管理类,把其作为入口,利用反射机制可以获得对应Model对象的引用。

/*** @author hongri* @date 2018/9/5** 统一Model管理器*/public class MVPDataModelManager {public static MVPBaseModel mvpModel;public static MVPBaseModel newInstance(String modelName) {try {mvpModel = (MVPBaseModel)Class.forName(modelName).newInstance();} catch (Exception e) {e.printStackTrace();}return mvpModel;}
}

三、MVVM框架

          图片源自网络

MVVM的全称是Model(模型)--View(视图)--ViewModel(视图模型):

MVVM可以算是MVP的升级版,将 Presenter 改名为 ViewModel。关键在于View和Model的双向绑定,当View有用户输入后,ViewModel通知Model更新数据,同理Model数据更新后,ViewModel通知View更新。

MVVM的优势如下:

1、ViewModel双向绑定,一方的改变都会影响另一方,开发者不用再去手动修改UI的数据。

2、不需要findViewById也不需要butterknife,不需要拿到具体的View去设置数据绑定监听器等等,这些都可以用DataBinding完成。

3、ViewModel的双向绑定是支持生命周期检测的,不会担心页面销毁了还有回调发生,这个由lifeCycle完成。

4、不会像MVC一样导致Activity中代码量巨大,也不会像MVP一样出现大量的ViewPresenter接口。项目结构更加低耦合。

下面具体介绍下MVVM框架:

1、VM层:

这里根据MVVMActivity的布局文件activity_mvvmpattern,介绍下Data Binding:

Data Binding对使用的环境还是有一定要求的:Android Studio版本在1.3以上,gradle的版本要在1.5.0-alpha1以上。
需要在Android SDK manager中下载Android Support repository
然后在对应的Module的build.gradle中添加:

android {....dataBinding {enabled =true}
}

然后使用databinding语法 对 xml 进行数据绑定,我们将 Click事件、输出结果都绑定到VM上。

<?xml version="1.0" encoding="utf-8"?>
<layoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><data><import type="android.view.View"/><variablename="userViewModel"type="com.hongri.model.mvvm.viewmodel.MVVMDataViewModel"/><variablename="handlers"type="com.hongri.model.mvvm.view.MVVMActivity"/><variablename="data"type="String"/></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><Buttonandroid:id="@+id/btnMVVM"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="@{handlers.onClickLoadData}"android:text="点击请求数据"/><Buttonandroid:id="@+id/btnToast"android:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="@{handlers.onClickShowToastName}"android:text="点击请求数据并Toast提示"/><TextViewandroid:id="@+id/tvData"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:text="@{userViewModel.data}"/></LinearLayout>
</layout>

VM层具体类:这里注意具体的属性需要加@Bindable,否则绑定无效。

/*** @author zhongyao* @date 2018/9/3* ViewModel层*/public class MVVMDataViewModel extends BaseObservable implements MVVMLoadDataCallback {private MVVMDataModel model;public MVVMDataViewModel() {model = new MVVMDataModel();}/*** 必须添加@Bindable注释* @return*/@Bindablepublic String getData() {return model.mData;}public void loadUserData() {model.requestData(this);}@Overridepublic void onSuccess() {notifyPropertyChanged(BR.data);}@Overridepublic void onFailure() {}
}

2、View层(MVVMActivity)引入VM:

/*** @author hongri* View层*/
public class MVVMActivity extends AppCompatActivity {private MVVMDataViewModel userViewModel;private TextView tvData;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMvvmpatternBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvmpattern);userViewModel = new MVVMDataViewModel();binding.setUserViewModel(userViewModel);binding.setHandlers(this);tvData = binding.tvData;}public void onClickShowToastName(View view) {Toast.makeText(this, tvData.getText().toString(), Toast.LENGTH_LONG).show();}public void onClickLoadData(View view) {userViewModel.loadUserData();}
}

3、Model层:

 用来进行具体的数据请求操作。

/*** @author hongri* @date 2018/9/3* Model层*/public class MVVMDataModel {public String mData;public MVVMDataModel() {this.mData = "初始数据";}public void requestData(MVVMLoadDataCallback callback) {this.mData = "数据请求成功";callback.onSuccess();}
}

MVVM虽然有这些个优点,但使用起来的坑也是不少的,所以目前MVVM框架在实际开发中应用并不是很多,主要以MVP框架开发为主。

最后提供下github源码,供大家参考:

源码下载

参考:

https://github.com/googlesamples/android-architecture

深度阅读(O(∩_∩)O):

Android面试题总结(最全)


http://chatgpt.dhexx.cn/article/G1auTKkG.shtml

相关文章

LDA主题模型评估方法–Perplexity

在LDA主题模型之后,需要对模型的好坏进行评估,以此依据,判断改进的参数或者算法的建模能力。 Blei先生在论文《Latent Dirichlet Allocation》实验中用的是Perplexity值作为评判标准。 一、Perplexity定义 源于wiki:http://en.wikipedia.org/wiki/Perplexity perplexity是一…

LDA困惑度perplexity的一些个人理解

纠结这个问题很久了&#xff0c;期间主要去了gensim的google论坛&#xff0c;以及在StackOverflow、StackexChange用关键词topic number perplexity搜了下&#xff0c;得到这些很模糊的认识&#xff1a; 1. gensim的log_perplexity()解读&#xff1a; 根据gensim3.8.3的源码&…

NLP基础知识点:困惑度(Perplexity)

本篇内容翻译自Speech and Language Processing. Daniel Jurafsky & James H. Martin. 链接&#xff1a;https://web.stanford.edu/~jurafsky/slp3/ 不愧是自然语言处理领域的圣经&#xff0c;读起来流畅自然&#xff0c;以后还是要多读经典。 困惑度&#xff08;Perplexit…

Python LDA gensim 计算 perplexity

转载自 https://blog.csdn.net/qq_23926575/article/details/79472742 1.LDA主题模型困惑度 这部分参照&#xff1a;LDA主题模型评估方法–Perplexity&#xff0c;不过后面发现这篇文章Perplexity(困惑度)感觉写的更好一点&#xff0c;两篇都是翻译的维基百科。 perplexity是一…

困惑度 (perplexity)

困惑度 (perplexity) 在自然语言处理中,对于一个语言模型,一般用困惑度来衡量它的好坏,困惑度越低,说明语言模型面对一句话感到困惑的程度越低,语言模型就越好。 对于LDA模型,最常用的两个评价方法困惑度(Perplexity)、相似度(Corre)。 其中困惑度可以理解为对于一篇…

Metric评价指标-Perplexity语言模型

欢迎关注知乎&#xff1a; 世界是我改变的 知乎上的原文链接 一. 原理介绍 在研究生实习时候就做过语言模型的任务&#xff0c;当时让求PPL值&#xff0c;当时只是调包&#xff0c;不求甚解&#xff0c;哈哈哈&#xff0c;当时也没想到现在会开发这个评价指标&#xff0c;那现…

perplexity和预训练时用的loss的区别

Perplexity和预训练时用的loss都是用来评估语言模型的性能的指标&#xff0c;但是它们的计算方式和意义有所不同。 Perplexity是一种用来衡量语言模型对一个测试集的预测能力的指标。它的计算方式是将测试集中的所有句子输入到语言模型中&#xff0c;计算每个句子的困惑度&…

技术干货 | 基于MindSpore详解Perplexity语言模型评价指标

01 原理介绍 在研究生实习时候就做过语言模型的任务&#xff0c;当时让求PPL值&#xff0c;当时只是调包&#xff0c;不求甚解&#xff0c;哈哈哈&#xff0c;当时也没想到现在会开发这个评价指标&#xff0c;那现在我来讲一下我对这个指标的了解&#xff0c;望各位大佬多多指…

Perplexity定义

Refer from http://blog.csdn.net/pipisorry/article/details/42460023 http://blog.csdn.net/pipisorry/article/details/42460023 熵/信息熵 Perplexity定义 perplexity是一种信息理论的测量方法&#xff0c;b的perplexity值定义为基于b的熵的能量&#xff08;b可以是一个概…

gensim---LDA---perplexity

以下内容来源于https://blog.csdn.net/qq_25073545/article/details/79773807 使用gensim实现lda&#xff0c;并计算perplexity&#xff08; gensim Perplexity Estimates in LDA Model&#xff09; Neither. The values coming out of bound() depend on the number of topi…

世界上第一个会话搜索引擎——Perplexity AI使用测评

引言 比起传统的列表式搜索&#xff0c;Perplexity AI把艳惊四座的ChatGPT和必应搜索结合起来&#xff0c;既有ChatGPT式的问答&#xff0c;又像普通搜索引擎那样列出链接&#xff0c;就连马斯克也亲自称赞&#xff1a;它不仅总结出了推文的由来&#xff0c;还将推文的内容解释…

主题模型TopicModel:LDA主题模型的评估

http://blog.csdn.net/pipisorry/article/details/42460023 基础知识&#xff1a;熵 [熵与互信息] 皮皮blog Perplexity定义 perplexity是一种信息理论的测量方法&#xff0c;b的perplexity值定义为基于b的熵的能量&#xff08;b可以是一个概率分布&#xff0c;或者概率模型…

语言模型常用评价方法:perplexity、bleu

目录 1. perplexity&#xff08;困惑度、复杂度&#xff09; 2. BLEU 代码实现 1. perplexity&#xff08;困惑度、复杂度&#xff09; 更多详细&#xff0c;参考&#xff1a;详解语言模型NGram及困惑度Perplexity 语言模型&#xff1a;语言模型可以表示为一个计算 的模型&a…

LDA主题模型绘制困惑度(perplexity)-主题数曲线——python

主题建模作为一种基于机器学习的文本内容分析技术&#xff0c;一般用于推断文本文档中隐藏主题的技术。很多研究使用了基于Latent Dirichlet Allocation (LDA)的主题建模算法来处理大规模文档并识别潜在主题。LDA主题模型已经在多个研究领域得到应用&#xff0c;且都有着不俗表…

Android keystore

1.keystore是一个密钥库&#xff0c;密钥库中可以放很多对密钥对&#xff08;私钥证书(证书中包含公钥&#xff0c;数字签名,证书有效期&#xff0c;组织机构名称&#xff0c;申请时间&#xff0c;算法等。)&#xff09;kestore中有两种密码&#xff0c;一个密码是访问密钥库的…

查看KeyStore的信息,(本地的和线上的)

本地的&#xff1a; 1.找到jdk路径、如图 输入cmd 2.输入&#xff1a;keytool -list -v -keystore C:\Users\j\Desktop\app-android-v1.6-1caec749d84e708f91fd90ab383e42d7b417a47e\你的名.keystore 注意&#xff1a;C:\Users\j\Desktop\app-android-v1.6-1caec749d84e708f9…

Keystore与Truststore的区别

Keystore vs Truststore 概念 Keystore 用于存储特定程序应提供给双方&#xff08;服务器或客户端&#xff09;以进行验证的私钥和身份证书。 Truststore 用于存储来自认证机构 (CA) 的证书&#xff0c;这些证书验证服务器在 SSL 连接中提供的证书。 区别 KeystoreTruststo…

Keystore、Key attestation

最近看见了Keystore这个名词不知道什么意思&#xff0c;百度找到了前辈的优秀文章&#xff0c;这里copy学习一下&#xff0c;原文链接放在文末&#xff0c;感谢前辈。 Keystore的技术演进之路 Android提供的keystore功能发展历程伴随着Android版本不断演进。 从 Android 6.0 …

Android KeyStore流程

文章目录 一、Keystore二、Keystore架构及接口函数1. Keystore组件架构2. IKeymasterDevice.hal中的几个重要接口函数2.1 begin函数2.2 update函数2.3 finish函数2.4 abort函数 3. Keymaster TA4. 对称密码函数API 三、从Keystore到Keymaster的完整分析1. cts问题2. 代码流程分…

AndroidStudio生成keystore

相信大家都慢吞吞的切换将开发工具迁移到了AS&#xff0c;今天&#xff0c;奉上生成keystore的方法。 看图&#xff1a; 点击我选中的Generate Signed APK&#xff0c;翻译过来大致是&#xff0c;生成已签署的APK&#xff0c;我们点击这一项 如果还没有生成keystore&#xff…