[譯]MVP實踐(Android)-Part1:讓我們了解這個項目

使用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ù)評估的一部分.

Marvel Android 應(yīng)用程序截屏
Marvel Android 應(yīng)用程序截屏

這個應(yīng)用需要搜索人物,展現(xiàn)搜索結(jié)果并緩存上一次搜索瘫怜。

這個項目的實現(xiàn)使用了MVP术徊,包含了一些現(xiàn)代的Android開發(fā)理念和第三方庫,這些都可以改變你的職業(yè)生涯鲸湃!

在接下來的系列文章的不同部分我將竭盡所能去解釋一切赠涮,即:Dagger子寓,Retrofit,RxJava和Tests笋除。

這個項目使用了Circleci.comTravis-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:

app module包含MVP中的Android View層掉瞳,其余兩層(ModelPresentation)都放在了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
    java sample module和Android Application一樣使用core的運行結(jié)果
    java sample module和Android Application一樣使用core的運行結(jié)果

模塊名稱清理:

為了使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ā)生了什么事情肴掷?

core module的文件結(jié)構(gòu)
core module的文件結(jié)構(gòu)
  • base package: 包含了所有的基礎(chǔ)接口,包括所有Intersctor PresentresViews的通用方法背传。
  • 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)度接口用于RxJavaRxAndroid的多線程(我將在本文的相關(guān)部分做詳細(xì)介紹)。

參考SOLID依賴性反轉(zhuǎn)原理韵吨,“應(yīng)該依賴抽象而不依賴于具體”或引用Novoda的這篇精彩文章“你不應(yīng)該把一盞燈直接連接到你的房子”匿垄!,所有的兩個模塊之間的鏈接 (app&core)通過接口實現(xiàn)归粉,并用Dagger連接椿疗。

你不應(yīng)該把一盞燈直接連接到你的房子
你不應(yīng)該把一盞燈直接連接到你的房子

app module內(nèi)部又發(fā)生了什么?

app module的文件結(jié)構(gòu)
app module的文件結(jié)構(gòu)
  • 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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拘哨,隨后出現(xiàn)的幾起案子谋梭,更是在濱河造成了極大的恐慌,老刑警劉巖倦青,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓮床,死亡現(xiàn)場離奇詭異,居然都是意外死亡姨夹,警方通過查閱死者的電腦和手機纤垂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門矾策,熙熙樓的掌柜王于貴愁眉苦臉地迎上來磷账,“玉大人,你說我怎么就攤上這事贾虽√釉悖” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵蓬豁,是天一觀的道長绰咽。 經(jīng)常有香客問我,道長地粪,這世上最難降的妖魔是什么取募? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮蟆技,結(jié)果婚禮上玩敏,老公的妹妹穿的比我還像新娘。我一直安慰自己质礼,他們只是感情好旺聚,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眶蕉,像睡著了一般砰粹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上造挽,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天碱璃,我揣著相機與錄音弄痹,去河邊找鬼。 笑死嵌器,一個胖子當(dāng)著我的面吹牛界酒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嘴秸,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼毁欣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了岳掐?” 一聲冷哼從身側(cè)響起凭疮,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎串述,沒想到半個月后执解,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡纲酗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年衰腌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片觅赊。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡右蕊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吮螺,到底是詐尸還是另有隱情饶囚,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布鸠补,位于F島的核電站萝风,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏紫岩。R本人自食惡果不足惜规惰,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泉蝌。 院中可真熱鬧歇万,春花似錦、人聲如沸梨与。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粥鞋。三九已至缘挽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背壕曼。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工苏研, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腮郊。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓摹蘑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親轧飞。 傳聞我的和親對象是個殘疾皇子衅鹿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容