Android下MVP和MVVM模式的实践

devtools/2024/9/24 12:26:19/

转载注明出处:https://blog.csdn.net/skysukai

1、前言

MVP和MVVM诞生已经好些年头了,记得刚毕业才参加工作的时候,第一次见到了有上万行的Activity,这种巨无霸的Activity维护起来简直就是噩梦。这时候,就需要进行代码重构了。MVP和MVVM两种框架,将逻辑层、显示层、数据层分层处理,逻辑清楚。

2、MVC

在讲MVP、MVVM之前,要先说明MVC才能讲清楚这些架构。MVC架构诞生已有40余年,其典型定义如下:
在这里插入图片描述

3、MVP

3.1 MVP模式

有关MVP的介绍网上有很多,大家可以自行百度。这里贴一个图,能比较清楚地说明MVP模式内各个模块间的关系:
在这里插入图片描述

可以看到Presenter层处于MVP模式的中心地位,View层负责UI显示,Model层负责数据处理。

3.2 一个简单MVP的实践

首先给出首页界面的视觉效果图:
首页
可以看到,这是一个标准的互联网应用。首页会请求数据加载到页面显示,比较适合使用MVP模式。

3.2.1 类定义

MVP编程是面向接口编程,相对来说,接口会比较多。Presenter层、View层、Model层分别定义各自的接口,给出首页界面的类定义:

public interface IPreviewPresenter {void destory();
}
public interface IPreviewView {void onLoadStart();void onLoadEnd();void onLoadFail(CaseListResult caseList);void onLoadMoreSuccess(CaseListResult caseList);void onRefreshStarted();void onRefreshSuccess(CaseListResult caseList);
}
public interface IPreviewModel {void getData(int pageNum, RequestListener<CaseListResult> listener);
}

在以上各个接口中,需要预先考虑需要的业务,放在各自的接口中。

3.2.2 实现

在MVP模式中,通常View层是Activity/Fragment用以显示UI;Presenter层会同时持有View层、Model层的引用:

public class PreviewPresenter implements IPreviewPresenter {private IPreviewView mView;private IPreviewModel mModel;public PreviewPresenter(IPreviewView view) {mView = view;mModel = new PreviewModel();mParam = new CaseListParam();}public void loadPreviewList(int pageNum) {mView.onLoadStart();mModel.getData(pageNum, new RequestListener<CaseListResult>() {@Overridepublic void onSuccess(CaseListResult result) {mView.onLoadEnd();mView.onLoadMoreSuccess(result);}@Overridepublic void onFailure(CaseListResult result) {mView.onLoadEnd();mView.onLoadFail(result);}});}@Overridepublic void destory() {……}……
}
public class PreviewFragment extends UltrasoundBaseFragmentimplements IPreviewView {private PreviewPresenter mPresenter;private PreviewListAdapter mAdapter;@Overridepublic void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {super.onViewCreated(view, savedInstanceState);……initAdapter();}private void initAdapter() {mRefreshLayout.setRefreshing(false);mAdapter = new PreviewListAdapter(R.layout.item_preview, null);mPreviewList.setAdapter(mAdapter);……}@Overridepublic void onResume() {super.onResume();reloadData();}private void reloadData() {……mPageNum = 1;mPresenter.loadPreviewList(mPageNum);}@Overridepublic void onRefreshStarted() {……}@Overridepublic void onRefreshSuccess(CaseListResult caseList) {……}@Overridepublic void onLoadStart() {……}@Overridepublic void onLoadEnd() {……}@Overridepublic void onLoadMoreSuccess(CaseListResult caseList) {……if (count == 0) {View emptyView = LayoutInflater.from(getContext()).inflate(R.layout.empty_view, null);mAdapter.setEmptyView(emptyView);return;}if (caseList.result.pageNum * caseList.result.pageSize>= caseList.result.count) {//数据全部加载完毕mAdapter.loadMoreEnd();} else {mAdapter.loadMoreComplete();}}@Overridepublic void onLoadFail(CaseListResult caseList) {if (caseList.result == null) {View errorView = LayoutInflater.from(getContext()).inflate(R.layout.error_view, null);mAdapter.setEmptyView(errorView);return;} else {mAdapter.loadMoreFail();}}}
public class PreviewModel implements IPreviewModel {@Overridepublic void getData(int pageNum, RequestListener<CaseListResult> listener) {RequestHelper.getPreviewList(pageNum, listener);}
}

以上,一个MVP模式的首界面代码就实现了。

3.3 略微复杂的MVP模式

还是首先给出设计稿:
在这里插入图片描述
要达到的目标是在插入U盘时,扫描U盘下的视频文件然后列表展示。当然这只是视频列表页面,还有图片列表、音乐列表界面。随着业务复杂程度的增加,代码行数也成线性倍数增加。

3.3.1 UML类图

这个项目的代码复杂度高了很多,给出UML类图,代码就不展示了。
在这里插入图片描述
刚才已经提到,代码有三套相互独立的业务。那当然可以抽象出统一的部分:BaseFragment<V, P extends BasePresenter>、BasePresenter。BaseFragment用于存放三套业务都涉及的诸如初始化操作等:createPresenter()、initViews()等;BasePresenter用于持有view层等。处理完公共部分就是各自业务了。
在MVP模式中,处于核心地位的依然是presenter:
VideoListPresenter<V extends IVideoListView>这个presenter没有直接持有view,由统一抽象的BasePresenter通过弱引用持有了view层,这样就间接持有了view层;而model层则是直接被presenter层持有。

3.4 总结

在MVP模式中,presenter层持有view层和model层的引用,presenter层始终处于核心地位。如果model层要需要和presenter层通信,可以由回调接口来实现。

4、MVVM

4.1 MVVM框架

和MVP模式一样,先给出MVVM的框架图:
在这里插入图片描述

把MVVM称为框架是因为谷歌公司做了许多适配的工作,开发者只需要在既定的框架下实现功能即可。从框架图我们可以看到,MVVM和MVP的最大区别就是View层和ViewModel层之间变成了一个双向的箭头,不是两个单向的箭头。在MVP模式中,Presenter层会同时持有View层和Model的引用;而在MVVM框架里面,ViewModel不需要持有View层的引用,发生变化之后,View层会自动更新界面。

4.2 早期的MVVM示例

视觉图
还是先给出实现效果。在说MVVM之前,就不得不提一提databinding。databinding是一种工具,是MVVM的具体实现。这里默认读者已经了解databinding。使用databinding,需要在xml布局文件里定义好需要绑定的数据及控件:

<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><data><variablename="viewmodel"type="com.myapp.data.ViewModel" /></data><ConstraintLayout... /> <!-- UI layout's root element -->
</layout>

定义好布局绑定之后,还需要在UI界面里定义定义适配器,通过编译之后才能确定view和viewmodel的绑定关系:

@BindingAdapter("app:goneUnless")
public static void goneUnless(View view, Boolean visible) {view.visibility = visible ? View.VISIBLE : View.GONE;
}

4.3 使用Jetpack里的ViewModel

随着时间的推移,databinding已经逐渐被弃用,谷歌推出了ViewModel来取代databinding。有关ViewModel的描述可以参考官网。还是首先给出设计稿:
在这里插入图片描述

4.3.1 UML类图

在这里插入图片描述

4.3.2 准备工作

建立View层和ViewModel层的对应关系:

public static CloudMusicHomeRecommendViewModel getInstance(ViewModelStoreOwner owner) {return new ViewModelProvider(owner).get(CloudMusicHomeRecommendViewModel.class);}

4.3.3 实现细节

在这里插入图片描述
以每日推荐页签时序图进行说明。进入页面后,首先请求数据:

mRecommendCloudMusicViewModel.getDailyRecommendLiveData().observe(getViewLifecycleOwner(), dailyRecommend -> {ImageUtil.show(dailyRecommend.get(0).getCoverImgUrl(), mBinding.recommendMusicDailyMusicImv, R.drawable.recommend_music_daily_recommend_music,R.drawable.recommend_music_daily_recommend_music, 746, 280, 1);});mRecommendCloudMusicViewModel.getDailyRecommendList();

请求到数据后,通过SetValue来通知View层更新:

public void getRecommendPlayList() {repository.getRecommendPlaylist(recommendList -> {if (recommendList != null) {recommendPlaylistLiveData.setValue(recommendList.getData().getList());}});}

View层通过设置监听来刷新UI:

mRecommendCloudMusicViewModel.getRecommendPlaylistLiveData().observe(getViewLifecycleOwner(), this::updateRecommendPlaylistUI);

如此,就实现了通过jetpack里的ViewModel来实现了MVVM模式。


http://www.ppmy.cn/devtools/116488.html

相关文章

中电金信多模态鉴伪技术抵御AI造假威胁

AI换脸技术&#xff0c;属于深度伪造最常见方式之一&#xff0c;是一种利用人工智能生成逼真的虚假人脸图片或视频的技术。基于深度学习算法&#xff0c;可以将一个人的面部特征映射到另一个人的面部&#xff0c;创造出看似真实的伪造内容。近年来&#xff0c;以AI换脸为代表的…

dbt snapshot命令及应用示例

DBT是一种功能强大的数据转换工具&#xff0c;它使数据分析师和工程师能够更有效地转换仓库中的数据。dbt的一个关键特性是能够创建快照&#xff0c;这是跟踪数据随时间变化的一种方法。本文带你一起完成创建和使用dbt快照的过程。 理解缓慢变化维度 缓慢变化维度(scd)是数据仓…

什么是孤儿进程和僵死进程?

一、前言 本文先介绍unix系统中进程的退出以及终止过程&#xff0c;然后介绍什么是孤儿进程以及僵死进程。包含如下内容&#xff1a; 1.进程终止过程 2.孤儿进程 3.僵死进程 二、进程终止的过程 2.1 进程的终止状态 进程终止分为正常终止和异常终止。 正常终止包括如下5种情…

使用【Sa-Token】实现Http Basic 认证

使用Sa-Token开源架构快速实现Http Basic 认证&#xff0c;如上图 1、springboot环境下直接添加starter即可 <!-- Sa-Token 权限认证&#xff0c;在线文档&#xff1a;https://sa-token.cc --> <dependency><groupId>cn.dev33</groupId><artifactI…

axios封装RESTful API 接口

RESTful API 概念 RESTful API &#xff08;Representational State Transfer&#xff09;是一种设计风格和架构原则&#xff0c;用于构建网络应用程序的接口。它基于 HTTP 协议&#xff0c;并使用一组规范来定义和处理资源。 RESTful API 的核心概念是资源&#xff08;Resou…

性能监控之Python实战SkyWalking链路追踪

文章目录 一、介绍二、SkyWalking支持的语言三、SkyWalking安装3.1 前提准备3.2 先安装ElasticSearch7.X3.3 Skywalking-OAP 安装3.4 Skywalking-UI 界面安装3.5 访问页面检查SkyWalking是否可以访问 四、Python 项目接入SkyWalking4.1 演示项目代码4.2 验证 sw-python4.3 配置…

为什么 Feign 要用 HTTP 而不是 RPC?

一、引言 在现代微服务架构中&#xff0c;服务之间的通信是至关重要的环节。Feign 是一种常用的声明式 HTTP 客户端工具&#xff0c;它简化了服务间的调用过程。然而&#xff0c;在服务通信的领域中&#xff0c;除了基于 HTTP 的方式&#xff0c;还有 RPC&#xff08;Remote Pr…

无人机之AI跟踪篇

无人机的AI识别技术依托于计算机视觉和深度学习技术&#xff0c;实现了对目标的快速精准识别&#xff0c;在多个领域展现出了巨大的应用潜力和价值。以下是对无人机AI识别技术的详细解析&#xff1a; 一、无人机AI识别算法的基础原理 无人机AI识别算法主要基于先进的计算机视觉…