【譯】Android技術(shù)棧盟步,1#架構(gòu)

本文是如何開發(fā)一款具有擴(kuò)展性,維護(hù)性和測(cè)試性的Android應(yīng)用專題的第一篇。本專題將會(huì)涉及到一些設(shè)計(jì)模式和類庫的使用方式菇存,減少Android Developer日常開發(fā)的苦惱彰居。

簡介:##

作為例子,我將使用以下這個(gè)項(xiàng)目撰筷,事實(shí)上就是一個(gè)簡單的電影概念目錄陈惰,可以稱之為視圖或者其它。

關(guān)于電影的信息可以從一個(gè)叫做Themoviedb的公開API中獲得毕籽,在這個(gè)版塊中Apiary可以找到不錯(cuò)的文檔說明抬闯。

項(xiàng)目基于Model View Presenter 設(shè)計(jì)模式,也參考了一些Material Design 設(shè)計(jì)規(guī)范关筒,比如轉(zhuǎn)場(chǎng)溶握,(界面)結(jié)構(gòu),動(dòng)畫蒸播,配色等等睡榆。

所有代碼都可以從Github中獲得,所以請(qǐng)隨意看袍榆,這里同樣有一個(gè)視頻用來展示App胀屿。

Paste_Image.png

架構(gòu):##

架構(gòu)的設(shè)計(jì)基于Model View Presenter ,它是Model View Controller 設(shè)計(jì)模式的一個(gè)變種包雀。

這種設(shè)計(jì)試圖抽象Presentation層的業(yè)務(wù)邏輯宿崭,在Android中這是很重要的,因?yàn)樽陨?em>Framework 提倡這兩部分與數(shù)據(jù)層解耦合才写,一個(gè)明顯的例子就是AdaptersCursorLoaders葡兑。

這種架構(gòu)促使業(yè)務(wù)邏輯層和數(shù)據(jù)層不再隨著視圖層的變換而改變,這樣無論是Domain層的代碼復(fù)用還是例如Database或者REST API等數(shù)據(jù)源的改變赞草,都變得簡單起來讹堤。

概述##

這種結(jié)構(gòu)可以被劃分為三個(gè)主要層次:

  • presentation
  • model
  • domain

Presentation
Presentation層負(fù)責(zé)提供數(shù)據(jù)并展示圖形化界面。

Model
Model層將負(fù)責(zé)提供信息厨疙,這一層并不知道Presentation層和Domain洲守,它能夠與數(shù)據(jù)庫,REST API或者其他可持久化數(shù)據(jù)等實(shí)現(xiàn)連接轰异。

在這一層岖沛,也可以實(shí)現(xiàn)一些應(yīng)用程序的實(shí)體類暑始,用來代表搭独,電影,種類等等廊镜。

Domain
Domain層完全獨(dú)立于Presentation層之外牙肝,這一層專門處理業(yè)務(wù)邏輯。

實(shí)現(xiàn)##

Domain層和Model層被放到兩個(gè)java module中,app module也就是Android應(yīng)用代表Presentation層配椭,這里還有另外一個(gè)common module虫溜,用來存放一些公共類庫和工具類們。

Domain module

Domain module存放著一些usecase和它們的實(shí)現(xiàn)類股缸,它們是應(yīng)用程序的業(yè)務(wù)邏輯衡楞。

這個(gè)module完全獨(dú)立于Android framework

依賴它的模塊有model module和common module敦姻。

一個(gè)usecase可以用來獲得不同類別電影的總評(píng)分瘾境,看一看哪個(gè)類別的電影最受歡迎,usecase需要獲取信息然后做出計(jì)算镰惦,所有這些信息都由Model層提供迷守。

dependencies {
    compile project (':common')
    compile project (':model')
}

Model module##

model module負(fù)責(zé)處理信息,查詢旺入,保存兑凿,刪除等等,我只處理了從API獲取電影詳情的操作茵瘾。

也實(shí)現(xiàn)了一些實(shí)體類礼华,比如TvMovie,用來表現(xiàn)一部電影拗秘。

它目前只依賴common module卓嫂,通過這個(gè)類庫處理API請(qǐng)求,在這個(gè)例子中我使用Square出品的Retrofit聘殖,我將在接下來的博客中介紹Retrofit晨雳。

dependencies {
    compile project(':common')
    compile 'com.squareup.retrofit:retrofit:1.9.0'
}

Presentation module##

就是Android應(yīng)用自身,包括resources, assets, 邏輯等等奸腺。

它與執(zhí)行usecaseDomain進(jìn)行交互餐禁,比如可以用來獲取某一時(shí)段的電影列表,或者從某部電影中獲取特殊的數(shù)據(jù)突照。

這個(gè)模塊只包含PresenterView帮非。

每一個(gè)ActivityFragment讹蘑,Dialog都實(shí)現(xiàn)MVPView接口末盔,它指定了一些在View上進(jìn)行顯示,隱藏座慰,顯示信息等操作陨舱。

比如,PopularMoviesView通過指定一些操作展示當(dāng)前電影列表版仔,然后MoviesActivity實(shí)現(xiàn)它游盲。

public interface PopularMoviesView extends MVPView {

    void showMovies (List<TvMovie> movieList);

    void showLoading ();

    void hideLoading ();

    void showError (String error);

    void hideError ();
}

MVP設(shè)計(jì)模式就是讓View變得盡可能的簡單误墓,由Presenter決定它們的行為。(譯者注:View層應(yīng)體現(xiàn)KISS原則益缎,感興趣的同學(xué)可以了解一下Keep it simple stupid

public class MoviesActivity extends ActionBarActivity implements
    PopularMoviesView, ... {

    ...
    private PopularShowsPresenter popularShowsPresenter;
    private RecyclerView popularMoviesRecycler;
    private ProgressBar loadingProgressBar;
    private MoviesAdapter moviesAdapter;
    private TextView errorTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        ...
        popularShowsPresenter = new PopularShowsPresenterImpl(this);
        popularShowsPresenter.onCreate();
    }

    @Override
    protected void onStop() {

        super.onStop();
        popularShowsPresenter.onStop();
    }

    @Override
    public Context getContext() {

        return this;
    }

    @Override
    public void showMovies(List<TvMovie> movieList) {

        moviesAdapter = new MoviesAdapter(movieList);
        popularMoviesRecycler.setAdapter(moviesAdapter);
    }

    @Override
    public void showLoading() {

        loadingProgressBar.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {

        loadingProgressBar.setVisibility(View.GONE);
    }

    @Override
    public void showError(String error) {

        errorTextView.setVisibility(View.VISIBLE);
        errorTextView.setText(error);
    }

    @Override
    public void hideError() {

        errorTextView.setVisibility(View.GONE);
    }

    ...
}

這個(gè)usecase通過Presenter調(diào)用谜慌,并且Presenter接收相應(yīng)結(jié)果,然后處理View上的表現(xiàn)莺奔。

通信##

對(duì)于這個(gè)項(xiàng)目欣范,我選擇了Message Bus(譯者注:消息總線)系統(tǒng),這個(gè)系統(tǒng)對(duì)于廣播事件令哟,或者在兩個(gè)組件之間建立通信是非常有用的熙卡,尤其特別適用于后者。

基本上励饵,通過Bus發(fā)送事件驳癌,對(duì)事件感興趣的類,需要訂閱Bus役听,才能消費(fèi)那個(gè)事件颓鲜。

適用這個(gè)系統(tǒng)可以降低模塊間的耦合度。

為了實(shí)現(xiàn)這個(gè)系統(tǒng)總線典予,我使用Square出品的Otto類庫甜滨。

我定義了兩個(gè)Bus,一個(gè)用來使usecase和REST API進(jìn)行通信瘤袖,另一個(gè)用來發(fā)送事件至Presentation
層衣摩。

REST_BUS使用任意線程處理事件,UI_BUS使用默認(rèn)線程發(fā)送事件捂敌,這個(gè)線程就是主線程艾扮。

public class BusProvider {

    private static final Bus REST_BUS = new Bus(ThreadEnforcer.ANY);
    private static final Bus UI_BUS = new Bus();

    private BusProvider() {};

    public static Bus getRestBusInstance() {

        return REST_BUS;
    }

    public static Bus getUIBusInstance () {

        return UI_BUS;
    }
}

這個(gè)類通過common module管理。因?yàn)樗械哪K都需要訪問它占婉,從而與Bus進(jìn)行交互泡嘴。

dependencies {
    compile 'com.squareup:otto:1.3.5'
}

最后,想象一下這個(gè)場(chǎng)景逆济,當(dāng)用戶打開應(yīng)用酌予,顯示最受歡迎的電影。

當(dāng)View調(diào)用onCreate()方法時(shí)奖慌,Presenter訂閱UI_BUS接收事件抛虫。當(dāng)onStop()方法被調(diào)用的時(shí)候Presenter取消訂閱。Presenter運(yùn)行GetMoviesUseCase這個(gè)usecase简僧。

@Override
    public void onCreate() {

        BusProvider.getUIBusInstance().register(this);

        Usecase getPopularShows = new GetMoviesUsecaseController(GetMoviesUsecase.TV_MOVIES);
        getPopularShows.execute();
    }

    ...

    @Override
    public void onStop() {

        BusProvider.getUIBusInstance().unregister(this);
    }
}

為了接收事件建椰,Presenter需要實(shí)現(xiàn)一個(gè)方法,這個(gè)方法所接受參數(shù)的數(shù)據(jù)類型必須與Bus發(fā)送的事件的數(shù)據(jù)類型一致涎劈,兵器必須使用注解:@Subscribe广凸。

@Subscribe
    @Override
    public void onPopularMoviesReceived(PopularMoviesApiResponse popularMovies) {

        popularMoviesView.hideLoading();
        popularMoviesView.showMovies(popularMovies.getResults());
    }

資源:##

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末阅茶,一起剝皮案震驚了整個(gè)濱河市蛛枚,隨后出現(xiàn)的幾起案子谅海,更是在濱河造成了極大的恐慌,老刑警劉巖蹦浦,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扭吁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盲镶,警方通過查閱死者的電腦和手機(jī)侥袜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溉贿,“玉大人枫吧,你說我怎么就攤上這事∮钌” “怎么了九杂?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宣蠕。 經(jīng)常有香客問我例隆,道長,這世上最難降的妖魔是什么抢蚀? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任镀层,我火速辦了婚禮,結(jié)果婚禮上皿曲,老公的妹妹穿的比我還像新娘唱逢。我一直安慰自己,他們只是感情好屋休,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布惶我。 她就那樣靜靜地躺著,像睡著了一般博投。 火紅的嫁衣襯著肌膚如雪绸贡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天毅哗,我揣著相機(jī)與錄音听怕,去河邊找鬼。 笑死虑绵,一個(gè)胖子當(dāng)著我的面吹牛尿瞭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播翅睛,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼声搁,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼黑竞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起疏旨,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤很魂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后檐涝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遏匆,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年谁榜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幅聘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡窃植,死狀恐怖帝蒿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情巷怜,我是刑警寧澤葛超,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站丛版,受9級(jí)特大地震影響巩掺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜页畦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一胖替、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧豫缨,春花似錦独令、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至舍败,卻和暖如春招狸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背邻薯。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工裙戏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厕诡。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓累榜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親灵嫌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子壹罚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,162評(píng)論 25 707
  • 作者:李旺成### 時(shí)間:2016年4月3日### 上篇 5. 最佳實(shí)踐# 好了終于要點(diǎn)講自己的東西了葛作,有點(diǎn)小激動(dòng)...
    diygreen閱讀 30,262評(píng)論 54 493
  • 5. 最佳實(shí)踐 好了終于要點(diǎn)講自己的東西了,有點(diǎn)小激動(dòng)猖凛。下面這些僅表示個(gè)人觀點(diǎn)赂蠢,非一定之規(guī),各位看官按需取用形病,有說...
    SnowDragonYY閱讀 2,394評(píng)論 4 36