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

news2024/12/26 11:53:50

一,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): T

    fun 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<*> ?=null

    constructor(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.value
            return 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? = null

    fun 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>?=null

    private var presenter :P ?=null

    constructor(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? = null
    protected var mDecorView: View? = null

    open fun <T : View?> bindView(id: Int): T? {
        var view: T? = mViews[id] as T
        if (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? =null

    override 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?=null

    override 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
    }
}

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

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

相关文章

Securing TEEs With Verifiable Execution Contracts【TDSC`23】

目录 摘要引言贡献 背景Intel SGX侧信道攻击Intel处理器的硬件扩展 概述威胁模型SGX已存的安全威胁侧信道泄露操作系统相关的威胁现有防御的限制 可验证的执行合同作为防御 摘要 最近的研究表明&#xff0c;可信执行环境&#xff0c;如Intel Software Guard Extensions&#x…

Nginx 背锅解析漏洞

Nginx 背锅解析漏洞 文章目录 Nginx 背锅解析漏洞1 在线漏洞解读:2 环境搭建3 影响版本&#xff1a;4 漏洞复现4.1 访问页面4.2 上传文件 4.3 上传失败4.4 使用bp进行分析包4.5 对返回图片位置进行访问4.6 执行php代码技巧-图片后缀加./php4.7 分析原因 --》cgi.fix_pathinfo--…

工艺防错指导、可视化工具管理——SunTorque智能扭矩系统

智能扭矩系统-智能拧紧系统-智能扭矩控制-SunTorque 拧紧的定义——运用拧紧工具及螺栓&#xff0c;使被联接体紧密贴合&#xff0c;并能承受一定的载荷&#xff0c;且被连接体间具备足够的夹紧力&#xff0c;以确保被联接零件的可靠联接和正常工作。 从定义中前六个字“运用…

解读:ISO 14644-21:2023《洁净室及相关受控环境:悬浮粒子采样》发布指导粒子采样!

药品洁净实验室环境监测结果是否满足微生物检测需求&#xff0c;直接决定检测结果的有效性准确性&#xff0c;进行药品微生物检测&#xff0c;必须对实验环境进行日常和定期监测&#xff0c;其内容包括非生物活性的空气悬浮粒子数及有生物活性的微生物监测。 悬浮粒子监测是保证…

python百钱百鸡

编写程序&#xff0c;解决“百钱百鸡”问题。 一只公鸡值五钱&#xff0c;一只母鸡值三钱&#xff0c;三只小鸡值一钱。 源代码&#xff1a; for a in range(1, 101): for b in range(1, 101): for c in range(1, 101): if (a * 5 b * 3 c / 3 100)…

CSRF攻击

防御策略 过滤判断换referer头&#xff0c;添加tocken令牌验证&#xff0c;白名单 CSRF攻击和XSS比较 相同点&#xff1a;都是欺骗用户 不同点&#xff1a; XSS有攻击特征&#xff0c;所有输入点都要考虑代码&#xff0c;单引号过滤 CSRF没有攻击特征&#xff0c;利用的点…

城市智慧公厕:引领科技创新的新时代

城市智慧公厕已经成为当下社会治理模式的升级范式&#xff0c;催生了无限的科技创新。如智慧公厕源头厂家广州中期科技有限公司&#xff0c;所推出的智慧公厕整体解决方案&#xff0c;除基本的厕位监测与引导、环境监测与调节、安全防范与管理、保洁考勤管理、多媒体交互、综合…

数字化转型的五个等级及思考

数字化转型是当前企业和社会关注的热点话题。然而&#xff0c;对于数字化转型的五个等级及其思考&#xff0c;并没有一个清晰的概述。以下是我对数字化转型的五个等级及其思考的简要探讨。 第一等级&#xff1a;基础设施升级 在数字化转型的初始阶段&#xff0c;企业需要对其基…

长期用眼不再怕!NineData SQL 窗口支持深色模式

您有没有尝试过被明亮的显示器闪瞎眼的经历&#xff1f; 在夜间或低光环境下&#xff0c;明亮的界面会导致许多用眼健康问题&#xff0c;例如长时间使用导致的眼睛疲劳、干涩和不适感&#xff0c;同时夜间还可能会抑制褪黑素分泌&#xff0c;给您的睡眠质量带来影响。 这些问…

uni-app:实现picker下拉列表

效果 代码 <template><view class"container"><picker name"info" change"bindPickerChange9" :value"index9" :range"selectDatas9"><view class"right"><view class"right_l…

Nginx负载均衡详解

一、负载均衡介绍 1、负载均衡的定义 单体服务器解决不了并发量大的请求&#xff0c;所以&#xff0c;我们可以横向增加服务器的数量&#xff08;集群&#xff09;&#xff0c;然后将请求分发到各个服务器上&#xff0c;将原先请求集中到单个服务器上的情况改为将请求分发到多…

【EI会议】第三届大数据、人工智能与风险管理国际学术会议 (ICBAR 2023)

第三届大数据、人工智能与风险管理国际学术会议 (ICBAR 2023) 2023 3rd International Conference on Big Data, Artificial Intelligence and Risk Management 第三届大数据、人工智能与风险管理国际学术会议&#xff08;ICBAR2023&#xff09;将于2023年11月24-26日在中国成…

18643 鸡马立克氏病诊断技术

声明 本文是学习GB-T 18643-2021 鸡马立克氏病诊断技术. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了鸡马立克氏病临床诊断&#xff0c;以及病毒分离、琼脂免疫扩散试验、PCR 检测和荧光定量 PCR 检测等实验室检测的技术要求…

【通世智库】彭子商:秋风集

我的人生阅历是丰富的&#xff0c;感受是复杂的。 我出生于1949年&#xff0c;父亲是中国老一代旧知识分子&#xff0c;是重庆宽仁医院(教会医院)最后一任院长。【作者&#xff1a;彭子商;来源&#xff1a;通世智库;编撰&#xff1a;张小青】 我同共和国一起成长&#xff0c;经…

开源视频处理软件OBS Studio下载和使用

为了免费使用麦克风降噪功能&#xff0c;可以使用可以使用OBS Studio软件。在清华大学开源软件镜像站提供的OBS Studio镜像源上下载或者上csdn 找我的内容源0积分下载。链接https://download.csdn.net/download/A41915460/88371699 录VCR的时候提取打好纸张发言稿件&#xff0c…

Vue3最佳实践 第五章 Vue 组件应用 5 (Vue 插件)

想了解Vue插件所以你看了官方文档却看不懂&#xff0c;或者你想知道Vue.use()方法和插件的关系。在本文档中&#xff0c;我们将参照文档讲解插件制作的基础知识&#xff0c;了解基础知识后&#xff0c;我们将制作与更实用的下拉菜单和脚本加载相关的插件。读完之后&#xff0c;…

浅谈智慧医院的信息集成平台建设与配电设计方案

安科瑞电气股份有限公司 上海嘉定201801 摘要&#xff1a;随着云计算、5G、大数据、物联网等技术的不断发展与进步&#xff0c;推动着智慧医院建设的飞速发展。智慧医院建设强调医院内部业务的多流程联动和医疗信息互联互通的高协同效率&#xff0c;突出了数据驱动下构建高质量…

视频号如何下载到相册,无水印高清下载保存到手机相册

微信视频号是一个全新的内容记录与创作平台&#xff0c;他不同于订阅号与服务号&#xff0c;很多人都有这么一个需求视频号视频怎么下载保存到手机相册&#xff0c;不同的人思路要有所不同&#xff0c;今天这篇内容就和大家聊聊如何下载并保存到手机相册。 视频号如何下载到相…

苹果手机屏幕上的悬浮球怎么设置(必备教程)

苹果手机有一个好用又好玩的功能叫做悬浮球&#xff0c;又名白色小圆点。该功能最大的优点就是给用户提供了便携式访问&#xff0c;它相当于一个快捷键&#xff0c;能够让用户在无需返回主屏幕的情况下快速完成一些其他操作。 但是一些新用户可能还不知道如何设置悬浮球。苹果…