Google 开发Android MVP架构Demo深入解析

目录
  • 1.什么是MVP?
  • 2.Google官方的MVP
  • 3.V1.1 My MVP V1
  • 4.V1.2 My MVP V2

1.什么是MVP?

Google在2016年推出了官方的Android MVP架构Demo,本文主要分析一下官方的MVP Demo,并且借由自己的一些经验,提出一些学习过程中,遇到的问题和自己的改进、封装措施。

MVP架构已经推出很多年了,现在已经非常普及了,我在这里就不过多介绍,简单的说,它分为以下三个层次:

  • Model:数据模型层,主要用来数据处理,获取数据;
  • View:显示界面元素,和用户进行界面交互;
  • Presenter: 是Model和View沟通的桥梁,不关心具体的View显示和Model的数据处理。View层中所有的逻辑操作都通过Presenter去通知Model层去完成,Model中获取的数据通过Presenter层去通知View层显示。 MVP架构最大的好处,就是把传统MVC架构中View层和Control层的复杂关系完全解耦,View层只关心界面显示相关的工作即可,Model层仅获取数据,处理逻辑运算即可,各司其职,而不用关心其他工作。而且大家发现没有,这样设计的话,很好写单元测试代码,针对于View、Presenter、Model层我们可以分别选用适合的单元测试框架,不用再像MVC/MVVM一样,由于代码混在或者分离在多处,到处无法“单一职责”的去完成每个类的单元测试代码编写。

在刚刚接触android的时候,或者说,现在依然有很大一部分的APP开发者,在开发过程中,总是习惯在一个Activity、Fragment中几乎完成了所有的功能。例如网络请求、数据加载、业务逻辑处理、界面加载、界面动画。 后来渐渐的我们接触到了MVC、MVP各种官方的框架,懂得了模块的分离、解耦(MVC),懂得了通过依赖于抽象去分离各个模块彻底解耦(MVP)。 但是官方的MVP框架的确已经帮我们做了很多,但是依然不够,接下来,我们基于官方给的MVP框架,结合六大基本原则,去封装更加适宜于项目的MVP框架。 整体设计模式Demo代码

2.Google官方的MVP

官方Demo怎么去做的?

我们为了方便去分析,我这里简化代码,我们逐步分析,BaseView 和 BasePresenter,BaseView谷歌是这么写的(其实就是view的接口,展示view),以下样例为了理解,我简化处理了部分代码 BaseView.java

package com.itbird.design.principle.mvp.google;
/**
 * Google Demo
 * Created by itbird on 2022/2/25
 */
public interface BaseView<T> {
    //View中,设置presenter对象,使View可以持有Presenter对象引用
    void setPresenter(T presenter);
}

BasePresenter

package com.itbird.design.principle.mvp.google;
/**
 * Google Demo
 * Created by itbird on 2022/2/25
 */
public interface BasePresenter {
}

TaskDetailContract

package com.itbird.design.principle.mvp.google;
/**
 * Google Demo
 * Created by itbird on 2022/2/25
 */
public interface TaskDetailContract {
    interface View extends BaseView<Presenter> {
        //界面UI刷新方法
        void updateTextView(String s);
    }
    interface Presenter extends BasePresenter {
        void loadDataFromModel();
    }
}

TaskGoogleActivity

package com.itbird.design.principle.mvp.google;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.itbird.design.R;
public class TaskGoogleActivity extends AppCompatActivity implements TaskDetailContract.View {
    private static final String TAG = TaskGoogleActivity.class.getSimpleName();
    private TaskDetailContract.Presenter mPresenter;
    private TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.textview);
        Log.e(TAG, TAG + " onCreate");
        new TaskGooglePresenter(this);
    }
    @Override
    public void setPresenter(TaskDetailContract.Presenter presenter) {
        mPresenter = presenter;
    }
    @Override
    public void updateTextView(String s) {
        mTextView.setText(s);
    }
}

TaskGooglePresenter

package com.itbird.design.principle.mvp.google;
public class TaskGooglePresenter implements TaskDetailContract.Presenter {
    private static final String TAG = TaskGooglePresenter.class.getSimpleName();
    TaskDetailContract.View mView;
    public TaskGooglePresenter(TaskDetailContract.View view) {
        mView = view;
        mView.setPresenter(this);
    }
    @Override
    public void loadDataFromModel() {
        //TODO :loaddata,此处可以用model、或者进行业务操作
        //调用界面方法,进行数据刷新
        mView.updateTextView("loaddata success!!!");
    }
}

小结 我们单从设计来说, Google MVP Demo的确向我们展示了MVP的优点:

  • 1)业务、数据、视图分离、解耦,从六大基本原则上来说,开闭、单一职责、里氏代换、依赖倒置、接口隔离、最少知道,基本都做到了。
  • 2)由于完全分离,所以我们很方便针对于每层去选取对应的单元测试模式,例如针对于数据层可以使用(Junit+Mockito)、视图层可以使用(AndroidJunitRunner+Espresso)、业务层可以使用(Junit+Mockito)
  • 3)通过Contract 契约类,完全将视图、业务相关的接口,封装放在了一个地方,简单明了,针对于开发者来说,不容易忘记和辅助养成习惯

但是,但是对于我们实际开发使用来说,依然有以下几点问题:

  • 1)setPresenter接口完全没有必要存在,因为Presenter对象一定是在View类中new出来的,我既然都有它自己的对象了,干嘛还要在Presenter内部,去调用mView.setPresenter(this);,再返回View中,再去保存一个引用,代码看着很怪
  • 2)我们知道MVP的精髓在与,P、V肯定要相互交互,他们互相要持有对方的引用,通过上面一点,我们知道Presenter对象一定是在View类中new出来的,所以View肯定有Presenter对象的引用,这个没问题了。但是Presenter要想拥有View的引用,只能通过Presenter构造方法、或者Presenter内部有一个setView方法,让开发者自己去调用setView或者实现带参构造方法,从设计角度来说,这明显是一个坑,因为一个框架的设计,是为了更加方便开发,而不是让开发人员设置这个、设置那个、必须调用某个方法。既然是必须调用的方法,我们应该通过框架去内部消化掉,而不是给开发者制造麻烦。
  • 3)Presenter中拥有View的引用,如果activity、fragment销毁,但是presenter依然在执行某些任务,这样会导致activity、fragment无法GC回收,导致内存泄露,甚至与崩溃,所以这也是一个框架必须解决的问题。

3.V1.1 My MVP V1

基于上面提出的三点,我们去优化Google的MVP框架。 我们首先将第一点和第二点通过抽象来解决一下。 IPresenter

package com.itbird.design.principle.mvp.v1;
/**
 * 自定义MVP框架,BasePresenter
 * Created by itbird on 2022/2/25
 */
public interface IPresenter {
    /**
     * 与view班定
     *
     * @param view
     */
    void onAttach(IView view);
    /**
     * 与view解绑
     */
    void onDetach();
    /**
     * 是否与view已经班定成功
     *
     * @return
     */
    boolean isViewAttached();
    /**
     * 获取view
     * @return
     */
    IView getView();
}

IView

package com.itbird.design.principle.mvp.v1;
/**
 * 自定义MVP框架,BaseView
 * Created by itbird on 2022/2/25
 */
public interface IView {
}

接下来是借助activity生命周期,对presenter的初始化进行封装 BaseActivity

package com.itbird.design.principle.mvp.v1;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
/**
 * Created by itbird on 2022/3/29
 */
public abstract class BaseActivity extends Activity implements IView {
    IPresenter mPresenter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = createPresenter();
        if (mPresenter != null) {
            mPresenter.onAttach(this);
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.onDetach();
            mPresenter = null;
        }
    }
    abstract IPresenter createPresenter();
}

BasePresenter

package com.itbird.design.principle.mvp.v1;
import java.lang.ref.WeakReference;
/**
 * Created by itbird on 2022/3/29
 */
public class BasePresenter<V extends IView> implements IPresenter {
    WeakReference<V> mIView;
    @Override
    public void onAttach(IView iView) {
        mIView = new WeakReference<>((V) iView);
    }
    @Override
    public void onDetach() {
        mIView = null;
    }
    @Override
    public V getView() {
        if (mIView != null) {
            mIView.get();
        }
        return null;
    }
    @Override
    public boolean isViewAttached() {
        return mIView != null && mIView.get() != null;
    }
}

4.V1.2 My MVP V2

看上图类图,我们依然发现有一些不满足的点:

1)activity中,依然需要初始化mPresenter

 @Override
    IPresenter createPresenter() {
        mTaskPresenter = new TaskMyPresenter();
        return mTaskPresenter;
    }

是否可以做到在activity中,自己像presenter中调用view一样,自己一句话getView就可以搞定

2)观察类图,其实IView、IPresenter没有必要存在,因为毕竟只是baseActivity、basePresenter的行为

所以改造如下: BasePresenter

package com.itbird.design.principle.mvp.v2;
import java.lang.ref.WeakReference;
/**
 * Created by itbird on 2022/3/29
 */
public abstract class BasePresenter<V> {
    WeakReference<V> mIView;
    public void onAttach(V iView) {
        mIView = new WeakReference<>(iView);
    }
    public void onDetach() {
        mIView = null;
    }
    public V getView() {
        if (mIView != null) {
            mIView.get();
        }
        return null;
    }
    public boolean isViewAttached() {
        return mIView != null && mIView.get() != null;
    }
}

BaseActivity

package com.itbird.design.principle.mvp.v2;
import android.app.Activity;
import android.os.Bundle;
import androidx.annotation.Nullable;
/**
 * Created by itbird on 2022/3/29
 */
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends Activity {
    T mPresenter;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = initPresenter();
        if (mPresenter != null) {
            mPresenter.onAttach((V) this);
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.onDetach();
            mPresenter = null;
        }
    }
    public T getPresenter() {
        return mPresenter;
    }
    abstract T initPresenter();
}

此时,契约类,不再需要依赖BasePresenter/BaseView相关接口,而且View中也可以自己获取到presenter的引用了。

package com.itbird.design.principle.mvp.v2;
/**
 * my Demo
 * Created by itbird on 2022/2/25
 */
public interface TaskMyContract {
    interface View {
        //界面UI刷新方法
        void updateTextView(String s);
    }
    interface Presenter {
        void loadDataFromModel();
    }
}
package com.itbird.design.principle.mvp.v2;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import com.itbird.design.R;
public class TaskMyActivity extends BaseActivity<TaskMyContract.View, TaskMyPresenter> implements TaskMyContract.View {
    private static final String TAG = TaskMyActivity.class.getSimpleName();
    TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.textview);
        Log.e(TAG, TAG + " onCreate");
        mPresenter.loadDataFromModel();
    }
    @Override
    TaskMyPresenter initPresenter() {
        return new TaskMyPresenter();
    }
    @Override
    public void updateTextView(String s) {
        mTextView.setText(s);
    }
}

此时的类图,是否更加明确,而且上面各个问题都已经得到了解决。

整体设计模式Demo代码

以上就是Google 开发Android MVP架构Demo深入解析的详细内容,更多关于Google Android MVP 架构的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android 获取实时网速实现详解

    目录 正文 TrafficStats简介 实现获取网速 实时网速 正文 最近接到个需求,需要计算WebView加载网页时的网速.查询了一下,Android没有提供直接获取网速的Api,但是提供了获取流量的类TrafficStats.本文介绍如何使用Trafficstats来实现获取网速功能. TrafficStats简介 TrafficStats提供了一些获取设备从本次开机到目前为止传输/接收的流量的接口,如下: 方法 参数 说明 getTotalTxBytes - 获取设备本次开机到目前为止,

  • Android 搜索框架使用详解

    目录 搜索框架简介 使用搜索框架实现搜索功能 可搜索配置 搜索页面 使用SearchView 使用搜索弹窗 搜索弹窗对Activity生命周期的影响 附加额外的参数 语音搜索 搜索记录 创建SearchRecentSuggestionsProvider 修改可搜索配置 在搜索页面中保存查询 清除搜索历史 示例 搜索框架简介 App中搜索功能是必不可少的,搜索功能可以帮助用户快速获取想要的信息.对此,Android提供了一个搜索框架,本文介绍如何通过搜索框架实现搜索功能. Android 搜索框架

  • Android 使用Toolbar实现应用栏实例详解

    目录 使用Toolbar实现应用栏 应用栏功能扩展 返回 菜单 使用Toolbar实现应用栏 App中应用栏是十分常见的,通常应用栏会显示当前页面的标题,还有一些操作按钮,例如返回.搜索.扫码等.本文介绍如何通过Toolbar实现应用栏. 使用Toolbar来实现应用栏,需要在AndroidManifest中设置NoActionBar的主题,并且Activity需要继承AppCompatActivity. <?xml version="1.0" encoding="ut

  • Android Jetpack导航组件Navigation创建使用详解

    目录 引言 依赖项 创建导航图 导航宿主 导航到目的地 传递参数 NavigationUI 多模块导航 引言 导航是指支持用户导航.进入和退出应用中不同内容片段的交互.Android Jetpack 的导航组件可实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对. 依赖项 def nav_version = "2.5.2" implementation "androidx.navigation:navigation-fragment-kt

  • Android O对后台Service限制详解

    目录 Service问题 什么是前台应用 前台Service和后台Service 后台Service限制 解决后台Service限制 Service问题 Service没有界面,运行于后台,它会消耗设备资源,并且可能会导致不好的用户体验,例如资源占用过多,导致设备运行不流畅.为了缓解这个问题,Android O版本(Android 8.0, API 26)对后台Service强加了一些限制.注意,只是对后台Service加了限制,前台Service不受影响. 什么是前台应用 在解释后台Servi

  • Android开发中Signal背后的bug与解决

    目录 背景 出现SIGABRT的原因 SIGSEGV被捕获但是调用jni无法进行 小结 背景 熟悉我的老朋友可能都知道,之前为了应对crash与anr,开源过一个“民间偏方”的库Signal,用于解决在发生crash或者anr时进行应用的重启,从而最大程度减少其坏影响. 在维护的过程中,发生过这样一件趣事,就是有位朋友发现在遇到信号为SIGSEGV时,再调用信号处理函数的时候 void SigFunc(int sig_num, siginfo *info, void *ptr) { // 这里判

  • Android 控件自动贴边实现实例详解

    目录 正文 判断交互 隐藏与显示 示例 正文 最近接到个需求,需要在用户与App交互时,把SDK中之前实现过的悬浮控件贴边隐藏,结束交互后延迟一段时间再自动显示.本篇文章介绍一下实现的思路. 判断交互 用户与App交互.结束交互可以通过监听触摸事件来实现.建议使用的Activity的dispatchTouchEvent,Activity下的所有触摸事件分发时都会回调此方法,代码如下: class AutoEdgeHideActivity : BaseGestureDetectorActivity

  • Android ApplicationContext接口深入分析

    目录 需求 实现方法 代码 调用 Application getApplicationContext() 参考 需求 Android(Kotlin)获取应用全局上下文 ApplicationContext. 希望可以在应用内任意位置获取上下文,而不是仅在 Activity 或 Service 里才能获取. ApplicationContext 是和应用全局相关的. 实现方法 自定义 MyApplication,保存自身的 Application 实例. MyApplication 配置到 And

  • Google 开发Android MVP架构Demo深入解析

    目录 1.什么是MVP? 2.Google官方的MVP 3.V1.1 My MVP V1 4.V1.2 My MVP V2 1.什么是MVP? Google在2016年推出了官方的Android MVP架构Demo,本文主要分析一下官方的MVP Demo,并且借由自己的一些经验,提出一些学习过程中,遇到的问题和自己的改进.封装措施. MVP架构已经推出很多年了,现在已经非常普及了,我在这里就不过多介绍,简单的说,它分为以下三个层次: Model:数据模型层,主要用来数据处理,获取数据: View

  • Android MVP BaseFragment 通用式封装的实现

    这篇已经是我们的 BaseMVP 基础框架系列文章的第六篇了,BaseMVP 已经被我们封装了快差不多了,从上篇的文章(Android MVP 架构(五)MVP 多个 Presenter 依赖注入)中,我们解决了多的 Presenter 的问题,这是利用依赖注入及反射的方式,动态的去实例化不同 Presenter 层实现类,并且与同一个 View 做了绑定和解绑的操作,这就是一个 View 对多 Presenter 的需要手动 new 的解决方案. BaseMVP 基础框架的搭建,到这里的话,还

  • 浅谈Android官方MVP架构解读

    综述 对于MVP (Model View Presenter)架构是从著名的MVC(Model View Controller)架构演变而来的.而对于Android应用的开发中本身可视为一种MVC架构.通常在开发中将XML文件视为MVC中的View角色,而将Activity则视为MVC中的Controller角色.不过更多情况下在实际应用开发中Activity不能够完全充当Controller,而是Controller和View的合体.于是Activity既要负责视图的显示,又要负责对业务逻辑的

  • 详解Android中的MVP架构分解和实现

    1.概述 传统的Android开发架构一般是MVC模式, Model:业务逻辑和实体模型 View:对应于布局文件 Controllor:对应于Activity 单独从逻辑看起来非常好,与我们做Web开发时,开发模式类似,但在实际开发中,View对应于布局文件,实际上关于该布局文件中的数据绑定的操作,事件处理的代码都在Activity中,Activity既像View又像Controller(MVVP架构中包括数据绑定),导致Activity中职责太重,耦合度大.修改和维护起来非常麻烦. 2.MV

  • 详解Android MVP开发模式

    本文主要讲解MVP开发模式以及具体实例. 一.简介 MVP(Model View Presenter)模式是著名的MVC(Model View Controller)模式的一个演化版本,目前它在Android应用开发中越来越重要了.初看起来我们会感觉增加了很多类接口代码看起来更加清晰. MVP模式可以分离显示层和逻辑层,所以功能接口如何工作与功能的展示可以实现分离,MVP模式理想化地可以实现同一份逻辑代码搭配不同的显示界面.不过MVP不是一个结构化的模式,它只是负责显示层而已,任何时候都可以在自

  • Android登录代码MVP架构详解

    登录代码MVP架构的具体实现,分享给大家. MainActivity.java public class MainActivity extends AppCompatActivity implements View.OnClickListener, Mvp.fff { private EditText mUsername; private EditText mPassword; private Button login; private ProgressDialog mProgressDialo

  • SpringBoot基本web开发demo过程解析

    这篇文章主要介绍了SpringBoot基本web开发demo过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.在创建的springboot项目中的pom.xml中导入Lombok的依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18

  • Android开发组件化架构设计原理到实战

    目录 为什么需要组件化 组件化和模块化 模块化架构 组件化架构 组件化带来的优势 组件化需解决的问题 资源冲突解决 AndroidManifest 独立调试 单工程方案 多工程方案 页面跳转 Arouter 实现组件间方法调用 组件化的消息通信方式选择 广播 事件总线 Application生命周期分发 为什么需要组件化 小项目是不需要组件化的.当一个项目有数十个人开发,编译项目要花费10分钟,修改一个bug就可能会影响到其他业务,小小的改动就需要进行回归测试,如果是这种项目,那么我们需要进行组

  • Android音视频开发Media FrameWork框架源码解析

    目录 一.Media FrameWork背景 二.Media Framework“路线图” 2.1 代理端 2.2 服务端 2.2.1 Source 2.2.2 Decoder 2.2.3 Renderer 2.2.4 Foundation 2.3 OMX端 2.4 Kernel端 三.media播放的流程 四.Media FrameWork源码分析 一.Media FrameWork背景 Media Framework (媒体函数库):此函数库让Android 可以播放与录制许多常见的音频与视

  • Android中gson、jsonobject解析JSON的方法详解

    JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换.JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为. JSON对象: JSON中对象(Object)以"{"开始, 以"}"结束. 对象中的每一个item都是一个key-value对, 表现为"key:value"的形式, ke

随机推荐