转载请注明出处:http://www.cnblogs.com/cnwutianhao/p/6700668.html

引言:在Google没有给出一套权威的架构实现之前,很多App项目在架构方面都有或多或少的问题。第一种常见问题是没有架构,需求中的一个页面对应项目中的一个activity或一个fragment,所有的界面响应代码、业务逻辑代码、数据请求代码等等都集中在其中。第二种常见的问题是架构实现的不断变化,不断在各种架构间摇摆,一直找不到一个适合自己的架构。

Google官方示例项目地址 https://github.com/googlesamples/android-architecture/tree/todo-mvp/

Google提供这个示例项目有两个目的:

  • Provide a basic Model-View-Presenter (MVP) architecture without using any architectural frameworks.
  • Act as a reference point for comparing and contrasting the other samples in this project.

中文解释:

  • 提供了一个基础的MVP架构,而不是用其他的架构。
  • 用这个项目和其他类似的做一个参考对比。

当然Google也明确表示了这些示例只是用来做参考,而并不是要为了当做标准

下面我们从源码的角度来分析todo-mvp(mvp基础架构示例)的实现。我们先从项目的整体组织方式开始,再看项目究竟使用了哪些组件,最后当然是最重要的具体mvp的实现方式。

先看一下项目代码组织方式:

项目含一个app src目录,4个测试目录,分别是androidTest(UI层测试)、androidTestMock(UI层测试mock数据支持)、test(业务层单元测试)、mock(业务层单元测试mock数据支持)。

src目录的代码组织方式完全是按照功能来组织的,功能内部分为xActivity、xContract、xFragment、xPresenter四个类文件(x代表业务名称)。

组件使用

由于项目是基于gradle进行编译的,所以我们可以从build.gradle文件看到项目依赖的全貌。

dependencies {
// App's dependencies, including test
compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
compile "com.android.support:cardview-v7:$rootProject.supportLibraryVersion"
compile "com.android.support:design:$rootProject.supportLibraryVersion"
compile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion"
compile "com.android.support:support-v4:$rootProject.supportLibraryVersion"
compile "com.android.support.test.espresso:espresso-idling-resource:$rootProject.espressoVersion"
compile "com.google.guava:guava:$rootProject.guavaVersion" // Dependencies for local unit tests
testCompile "junit:junit:$rootProject.ext.junitVersion"
testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
testCompile "org.hamcrest:hamcrest-all:$rootProject.ext.hamcrestVersion" // Android Testing Support Library's runner and rules
androidTestCompile "com.android.support.test:runner:$rootProject.ext.runnerVersion"
androidTestCompile "com.android.support.test:rules:$rootProject.ext.runnerVersion" // Dependencies for Android unit tests
androidTestCompile "junit:junit:$rootProject.ext.junitVersion"
androidTestCompile "org.mockito:mockito-core:$rootProject.ext.mockitoVersion"
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.2' // Espresso UI Testing
androidTestCompile "com.android.support.test.espresso:espresso-core:$rootProject.espressoVersion"
androidTestCompile "com.android.support.test.espresso:espresso-contrib:$rootProject.espressoVersion"
androidTestCompile "com.android.support.test.espresso:espresso-intents:$rootProject.espressoVersion" // Resolve conflicts between main and test APK:
androidTestCompile "com.android.support:support-annotations:$rootProject.supportLibraryVersion"
androidTestCompile "com.android.support:support-v4:$rootProject.supportLibraryVersion"
androidTestCompile "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion"
androidTestCompile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
androidTestCompile "com.android.support:design:$rootProject.supportLibraryVersion"
}

项目中使用到了Guava库,官网地址 https://github.com/google/guava

该库是Google在基于java的项目中都会引用到得一个库,库中包含大约14k的方法数,是个很大的库,其中包含了集合、缓存、并发、基本注解、字符串处理、io处理等等。项目中使用Guava库主要是处理null这种不安全的情况,因为一般我们在使用有可能为null的对象时,一般会增加一次判断。比如项目中的出现的:

public boolean isEmpty() {
return Strings.isNullOrEmpty(mTitle) &&
Strings.isNullOrEmpty(mDescription);
}

这样面对空的时候,就不用再多写很多代码了,确实是方便了很多。但是不建议为了null安全直接引入如此大的一个库,因为我们都知道android apk的65k方法数限制,如果要用的话可以把源码中涉及到得部分直接拿出来用。当然Guava中还有很多重要的功能,其他功能读者可以自行研究,关于Guava就先到这里了。

测试相关组件

示例项目在可测试方面做的非常好,由于对视图逻辑(view层)和业务逻辑(presenter层)进行了拆分,所以我们就可以对UI、业务代码分别进行测试。为了进行UI测试引入了Espresso,为了对业务层进行单元测试引入了junit,为了生成测试mock对象引入了mockito,为了支撑mockito又引入了dexmaker,hamcrest的引入使得测试代码的匹配更接近自然语言,可读性更高,更加灵活。

重头戏:项目MVP实现方式

1.基类

两个Base接口 BasePresenter 和 BaseView,这两个类分别是 presenter 和 view 的基类。

public interface BasePresenter {

    void start();

}

BasePresenter 中含有方法 start(),该方法的作用是 presenter 开始获取数据并调用 view 中方法改变界面显示,其调用时机是在 Fragment 类的 onResume 方法中。

项目中调用 start() 的地方:

public interface BaseView<T> {

    void setPresenter(T presenter);

}

BaseView 中含方法 setPresenter(),该方法作用是在将 presenter 实例传入 view 中,其调用时机是 presenter 实现类的构造函数中。

项目中调用 setPresenter() 的地方:

2.契约类

与之前见到的所有mvp实现都不同,Google官方的实现中加入了契约类来统一管理view与presenter的所有的接口,这种方式使得view与presenter中有哪些功能,一目了然,维护起来也方便,实例如下:

public interface TasksContract {

    interface View extends BaseView<Presenter> {

        void setLoadingIndicator(boolean active);

        void showTasks(List<Task> tasks);

        void showAddTask();

        ...
} interface Presenter extends BasePresenter { void result(int requestCode, int resultCode); void loadTasks(boolean forceUpdate); void addNewTask(); ...
}
}

3.Activity在MVP中的作用

Activity 在项目中是一个全局的控制者,负责创建 view 以及 presenter 实例,并将二者联系起来,下面是 Activity 中创建 view 及 presenter 的代码:

TasksFragment tasksFragment =
(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(
getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
} // Create the presenter
mTasksPresenter = new TasksPresenter(
Injection.provideTasksRepository(getApplicationContext()), tasksFragment);

我们可以从上面看到整个创建过程,而且要注意的是创建后的 Fragment 实例作为 presenter 的构造函数参数被传入,这样就可以在 presenter 中调用 view 中的方法了。

4.MVP的实现与组织

实例中将 Fragment 作为 view 层的实现类,为什么是 Fragment 呢?

有两个原因:

  1. 我们把 Activity 作为一个全局控制类来创建对象,把 Fragment 作为 view,这样两者就能各司其职。
  2. 因为 Fragment 比较灵活,能够方便的处理界面适配的问题。

我们先看 view 的实现,我们只挑一部分重要的方法来看

public class TasksFragment extends Fragment implements TasksContract.View {

    ...

    @Override
public void onResume() {
super.onResume();
mPresenter.start();
} @Override
public void setPresenter(@NonNull TasksContract.Presenter presenter) {
mPresenter = checkNotNull(presenter);
} ... }

上面可以看到 setPresenter() 方法,该方法继承于父类,通过该方法,view 获得了 presenter 得实例,从而可以调用 presenter 代码来处理业务逻辑。我们看到在 onResume 中还调用了 presenter 得 start() 方法。

下面我们再看presenter的实现

public class TasksPresenter implements TasksContract.Presenter {

    ...

    public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); mTasksView.setPresenter(this);
} @Override
public void start() {
loadTasks(false);
} ... }

presenter 构造函数中调用了 view 的 setPresenter() 方法将自身实例传入,start() 方法中处理了数据加载与展示。如果需要界面做对应的变化,直接调用 view 层的方法即可,这样 view 层与 presenter 层就能够很好的被划分。

最后还剩下 model 层实现,项目中 model 层最大的特点是被赋予了数据获取的职责,与我们平常 model 层只定义实体对象截然不同,实例中,数据的获取、存储、数据状态变化都是 model 层的任务,presenter 会根据需要调用该层的数据处理逻辑并在需要时将回调传入。这样 model、presenter、view 都只处理各自的任务,此种实现确实是单一职责最好的诠释。

5.总结:

我们再来整体看下官方的实现方式有哪些特性。首先是复杂度,我们可以从上面的分析看出整体的复杂度还是较低的,易学的;然后是可测试性,由于将UI代码与业务代码进行了拆分,整体的可测试性非常的好,UI层和业务层可以分别进行单元测试;最后是可维护性和可扩展性,由于架构的引入,虽然代码量有了一定的上升,但是由于界限非常清晰,各个类职责都非常明确且单一,后期的扩展,维护都会更加容易。

关注我的新浪微博,获取更多Android开发资讯!
关注科技评论家,领略科技、创新、教育以及最大化人类智慧与想象力!

Google官方MVP模式示例项目解析 todo-mvp的更多相关文章

  1. 转:Android官方MVP架构示例项目解析

    转自: http://www.infoq.com/cn/articles/android-official-mvp-architecture-sample-project-analysis 作者 吕英 ...

  2. Android MVP模式 谷歌官方代码解读

    Google官方MVP Sample代码解读 关于Android程序的构架, 当前(2016.10)最流行的模式即为MVP模式, Google官方提供了Sample代码来展示这种模式的用法. Repo ...

  3. MVP模式在Android项目中的使用

    以前在写项目的时候,没有过多考虑架构模式的问题,因为之前一直做J2EE开发,而J2EE都是采用MVC模式进行开发的,所以在搭建公司项目的时候,也是使用类似MVC的架构(严格来讲,之前的项目还算不上MV ...

  4. Android -- 思考 -- 为什么要在项目中使用MVP模式

    1,其实有时候一直在找借口不去思考这个问题,总是以赶项目为由,没有很认真的思考这个问题,为什么我们要在项目中使用MVP模式,自己也用MVP也已经做了两个项目,而且在网上也看了不少的文章,但是感觉在高层 ...

  5. android的MVP模式

    MVP简介 相信大家对MVC都是比较熟悉了:M-Model-模型.V-View-视图.C-Controller-控制器,MVP作为MVC的演化版本,那么类似的MVP所对应的意义:M-Model-模型. ...

  6. MVP模式(Android)

    以前在写项目的时候,没有过多考虑架构模式的问题,因为之前一直做J2EE开发,而J2EE都是采用MVC模式进行开发的,所以在搭建公司项目的时候,也是使用类似MVC的架构(严格来讲,之前的项目还算不上MV ...

  7. Android MVP模式

    转自http://segmentfault.com/blogs,转载请注明出处Android MVP Pattern Android MVP模式\[1\]也不是什么新鲜的东西了,我在自己的项目里也普遍 ...

  8. .Net平台-MVP模式初探(一)

    为什么要写这篇文章 笔者当前正在负责研究所中一个项目,这个项目基于.NET平台,初步拟采用C/S部署体系,所以选择了Windows Forms作为其UI.经过几此迭代,我们发现了一个问题:虽然业务逻辑 ...

  9. MVP模式在Android中的使用

    转载了一篇博客.博客来自:http://www.liuling123.com/2015/12/mvp-pattern-android.html 觉得博主写的非常好 曾经在写项目的时候.没有过多考虑架构 ...

随机推荐

  1. 禁止Linux系统被 ping

    echo "net.ipv4.icmp_echo_ignore_all=1" >> /etc/sysctl.conf sysctl -p 生效 开启ping功能: 删除 ...

  2. 第26篇 jQuery 快速学习下

    前面说了下jQuery了,大部分说的都是选择器和过滤器方面的东西,这个写完后,后面就说下剩下的东西了,离目标越来越近了.下面就说说这些东西 事件 传统比较 在js中说了原生js的绑定事件,基本的形式如 ...

  3. 第25篇 jQuer快速学习(上)---选择器和DOM操作

    这个文章经历的时间比较长,不是因为jQuery比较难,而是东西比较多,真心是个体力活.所以本来想把jQuery做成一篇去写,但由于写的时候发现jQuery发现写成一篇的话过于长,对于阅读起来也不是一个 ...

  4. Asp.Net 常用工具类之加密——非对称加密RSA算法

    踏入程序员这个行业也有几年了,几年中有收获(技术加强),有付出(时间和亚健康状态).当然喏,并不后悔,代码路还长!!! On The Way,永不止步!!! 开发过程中也积累了一些自己的经验.代码块和 ...

  5. 数据库笔试面试题库(Oracle、MySQL等)

    数据库笔试面试题库(Oracle.MySQL等) 版权声明:版权所有,欢迎分享本文,转载请保留出处,否则追究法律责任,谢谢合作. 注:本文将持续更新,可关注作者微信公众号以便获得最新笔试面试资料. ⊙ ...

  6. x86主机搭建家庭智能路由系统 ---- Proxmox虚拟化实现一机多用

    Proxmox VE简介 Proxmox VE(Proxmox Virtual Environment) 是一款完全开源虚拟化管理平台,可以管理QEMU/KVM虚拟机和LXC容器.事实上它只是一个前端 ...

  7. 微信小程序之提高应用速度小技巧

    作者:vicyao, 腾讯web前端开发 高级工程师商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. 原文链接:http://wetest.qq.com/lab/view/294.htm ...

  8. Triangle Problems

    Triangle Problem songxiuhuan 宋修寰 Import the Junit and eclemma Choose the project and right click, ch ...

  9. 根据模板导出Excel报表并生成多个Sheet页

    因为最近用报表导出比较多,所有就提成了一个工具类,本工具类使用的场景为  根据提供的模板来导出Excel报表 并且可根据提供的模板Sheet页进行复制 从而实现多个Sheet页的需求, 使用本工具类时 ...

  10. gitoschina使用入门

    1 下载git sudo apt-get install git 2 添加公钥:terminal:  ssh-keygen -t rsa -C "git.oschina.net" ...