使用MVP, RxJava Dagger2, Retrofit2, Test 以及所有最新的現(xiàn)代方法、庫來實現(xiàn)一個Android實例應(yīng)用猛铅。
我在StackOverFlow上發(fā)現(xiàn)一些有關(guān)MVP使用的問題沒有回答戴差,這促使我產(chǎn)生強烈的興趣來寫下這一系列文章建钥,并提供一個樣例工程(即我自己的實踐經(jīng)驗)哮笆。
我從去年開始熟悉MVP,一個tuxedo開發(fā)冠绢,并開始尋找樣例和教程。我花了很長時間來處理常潮、連接這個未知大謎題的不同部分唐全。我可以推薦的最有用的網(wǎng)站是caster.io,它總是充滿了新的Android視頻教程。
解釋*MVP本身看起來有些古怪蕊玷!因為已經(jīng)有許多文章解釋它是怎么工作的邮利,以及它是怎么分層的等等,但是附帶足夠的評論(注釋)來幫助新手了解這個方法的樣例少之又少垃帅。
變化是怎么開始的延届。。贸诚。
這一切都從SOLID(面向?qū)ο蟮脑O(shè)計原則)開始方庭,感謝親愛的Robert C. Martin厕吉。
從維基文章的內(nèi)容我們知道SOLID代表:
- S (SRP): 單一功能原則(認(rèn)為對象應(yīng)該僅具有一種單一功能的概念)
- O (OCP): 開閉原則(認(rèn)為“軟件體應(yīng)該是對于擴展開放的,但是對于修改封閉的”的概念)
- L (LSP): 里氏替換原則(認(rèn)為“程序中的對象應(yīng)該是可以在不改變程序正確性的前提下被它的子類所替換的”的概念)
- I (ISP): 接口隔離原則(認(rèn)為“多個特定客戶端接口要好于一個寬泛用途的接口”的概念)
- D (DIP): 依賴反轉(zhuǎn)原則(認(rèn)為一個方法應(yīng)該遵從“依賴于抽象而不是一個實例”的概念械念。依賴注入是該原則的一種實現(xiàn)方式头朱。)
MVP在一定程度上嘗試遵循這5條原則的全部。我將竭盡全力在示例項目中逐一定位這些原則龄减,以使它們更加透明项钮。
通過這篇完美的MVP文章,MVP代表:
Model 就是將在View(用戶界面)中展現(xiàn)的數(shù)據(jù)希停。
View就是用于展示數(shù)據(jù)(Model)的的界面烁巫,同時將用戶的命令(events)傳遞給Presenter,由Presenter對數(shù)據(jù)進(jìn)行操作宠能。view通常持有一個Presenter的引用
Presenter就是一個“中間人”(MVC模式中Controller扮演的角色)亚隙,它同時持有vew和model的引用。
Model违崇?0⑵!羞延!
請注意“Model”這個詞語有誤導(dǎo)性
它應(yīng)該是檢索或者操作Model的業(yè)務(wù)邏輯渣淳。
比如:如果你有一個數(shù)據(jù)庫,在一個表中存儲了User
數(shù)據(jù)肴楷,你的view想要展示一個用戶列表水由,那么Presenter應(yīng)該持有一個數(shù)據(jù)庫業(yè)務(wù)邏輯(比如一個DAO)的引用,通過這個業(yè)務(wù)邏輯Presenter可以查詢到一個用戶列表赛蔫。
你能夠?qū)VP再多做一點解釋嗎砂客?
不不不不不不!呵恢!通過關(guān)鍵字Google(不能翻墻的就Bing)鞠值,你會發(fā)現(xiàn)所有關(guān)于這個新方法的理論。(或者至少閱讀一下這篇文章)渗钉。
這個樣例項目是關(guān)于什么的彤恶?
這個應(yīng)用是Marvel的人物搜索程序,Marvel.com的一個簡單Android客戶端鳄橘。此應(yīng)用程序由我創(chuàng)建声离,作為smava GmbH技術(shù)團(tuán)隊的技術(shù)評估的一部分.
這個應(yīng)用需要搜索人物,展現(xiàn)搜索結(jié)果并緩存上一次搜索瘫怜。
這個項目的實現(xiàn)使用了MVP术徊,包含了一些現(xiàn)代的Android開發(fā)理念和第三方庫,這些都可以改變你的職業(yè)生涯鲸湃!
在接下來的系列文章的不同部分我將竭盡所能去解釋一切赠涮,即:Dagger子寓,Retrofit,RxJava和Tests笋除。
這個項目使用了Circleci.com和Travis-ci.org來做持續(xù)集成(CI)斜友,使用了Codecov.io來做代碼覆蓋測試,還使用了google的Firebase垃它,這一部分你可以自己學(xué)習(xí)鲜屏,因為一定程度上這已經(jīng)脫離了本文的主題。
在開始之前嗤瞎,你可以先閱讀該工程的README和任務(wù)列表文件來多做一些了解墙歪。
Okey听系,告訴我你都了解到了什么:
讓我們先來看一下項目的結(jié)構(gòu):
我個人喜歡整潔的代碼贝奇,所以我喜歡將項目分成有意義的模塊,以便我和整個團(tuán)隊保持更清晰的任務(wù)靠胜。
Modules:
整個工程包含兩個main module 和一個java console sample module:
- 1.app(marvel-app): Anddroid Application module
- 2.core-lib(marvel-core):Core Library module
- 3.console(marvel-console):java console sample module
app module包含MVP中的Android View層掉瞳,其余兩層(Model和Presentation)都放在了core中,core是一個純粹的java包浪漠,編譯后可以生成一個jar庫陕习。
把代碼按照這種方式分成幾個module有什么好處?址愿!
- 首先该镣,將Android Application module 分開,是為了提醒你不要傳遞Context或者任何Android相關(guān)的對象給Presenter或者Model响谓!所以請現(xiàn)在就停止那樣做K鸷稀!娘纷!
- 其次嫁审,你能夠確保你的core部分是非常完整,你甚至可以把它和另一個UI搭配使用(即:java Console sample赖晶,Web控件律适,甚至在未來的某一天當(dāng) 一個iOS使用java!6舨濉)
- 最后捂贿,我和我們的團(tuán)隊真的喜歡以這樣的方式開發(fā)應(yīng)用!并且單獨分離core部分使整個團(tuán)隊都受益胳嘲,我們甚至將core放到git的一個submodule中在不同的項目中使用厂僧,大家使用同一個core而使用不同的UI。
模塊名稱清理:
為了使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看起來像這樣:
當(dāng)然這也會導(dǎo)致相關(guān)APK文件的名稱發(fā)生變化吁系。
怎樣避免不同的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內(nèi)部發(fā)生了什么事情肴掷?
- base package: 包含了所有的基礎(chǔ)接口,包括所有Intersctor Presentres和Views的通用方法背传。
- character package: 包含了應(yīng)用程序的主要功能呆瞻,即Marvel人物的搜索和緩存信息。
- database package:在本應(yīng)用中數(shù)據(jù)的緩存是通過OrmLite完成的径玖,這里我不會做過多的解釋痴脾,因為那樣又脫離了主題,但是你可以閱讀所有的源碼梳星!
- domain package:包含了通過retrofit2和RaJava庫連接網(wǎng)絡(luò)api的源碼赞赖。
- util package: 本項目中所需要的所有有用的工具類,即:Constants 包含了所有的核心常量冤灾。HashGenerator用于Marvel 的api需要的哈希參數(shù)前域。SchedulerProvider是一個調(diào)度接口用于RxJava和RxAndroid的多線程(我將在本文的相關(guān)部分做詳細(xì)介紹)。
參考SOLID的依賴性反轉(zhuǎn)原理韵吨,“應(yīng)該依賴抽象而不依賴于具體”或引用Novoda的這篇精彩文章“你不應(yīng)該把一盞燈直接連接到你的房子”匿垄!,所有的兩個模塊之間的鏈接 (app&core)通過接口實現(xiàn)归粉,并用Dagger連接椿疗。
app module內(nèi)部又發(fā)生了什么?
- activity package: 包含了3個作為Android應(yīng)用程序UI支柱的 activity盏浇。
- base package: 包含了兩個activity和fragment的基本抽象類变丧,抽象類中包括了用于注入的通用方法。
- character package: 包含了該應(yīng)用的主要功能绢掰,通過兩個Search & Cachefragment實現(xiàn)痒蓬。
- daabase package: 包含了Android側(cè)數(shù)據(jù)相關(guān)代碼,這里使用了OrmLite滴劲!
- util package:本項目Android側(cè)所需要的全部工具類攻晒,即:AppConstants 繼承了 core中的Constants,包含了Application的常量定義班挖。AppSchedulerProvider實現(xiàn)了core中的SchedulerProvider鲁捏,并提供RxAndroid調(diào)度者。CustomBindingAdapter萧芙,幫助新的Android DataBinding插件使用Picasso庫加載圖片给梅。GridSpacingItemDecoration幫助RecyclerView調(diào)整網(wǎng)格項目間距假丧。(這只是一個簡要的信息,在本文的相關(guān)部分都會做詳細(xì)的介紹)动羽。
好了包帚,就這么多吧
請從github上clone一份代碼并熟悉一下,因為從下一部分我將更多的介紹dagger以及它是怎樣連接各個module和各層的不同對象运吓。
我期待您的意見和幫助以便更好的改進(jìn)這篇文章渴邦。
繼續(xù)下一篇:MVP實踐(Android)-Part2:Dagger使用
原文鏈接:Yet another MVP article?—?Part 1: Lets get to know the project