美文网首页MVP项目Android知识Android开发
[译]MVP实践(Android)-Part1:让我们了解这个项

[译]MVP实践(Android)-Part1:让我们了解这个项

作者: 唐先僧 | 来源:发表于2016-12-17 10:05 被阅读267次

使用MVP, RxJava Dagger2, Retrofit2, Test 以及所有最新的现代方法、库来实现一个Android实例应用。

我在StackOverFlow上发现一些有关MVP使用的问题没有回答,这促使我产生强烈的兴趣来写下这一系列文章,并提供一个样例工程(即我自己的实践经验)。

我从去年开始熟悉MVP,一个tuxedo开发,并开始寻找样例和教程。我花了很长时间来处理、连接这个未知大谜题的不同部分。我可以推荐的最有用的网站是caster.io,它总是充满了新的Android视频教程。

解释*MVP本身看起来有些古怪!因为已经有许多文章解释它是怎么工作的,以及它是怎么分层的等等,但是附带足够的评论(注释)来帮助新手了解这个方法的样例少之又少。

变化是怎么开始的。。。

这一切都从SOLID(面向对象的设计原则)开始,感谢亲爱的Robert C. Martin

维基文章的内容我们知道SOLID代表:

- S (SRP): 单一功能原则(认为对象应该仅具有一种单一功能的概念)

- O (OCP): 开闭原则(认为“软件体应该是对于扩展开放的,但是对于修改封闭的”的概念)

- L (LSP): 里氏替换原则(认为“程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的”的概念)

- I (ISP): 接口隔离原则(认为“多个特定客户端接口要好于一个宽泛用途的接口”的概念)

- D (DIP): 依赖反转原则(认为一个方法应该遵从“依赖于抽象而不是一个实例”的概念。依赖注入是该原则的一种实现方式。)

MVP在一定程度上尝试遵循这5条原则的全部。我将竭尽全力在示例项目中逐一定位这些原则,以使它们更加透明。

通过这篇完美的MVP文章,MVP代表:

Model 就是将在View(用户界面)中展现的数据。
View就是用于展示数据(Model)的的界面,同时将用户的命令(events)传递给Presenter,由Presenter对数据进行操作。view通常持有一个Presenter的引用
Presenter就是一个“中间人”(MVC模式中Controller扮演的角色),它同时持有vew和model的引用。

Model?!!!

请注意“Model”这个词语有误导性
它应该是检索或者操作Model的业务逻辑
比如:如果你有一个数据库,在一个表中存储了User数据,你的view想要展示一个用户列表,那么Presenter应该持有一个数据库业务逻辑(比如一个DAO)的引用,通过这个业务逻辑Presenter可以查询到一个用户列表。

你能够对MVP再多做一点解释吗?

不不不不不不!!通过关键字Google不能翻墙的就Bing),你会发现所有关于这个新方法的理论。(或者至少阅读一下这篇文章)。

这个样例项目是关于什么的?

这个应用是Marvel的人物搜索程序,Marvel.com的一个简单Android客户端。此应用程序由我创建,作为smava GmbH技术团队的技术评估的一部分.

Marvel Android 应用程序截屏Marvel Android 应用程序截屏

这个应用需要搜索人物,展现搜索结果并缓存上一次搜索。

这个项目的实现使用了MVP,包含了一些现代的Android开发理念和第三方库,这些都可以改变你的职业生涯!

在接下来的系列文章的不同部分我将竭尽所能去解释一切,即:Dagger,Retrofit,RxJava和Tests。

这个项目使用了Circleci.comTravis-ci.org来做持续集成(CI),使用了Codecov.io来做代码覆盖测试,还使用了google的Firebase,这一部分你可以自己学习,因为一定程度上这已经脱离了本文的主题。

在开始之前,你可以先阅读该工程的README任务列表文件来多做一些了解。

Okey,告诉我你都了解到了什么:

让我们先来看一下项目的结构:
我个人喜欢整洁的代码,所以我喜欢将项目分成有意义的模块,以便我和整个团队保持更清晰的任务。

Modules:

整个工程包含两个main module 和一个java console sample module:

app module包含MVP中的Android View层,其余两层(ModelPresentation)都放在了core中,core是一个纯粹的java包,编译后可以生成一个jar库。

把代码按照这种方式分成几个module有什么好处?!

  • 首先,将Android Application module 分开,是为了提醒你不要传递Context或者任何Android相关的对象给Presenter或者Model!所以请现在就停止那样做!!!
  • 其次,你能够确保你的core部分是非常完整,你甚至可以把它和另一个UI搭配使用(即:java Console sample,Web控件,甚至在未来的某一天当 一个iOS使用java!!)
  • 最后,我和我们的团队真的喜欢以这样的方式开发应用!并且单独分离core部分使整个团队都受益,我们甚至将core放到git的一个submodule中在不同的项目中使用,大家使用同一个core而使用不同的UI
    java sample module和Android Application一样使用core的运行结果java sample module和Android Application一样使用core的运行结果

模块名称清理:

为了使modules看起来方便且好看,你可以像这样编辑settings.gradle文件:

include ':marvel-app', ':marvel-core', ':marvel-console'
project(':marvel-app').projectDir = new File('app')
project(':marvel-core').projectDir = new File('core-lib')
project(':marvel-console').projectDir = new File('console')

这样会使modules看起来像这样:



当然这也会导致相关APK文件的名称发生变化。

怎样避免不同的module中版本冲突以及冗余?

在你的Project module 中使用gradle的一个特性可以很方便的获取一份整洁的build.gradle文件,同时还能避免版本冲突和冗余问题。

首先,将你所有project的依赖放到一个gradle文件中,比如libraries.gradle:

ext {
    minSdkVersion = 9
    compileSdkVersion = 25
    buildToolsVersion = "25.0.0"

    //Android
    androidSupportVersion = "25.0.0"
    butterknifeVersion = "8.0.1"
    
    /*...*/

    libraries = [
            androidSupport   : "com.android.support:support-v4:${androidSupportVersion}",
            appCompat        : "com.android.support:appcompat-v7:${androidSupportVersion}",
            designSupport    : "com.android.support:design:${androidSupportVersion}",
            
            /*...*/
            
    ]
    
    /*...*/

}

然后将他放到你的project的主build.gradle文件(请注意最后一行):

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.2'
        
        /*...*/

    }
}

apply from: "./libraries.gradle"

最后,在你的app module的build.gradle文件中像一个插件一样使用(注意依赖部分):

apply plugin: 'com.android.application'
/*...*/

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion

    /*...*/
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':marvel-core')

    testCompile rootProject.ext.testLibraries.junit
    testCompile rootProject.ext.testLibraries.robolectric

    androidTestCompile rootProject.ext.testLibraries.mockito
    compile rootProject.ext.libraries.appCompat
    compile rootProject.ext.libraries.androidSupport
    compile rootProject.ext.libraries.designSupport
    
    /*...*/
}

在你的core module 的build.gradle文件中使用:

apply plugin: 'java'
/*...*/

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile rootProject.ext.libraries.rxjava

    testCompile rootProject.ext.testLibraries.junit
    testCompile rootProject.ext.testLibraries.mockito

    compile rootProject.ext.libraries.retrofit
    
    /*...*/
}

core module内部发生了什么事情?

core module的文件结构core module的文件结构
  • base package: 包含了所有的基础接口,包括所有Intersctor PresentresViews的通用方法。
  • character package: 包含了应用程序的主要功能,即Marvel人物的搜索和缓存信息。
  • database package:在本应用中数据的缓存是通过OrmLite完成的,这里我不会做过多的解释,因为那样又脱离了主题,但是你可以阅读所有的源码!
  • domain package:包含了通过retrofit2和RaJava库连接网络api的源码。
  • util package: 本项目中所需要的所有有用的工具类,即:Constants 包含了所有的核心常量。HashGenerator用于Marvel 的api需要的哈希参数。SchedulerProvider是一个调度接口用于RxJavaRxAndroid的多线程(我将在本文的相关部分做详细介绍)。

参考SOLID依赖性反转原理“应该依赖抽象而不依赖于具体”或引用Novoda的这篇精彩文章“你不应该把一盏灯直接连接到你的房子”!,所有的两个模块之间的链接 (app&core)通过接口实现,并用Dagger连接。

你不应该把一盏灯直接连接到你的房子你不应该把一盏灯直接连接到你的房子

app module内部又发生了什么?

app module的文件结构app module的文件结构
  • activity package: 包含了3个作为Android应用程序UI支柱的 activity。
  • base package: 包含了两个activity和fragment的基本抽象类,抽象类中包括了用于注入的通用方法。
  • character package: 包含了该应用的主要功能,通过两个Search & Cachefragment实现。
  • daabase package: 包含了Android侧数据相关代码,这里使用了OrmLite
  • util package:本项目Android侧所需要的全部工具类,即:AppConstants 继承了 core中的Constants,包含了Application的常量定义。AppSchedulerProvider实现了core中的SchedulerProvider,并提供RxAndroid调度者。CustomBindingAdapter,帮助新的Android DataBinding插件使用Picasso库加载图片。GridSpacingItemDecoration帮助RecyclerView调整网格项目间距。(这只是一个简要的信息,在本文的相关部分都会做详细的介绍)。

好了,就这么多吧

请从github上clone一份代码并熟悉一下,因为从下一部分我将更多的介绍dagger以及它是怎样连接各个module和各层的不同对象。

我期待您的意见和帮助以便更好的改进这篇文章。

继续下一篇:MVP实践(Android)-Part2:Dagger使用

原文链接:Yet another MVP article — Part 1: Lets get to know the project

相关文章

  • [译]MVP实践(Android)-Part1:让我们了解这个项

    使用MVP, RxJava Dagger2, Retrofit2, Test 以及所有最新的现代方法、库来实现一个...

  • mvp+rxjava+rxbus+retrofit+butter

    mvp+rxjava+rxbus+retrofit+butterknife最新实践 了解android的人应该经常...

  • MVP的四级台阶

    本篇带你快速了解MVP,然后用一个实践操作过程让我们彻底掌握MVP的使用。文章略长,欢迎收藏! 上篇:你好MVP!...

  • MVP设计模式

    参考资料 【译】Android开发中的MVP架构 前言 让我们思考一下为什么在Android开发中如此迫切地需要一...

  • MVP 笔记

    MVP 与 MVC 简单介绍 实践 参考资料:Android MVP 详解(下)一步一步实现 Android 的M...

  • Android 架构优化~MVP 架构改造

    以前我写过一篇关于 MVP 架构的文章《Android架构—MVP架构在Android中的实践》。 随着业务的复杂...

  • Android MVP进阶

    上一篇:Android MVP初探 上一篇文章讲了最简单的一个Android MVP,这个只是用来让初学者了解MV...

  • Android Mvp实践

    Android Mvp实践 简介 本文是参考google官方发布的MVP架构demo以及前人对MVP实现方式的一些...

  • Android MVP

    Android MVP初探 Android MVP进阶 Android MVP高级 Android MVP扩展

  • Android中MVP模式的了解及实例

    Android中MVP的由来 在了解MVP之前我相信很多人已经学习过MVC这个框架了!MVP的诞生可以说是建立在M...

网友评论

    本文标题:[译]MVP实践(Android)-Part1:让我们了解这个项

    本文链接:https://www.haomeiwen.com/subject/yauomttx.html