Android下MVP和MVVM模式的实践

news2024/9/27 20:03:27

转载注明出处: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>() {
            @Override
            public void onSuccess(CaseListResult result) {
                mView.onLoadEnd();
                mView.onLoadMoreSuccess(result);
            }

            @Override
            public void onFailure(CaseListResult result) {
                mView.onLoadEnd();
                mView.onLoadFail(result);
            }
        });
    }

    @Override
    public void destory() {
		……
    }
  ……
}
public class PreviewFragment extends UltrasoundBaseFragment
        implements IPreviewView {
	private PreviewPresenter mPresenter;
    private PreviewListAdapter mAdapter;

    @Override
    public 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);
		……
    }

    @Override
    public void onResume() {
        super.onResume();
        reloadData();
    }

    private void reloadData() {
        ……
        mPageNum = 1;
        mPresenter.loadPreviewList(mPageNum);
    }

    @Override
    public void onRefreshStarted() {
		……
    }

    @Override
    public void onRefreshSuccess(CaseListResult caseList) {
        ……
    }

    @Override
    public void onLoadStart() {
        ……
    }

    @Override
    public void onLoadEnd() {
        ……
    }

    @Override
    public 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();
        }
    }

    @Override
    public 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 {

    @Override
    public 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>
        <variable
            name="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.coloradmin.cn/o/2170878.html

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

相关文章

商标价值如何评估与增值?

商标是企业的标志&#xff0c;代表着企业的产品或服务质量、信誉和形象。一个具有高知名度和美誉度的商标&#xff0c;能够为企业带来巨大的商业价值。它不仅可以帮助企业在市场中脱颖而出&#xff0c;吸引消费者的关注和购买&#xff0c;还可以作为企业的重要资产进行融资、并…

无人便利店无人超市云值守收银系统源码

随着人力成本越来越高&#xff0c;很多门店越来想做无人值守模式&#xff0c;尤其是晚上休息时间等想让云值守客服来看店。自然要求收银系统需要可以在【有收银员值守】和【无收银员值守】两种模式灵活切换。 1. 有收银员值守模式 白天有收银员在店&#xff0c;收银员可以协助…

Tomcat服务与运用

案例准备 1.规划节点 IP 主机名 节点 192.168.20.20 tomcat Tomcat 2.基础准备 使用VMWare Workstation软件安装CentOS 7.2操作系统&#xff0c;镜像使用提供的CentOS-7-x86_64-DVD-1804.iso&#xff0c;最小化安装CentOS 7.2系统 案例实施 1.基础环境配置 1.1修改…

微信小程序的 button 标签的边框如何去除?

目录 问题描述&#xff1a; 问题原因&#xff1a; 解决办法&#xff1a; 方案一 方案二 问题描述&#xff1a; 实际开发中会发现这个 button 自带有样式&#xff0c;当背景颜色设置为白色的时候还有一个黑色的边框&#xff0c;刚开始那个边框怎么都去不掉 无法去除的边框…

IRR 之 24期免息等于免费?钱买保险还是余额宝?

一、IRR 是什么&#xff1f; 英文全称是 Internal Rate of Return 具体公式可以参考公众号 随园经济56&#xff5c;内部收益率 简单理解可以为&#xff1a; IRR是投资项目在整个生命周期中所能获得的平均年化收益率 更简单理解为&#xff0c;余额宝展现给你的年化收益 二、…

WLS2连接本地USB设备的方法

WLS2连接本地USB设备的方法 说明windows端1.下载usbipd-win并安装2.启动WSL3.以管理员身份运行Windows PowerShell”4.WSL中查看USB设备 说明 WLS2连接本地USB设备的方法 windows端 1.下载usbipd-win并安装 可下载**.msi文件&#xff0c;双击即可安装 2.启动WSL 3.以管理…

如何将Excel表格嵌入Web网页在线预览、编辑并保存到自己服务器上?

猿大师办公助手作为一款专业级的网页编辑Office方案&#xff0c;不仅可以把微软Office、金山WPS和永中Office的Word文档内嵌到浏览器网页中实现在线预览、编辑保存等操作&#xff0c;还可以把微软Office、金山WPS和永中Office的Excel表格实现网页中在线预览、编辑并保存到服务器…

如何在Windows、Mac和Linux系统上安装和更新Stable Diffusion WebUI

在图像生成领域&#xff0c;Stable Diffusion与Automatic1111表现出色&#xff0c;给MidJourney、OpenAI的DALL-E和Bing图像生成器带来了激烈的竞争。在本文中&#xff0c;我们将帮助您在本地安装Automatic1111 WebUI&#xff0c;以便您可以从文本提示创建出色的图像。 要生成…

原生代理IP是什么?

代理IP的各个类型称呼有很多&#xff0c;且它们在网络使用和隐私保护方面扮演着不同的角色。今天将探讨什么是原生IP以及原生IP和住宅IP之间的区别&#xff0c;帮助大家更好地理解这两者的概念和实际应用&#xff0c;并选择适合自己的IP类型。 一、什么是原生IP&#xff1f; 原…

智慧防灾,科技先行:EasyCVR平台助力地质灾害视频监测系统建设

随着科技的飞速发展&#xff0c;视频监控技术已成为地质灾害监测与预警的重要手段之一。在众多视频监控平台中&#xff0c;EasyCVR视频汇聚平台凭借其强大的视频整合、实时传输、视频处理及分发等能力&#xff0c;在地质灾害场景中展现出显著的应用优势。 一、实时监测与远程监…

2024年自动化、电气控制系统与设备国际学术会议(AECSE 2024)

在线投稿&#xff1a;学术会议-学术交流征稿-学术会议在线-艾思科蓝 2024年自动化、电气控制系统与设备国际学术会议&#xff08;AECSE 2024&#xff09;是致力于将“自动化与电气”领域的专家学者、研发者和技术人员汇集一堂的国际盛会。会议将于2024年10月18-20日在中国南京…

【CSS】透明度 、过渡 、动画 、渐变

opacity 透明度transition 过渡animation 动画background 渐变 ( 线性渐变 \ 径向渐变 ) opacity 透明度 设置元素的透明度&#xff0c;会影响元素及其所有子元素的透明度&#xff0c;值范围&#xff1a;0&#xff08;完全透明&#xff09;到 1&#xff08;完全不透明&#xff…

树上前缀和详解

零、前言 关于前缀和&#xff1a; 前缀和详解&#xff0c;朴素前缀和&#xff0c;前缀和变形&#xff0c;二维前缀和_前缀积-CSDN博客 关于LCA&#xff1a; LCA算法-倍增算法_lca倍增算法-CSDN博客 LCA算法-Tarjan算法_lca数组-CSDN博客 树链剖分——重链剖分&#xff0c…

IGZO基底无电容DRAM单元前景看好

1.DRAM技术简介 DRAM&#xff08;Dynamic Random Access Memory&#xff0c;动态随机存取存储器&#xff09;是一种用于计算机和其他电子设备中的主存储器类型&#xff0c;其主要由存储单元阵列构成&#xff0c;而每一个存储单元由一个电容器和一个晶体管组成&#xff0c;如图…

自动驾驶热成像物体检测数据集-热成像数据集-汽车行人热成像数据集

标签 物体检测fila data set模型 类别 (4) 自行车汽车狗人 度量指标 平均精度均值 (mAP)&#xff1a;88.8%准确率 (Precision)&#xff1a;92.0%召回率 (Recall)&#xff1a;78.9% 尝试此模型 上传一张图片或者从您的设备中选择 描述 自动驾驶热成像物体检测 概述 该…

重回1899元,小米这新机太猛了

如果不出意外&#xff0c;距离高通年度旗舰骁龙 8 Gen4 发布还剩下不到一个月时间。 对于以小米 15 为首即将到来的下半年各家旗舰机型厮杀画面&#xff0c;讲道理小忆早已是备好瓜子儿摆上果盘翘首以盼了。 不过在这之前&#xff0c;中端主流选手们表示有话要说&#xff1a;为…

React UI组件库推荐

Next UI&#xff1a;Vite | NextUI - Beautiful, fast and modern React UI Library Ant Design&#xff1a;Ant Design - 一套企业级 UI 设计语言和 React 组件库

Kafka系列之:安装使用kafka_exporter详细步骤

Kafka系列之:安装使用kafka_exporter详细步骤 一、kafka_exporter二、下载kafka_exporter三、理解Topic Metrics指标四、理解Consumer Groups Metrics指标五、启动kafka_exporter六、查看页面七、systemctl托管服务一、kafka_exporter kafka_exporter源码kafka_exporter下载页…

【大模型对话 的界面搭建-Open WebUI】

Open WebUI 前身就是 Ollama WebUI&#xff0c;为 Ollama 提供一个可视化界面&#xff0c;可以完全离线运行&#xff0c;支持 Ollama 和兼容 OpenAI 的 API。 github网址 https://github.com/open-webui/open-webui安装 第一种 docker安装 如果ollama 安装在同一台服务器上&…

餐饮平价时代到来,价格战只是开始

餐饮正在变天。 “‘活得好’要排在第一位&#xff0c;‘做大做强’往后排。”9月25日&#xff0c;西贝餐饮董事长贾国龙在某餐饮行业论坛上透露&#xff0c;上周与海底捞创始人张勇通电话聊了15分钟&#xff0c;一致认为现在要保利润率&#xff0c;而不是保增长率。 2021年开…