当前位置: 首页 > news >正文

Android 使用kotlin+注解+反射+泛型实现MVP架构

一,MVP模式的定义

①Model:用于存储数据。它负责处理领域逻辑以及与数据库或网络层的通信。

②View:UI层,提供数据可视化界面,并跟踪用户的操作,以便通知presenter。

③Presenter:从Model层获取数据,并且应用UI逻辑来决定显示什么。它管理View的状态,并且根据来自于View的用户的输入执行动作。

实现mvp模式的核心点就是将view层和presenter绑定,将view层和model层解耦

二,代码实现

首先,我们先三个基本的接口:

/**
*View层接口
*/
interface IView {fun getContext(): Context?fun getRootViews(): View?fun <T : View?> getView(id: Int): Tfun getActivity(): Activity?fun getFragment(): Fragment?
}
/*** module层接口* */
interface IModule {
}
/*** Presenter层接口* */
interface IPresenter {fun onCreate(savedInstanceState: Bundle?, mContext: Context?)fun onResume()fun onStart()fun onRestart()fun onPause()fun onStop()fun onDestroy()fun onSaveInstanceState(outState: Bundle)fun onRestoreInstanceState(savedInstanceState :Bundle)fun onActivityResult( requestCode :Int, resultCode :Int, data : Intent)}

View层主要是获取Activity,布局文件等操作

Presenter层主要是控制Activity生命周期等

Module层就是用户根据自己的业务逻辑具体的自己去定义

接下来我们就利用反射+注解,在系统启动activity的时候,自动的生成相应的presenter实例,这样就不用手动去绑定view和presenter了

创建一个注解:

@Retention(AnnotationRetention.RUNTIME)
annotation class Request(val value:KClass<*>)

在运行时生效,传入kotlin的Class实例KClass

采用工厂模式来生产presenter

先创建一个工厂模式的接口:

interface IPresenterFactory<P> {/*** 创建presenter* */fun createPresenter():P
}

因为我的Presenter需要等到运行的时候才会知道是哪个,所以使用泛型P代表

创建具体的Presenter工厂类,在静态方法findClass中,通过传入的Activity获得Activity的注解,并通过注解获得相应的presenter的Class

在createPresenter方法中,通过presenter的Class反射生成presenter对象

class PresenterFactory<P>() :IPresenterFactory<P>{private var presenterKClass :KClass<*> ?=nullconstructor(presenterKClass :KClass<*>): this(){this.presenterKClass =presenterKClass}companion object{fun <P> findClass(viewClass:Class<*>) : PresenterFactory<P>{val annotation = viewClass.getAnnotation(Request::class.java)val value:KClass<*> =annotation.valuereturn PresenterFactory(value)}}override fun createPresenter(): P {return presenterKClass?.java!!.newInstance() as P}}

创建一个BasePresenter,实现IPresenter接口,将具体的View层的实例传过来。因为不知道具体的View是哪一个,所以使用泛型T表示

abstract class BasePresenter<T> :IPresenter{protected var mView: @UnsafeVariance T? = nullfun attachViewCompont(view: T) {mView = view}fun detechViewCompont() {mView = null}abstract fun setListeners()
}

为了实现设计模式的单一性原则,我们增加一个工厂类的代理类,来控制presenter的创建,已及view的绑定等操作:

class PresenterDelegate<P,V>() where P:BasePresenter<V>,V:IView {private var presenterFactory :IPresenterFactory<P>?=nullprivate var presenter :P ?=nullconstructor(presenterFactory :IPresenterFactory<P>):this(){this.presenterFactory =presenterFactory}open fun getPresenter():P{if(presenter!=null){return presenter!!}if (presenterFactory != null) {if (presenter == null) {presenter = presenterFactory?.createPresenter()}}return presenter!!}open fun setPresenter(presenter: P) {this.presenter = presenter}open fun bindViewCompont(view: IView) {if (presenter == null) {getPresenter()}if (presenter != null) {presenter!!.attachViewCompont(view as V)}}open fun unbindViewCompont() {if (presenter != null) {presenter!!.detechViewCompont()}}}

创建一个BaseActivity,继承AppCompatActivity实现IView接口,在BaseActivity中,通过创建工厂类的装饰类,创建具体的P层,并将P层和View层绑定

abstract class BaseActivity<P,V> :AppCompatActivity(),IView where P:BasePresenter<V>,V:IView{private var presenterDelegate = PresenterDelegate<P,V>(PresenterFactory.findClass(javaClass))protected val mViews = SparseArray<View>()protected var rootView: View? = nullprotected var mDecorView: View? = nullopen fun <T : View?> bindView(id: Int): T? {var view: T? = mViews[id] as Tif (view == null) {view = rootView?.findViewById(id)mViews.put(id, view)}return view}override fun getContext(): Context? {return this}override fun getRootViews(): View? {return rootView}override fun <T : View?> getView(id: Int): T {return bindView<View>(id) as T}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)presenterDelegate.bindViewCompont(this)rootView = layoutInflater.inflate(getLayoutId(), null, false)setContentView(rootView)initFields()bindEventListener()getPresenter().onCreate(savedInstanceState,this)getPresenter().setListeners()mDecorView = window.decorView}override fun onStart() {super.onStart()getPresenter().onStart()}override fun onRestart() {super.onRestart()getPresenter().onRestart()}override fun onResume() {super.onResume()getPresenter().onResume()}override fun onPause() {super.onPause()getPresenter().onPause()}override fun onStop() {super.onStop()getPresenter().onStop()}override fun onDestroy() {getPresenter().onDestroy()presenterDelegate.unbindViewCompont()super.onDestroy()}protected open fun onSaveInstanceState(outState: Bundle?) {super.onSaveInstanceState(outState!!)getPresenter().onSaveInstanceState(outState!!)}protected open fun onRestoreInstanceState(savedInstanceState: Bundle?) {super.onRestoreInstanceState(savedInstanceState!!)getPresenter().onRestoreInstanceState(savedInstanceState!!)}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)getPresenter().onActivityResult(requestCode, resultCode, data!!)}/*** 返回layout 布局文件的id** @return*/abstract fun getLayoutId(): Int/*** 初始化其他属性*/abstract fun initFields()/*** 设置监听*/abstract fun bindEventListener()open fun getPresenter(): P {return presenterDelegate.getPresenter()}open fun setPresenter(presenter: P) {presenterDelegate.setPresenter(presenter)}}

这样一个mvp架构就搭建完毕了,下面看看使用:

interface ITestView :IView {fun setContent(string: String)}

Module层:

class TestModule :IModule{fun doNetWork(){println("网络请求")}
}

Presenter层:

class TestPresenter: BasePresenter<ITestView>() {var module:TestModule? =nulloverride fun setListeners() {}override fun onCreate(savedInstanceState: Bundle?, mContext: Context?) {module =TestModule()}override fun onResume() {module?.doNetWork()mView?.setContent("222222222")}override fun onStart() {}override fun onRestart() {}override fun onPause() {}override fun onStop() {}override fun onDestroy() {}override fun onSaveInstanceState(outState: Bundle) {}override fun onRestoreInstanceState(savedInstanceState: Bundle) {}override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {}}

View层:

@Request(TestPresenter::class)
class TestActivity :BaseActivity<TestPresenter,ITestView>(),ITestView {var textView:TextView?=nulloverride fun getLayoutId(): Int {return R.layout.activity_main}override fun initFields() {textView =findViewById<TextView>(R.id.txt)}override fun bindEventListener() {}override fun setContent(string: String) {textView?.text =string}override fun getActivity(): Activity? {return this}override fun getFragment(): Fragment? {return null}
}

相关文章:

Android 使用kotlin+注解+反射+泛型实现MVP架构

一&#xff0c;MVP模式的定义 ①Model&#xff1a;用于存储数据。它负责处理领域逻辑以及与数据库或网络层的通信。 ②View&#xff1a;UI层&#xff0c;提供数据可视化界面&#xff0c;并跟踪用户的操作&#xff0c;以便通知presenter。 ③Presenter&#xff1a;从Model层获…...

数据结构——堆(C语言)

本篇会解决一下几个问题&#xff1a; 1.堆是什么&#xff1f; 2.如何形成一个堆&#xff1f; 3.堆的应用场景 堆是什么&#xff1f; 堆总是一颗完全二叉树堆的某个节点总是不大于或不小于父亲节点 如图&#xff0c;在小堆中&#xff0c;父亲节点总是小于孩子节点的。 如图&a…...

B058-SpringBoot

目录 springboot概念与作用入门案例springboot运行方式热部署配置文件Profile多环境支持整合测试-springboot-testSpringboot-web1.返回json数据2.返回页面&#xff08;模板技术&#xff09;thymeleaf1.导入thymeleaf依赖2.模板文件3.controller4.启动类 SSM整合1.导包2.项目目…...

龙迅LT9611UXC 2PORT MIPICSI/DSI转HDMI(2.0)转换器+音频,内置MCU

龙迅LT9611UXC 1.描述&#xff1a; LT9611UXC是一个高性能的MIPI DSI/CSI到HDMI2.0转换器。MIPI DSI/CSI输入具有可配置的单 端口或双端口&#xff0c;1高速时钟通道和1~4高速数据通道&#xff0c;最大2Gbps/通道&#xff0c;可支持高达16Gbps的总带 宽。LT9611UXC支持突发…...

STM32存储左右互搏 I2C总线读写FRAM MB85RC1M

STM32存储左右互搏 I2C总线读写FRAM MB85RC1M 在较低容量存储领域&#xff0c;除了EEPROM的使用&#xff0c;还有铁电存储器FRAM的使用&#xff0c;相对于EEPROM, 同样是非易失性存储单元&#xff0c;FRAM支持更高的访问速度&#xff0c; 其主要优点为没有EEPROM持续写操作跨页…...

1340. 跳跃游戏 V;2039. 网络空闲的时刻;2767. 将字符串分割为最少的美丽子字符串

1340. 跳跃游戏 V 核心思想&#xff1a;动态规划记忆化搜索。定义dfs(i)&#xff0c;表示从i开始最多可以访问多少个下标&#xff0c;然后统计往左跳和往右边跳的最大值&#xff0c;思路其实比较简单&#xff0c;但是代码我感觉还是不太好想。 2039. 网络空闲的时刻 核心思想…...

ElementUI之CUD+表单验证

目录 前言&#xff1a; 增删改查 表单验证 前言&#xff1a; 继上篇博客来写我们的增删改以及表单验证 增删改查 首先先定义接口 数据样式&#xff0c;我们可以去elementUI官网去copy我们喜欢的样式 <!-- 编辑窗体 --><el-dialog :title"title" :visib…...

Linux:nginx---web文件服务器

我这里使用的是centos7系统 nginx源码包安装 Linux&#xff1a;nginx基础搭建&#xff08;源码包&#xff09;_鲍海超-GNUBHCkalitarro的博客-CSDN博客https://blog.csdn.net/w14768855/article/details/131445878?ops_request_misc%257B%2522request%255Fid%2522%253A%25221…...

go 端口转发 代理V2 --chatGPT

问&#xff1a;broker(localPort, targetPort), 实现远程访问localPort的http代理转发到目标机器 gpt: 要实现一个简单的 HTTP 代理服务器&#xff0c;你可以使用 Go 的 net/http 包来处理 HTTP 请求和响应。以下是一个示例&#xff0c;演示如何创建一个 HTTP 代理服务器将本地…...

idea环境下如何打包可运行jar?

工作中有时候偶尔写一些工具类、小程序&#xff0c;可是java程序员制作一个可运行jar实在折腾&#xff0c;利用idea开发环境&#xff0c;可以快速打包自己的可运行jar。具体怎么操作呢&#xff1f; 创建一个空白的java项目并完成自己的程序开发 完成java代码&#xff1a; /**…...

基于FFmpeg的Android播放器

基于FFmpeg的Android播放器 文章目录 基于FFmpeg的Android播放器1. 前言2. 编译相关组件库3. 解码器4. 解码流程5. 音频输出6. 视频输出&#xff08;需要优化&#xff09; 1. 前言 FFmpeg是一个最有名的开源的编解码库&#xff0c;实现了通常的编解码逻辑。它还能够根据平台特…...

osgPBR(十五)镜面IBL--查看不同级别的HDR环境贴图

首先&#xff0c;设置可以使用Mipmap&#xff0c;启用三线性过滤&#xff0c;设置最大级别和最小级别 osg::ref_ptr<osg::TextureCubeMap> tcm new osg::TextureCubeMap; tcm->setTextureSize(128, 128);tcm->setFilter(osg::Texture::MIN_FILTER, osg::Texture:…...

Docker的学习记录

Docker是一个被广泛使用的开源容器引擎&#xff0c;基于Go语言&#xff0c;遵从Apache2.0协议开源。 docker的三个概念&#xff1a;容器、镜像和仓库。 镜像&#xff08;Image&#xff09;&#xff1a;镜像是Docker中的一个模板。通过 Docker镜像 来创建 Docker容器&#xff…...

Android Jetpack组件架构:ViewModel的原理

Android Jetpack组件架构&#xff1a;ViewModel的原理 导言 本篇文章是关于介绍ViewModel的&#xff0c;由于ViewModel的使用还是挺简单的&#xff0c;这里就不再介绍其的基本应用&#xff0c;我们主要来分析ViewModel的原理。 ViewModel的生命周期 众所周知&#xff0c;一般…...

数据分析(python)学习笔记1.0

《利用Python进行数据分析》(原书第2版) 《利用Python进行数据分析》(原书第2版) 《利用Python进行数据分析》(原书第2版) 社区和会议 除了网络搜索,科学、数据相关的Python邮件列表对于解决问题也非常有帮助。可以看看下列邮件列表: pydata:与数据分析和pandas相…...

SW免安装的toolbox只读问题

把SOLIDWORKSDATA 整体复制到另外的目录&#xff0c;然后这里设置目录位置。不然原始位置有只读属性...

nodejs在pdf中绘制表格

需求 之前我已经了解过如何在pdf模板中填写字段了 nodejs根据pdf模板填入中文数据并生成新的pdf文件https://blog.csdn.net/ArmadaDK/article/details/132456324 但是当我具体使用的时候&#xff0c;我发现我的模板里面有表格&#xff0c;表格的长度是不固定的&#xff0c;所…...

使用不同尺寸的传感器拍照时,怎么保证拍出同样视场范围的照片?

1、问题背景 使用竞品机做图像效果对比时&#xff0c;我们通常都会要求拍摄的照片要视场范围一致&#xff0c;这样才具有可比性。之前我会考虑用同样焦距、同样分辨率的设备去拍照对比就可以了&#xff0c;觉得相机的视场范围只由镜头焦距来决定。 但如果对于不同尺寸的传感器…...

01-工具篇-windows与linux文件共享

一般来说绝大部分PC上装的系统均是windows&#xff0c;为了开发linux程序&#xff0c;会在PC上安装一个Vmware的虚拟机&#xff0c;在虚拟机上安装ubuntu18.04&#xff0c;由于windows上的代码查看软件、浏览器&#xff0c;通信软件更全&#xff0c;我们想只用ubuntu进行编译&a…...

医疗实施-住院流程详解

住院就诊流程详解 1.病人入院登记2.病人进入病区3.医生操作病人4.医嘱录入与审核执行5. 医嘱收费前在对应业务系统的操作5.1.药物医嘱5.2.检查检验医嘱5.3.手术医嘱 6.住院医嘱费用的产生7. 医嘱收费后在对应业务系统的操作8. 病人出院 这篇文章是基于我的文章《医疗实施-住院就…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...