转载请注明出处: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. Centos7 安装 zabbix3.2

    简介: Zabbix的一个很优秀的分布式监控服务器, 它有两部分组成: 1. “zabbix-server”用来收集并且在web端展示数据 2. “zabbix-agent”用来采集数据,发送给ser ...

  2. C#的for循环使用方法

    for循环是程序语言开发中常见的技法之一,这类循环可以执行指定的次数,并维护它自己的计数器,要定义for循环,需要下述信息:1.初始化计数器变量的一个起始值;2.继续循环的条件,它应涉及到计数器变量; ...

  3. progID

    ProgID程序员给CLSID指定的容易记住的名字ProgID命名约定:<Program>.<Component>.<Version>AppID:将某个APPID( ...

  4. Asp.Net 常用工具类之Office—Excel导出(4)

    开发过程中各类报表导入导出防不胜防,网上也是各种解决方法层出不穷,比如Excel,CSV,Word,PDF,HTML等等... 网上各种导出插件也是层出不穷,NPOI,微软Microsoft.Offi ...

  5. OpenFlow硬件交换机制作及刷机教程

    1.目的 将普通路由器升级成为一台支持OpenFlow的交换机. 具体哪些路由器可以刷OpenFlow可以参考:OpenWRT:http://wiki.openwrt.org/toh/start#su ...

  6. 超级素数幂 java

    链接:https://www.nowcoder.com/questionTerminal/fb511c3f1ac447309368d7e5432c6c79来源:牛客网如果一个数字能表示为p^q(^表示 ...

  7. SignalR指定用户推送消息

    一.首先,在MVC项目中安装SingalR包(SingalR2.0需要.net4.5以上,VS2010可以安装1.1.3版本,本例为VS2010+SignalR1.1.3). 打开工具-NuGet程序 ...

  8. C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(中)

    译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 41 ASP.NET MVC(中)),不对的地方欢迎指出与交流. 章节出自<Professional C# ...

  9. cuda编程学习4——Julia

    书上的例子编译会有错误,修改一下行即可. __device__ cuComplex(float a,float b):r(a),i(b){} /* ========================== ...

  10. Java完成简单猜数字游戏v2.0

    猜数字游戏v2.0 优化了获取随机数.输入数据超出边界值的代码,并增加了异常处理,能够在玩家输入错误数据错误时给出可靠指引,希望对和我一样的新人有帮助, 最后希望有大神愿意帮我解决代码优化的问题,谢谢 ...