【譯】Android技術棧,1#架構

本文是如何開發一款具有擴展性,維護性和測試性的Android應用專題的第一篇。本專題將會涉及到一些設計模式和類庫的使用方式,減少Android Developer日常開發的苦惱。

簡介:##

作為例子,我將使用以下這個項目,事實上就是一個簡單的電影概念目錄,可以稱之為視圖或者其它。

關于電影的信息可以從一個叫做Themoviedb的公開API中獲得,在這個版塊中Apiary可以找到不錯的文檔說明。

項目基于Model View Presenter 設計模式,也參考了一些Material Design 設計規范,比如轉場,(界面)結構,動畫,配色等等。

所有代碼都可以從Github中獲得,所以請隨意看,這里同樣有一個視頻用來展示App。

Paste_Image.png

架構:##

架構的設計基于Model View Presenter ,它是Model View Controller 設計模式的一個變種。

這種設計試圖抽象Presentation層的業務邏輯,在Android中這是很重要的,因為自身Framework 提倡這兩部分與數據層解耦合,一個明顯的例子就是AdaptersCursorLoaders

這種架構促使業務邏輯層和數據層不再隨著視圖層的變換而改變,這樣無論是Domain層的代碼復用還是例如Database或者REST API等數據源的改變,都變得簡單起來。

概述##

這種結構可以被劃分為三個主要層次:

  • presentation
  • model
  • domain

Presentation
Presentation層負責提供數據并展示圖形化界面。

Model
Model層將負責提供信息,這一層并不知道Presentation層和Domain,它能夠與數據庫,REST API或者其他可持久化數據等實現連接。

在這一層,也可以實現一些應用程序的實體類,用來代表,電影,種類等等。

Domain
Domain層完全獨立于Presentation層之外,這一層專門處理業務邏輯。

實現##

Domain層和Model層被放到兩個java module中,app module也就是Android應用代表Presentation層,這里還有另外一個common module,用來存放一些公共類庫和工具類們。

Domain module

Domain module存放著一些usecase和它們的實現類,它們是應用程序的業務邏輯。

這個module完全獨立于Android framework

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

一個usecase可以用來獲得不同類別電影的總評分,看一看哪個類別的電影最受歡迎,usecase需要獲取信息然后做出計算,所有這些信息都由Model層提供。

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

Model module##

model module負責處理信息,查詢,保存,刪除等等,我只處理了從API獲取電影詳情的操作。

也實現了一些實體類,比如TvMovie,用來表現一部電影。

它目前只依賴common module,通過這個類庫處理API請求,在這個例子中我使用Square出品的Retrofit,我將在接下來的博客中介紹Retrofit。

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

Presentation module##

就是Android應用自身,包括resources, assets, 邏輯等等。

它與執行usecaseDomain進行交互,比如可以用來獲取某一時段的電影列表,或者從某部電影中獲取特殊的數據。

這個模塊只包含PresenterView

每一個ActivityFragmentDialog都實現MVPView接口,它指定了一些在View上進行顯示,隱藏,顯示信息等操作。

比如,PopularMoviesView通過指定一些操作展示當前電影列表,然后MoviesActivity實現它。

public interface PopularMoviesView extends MVPView {

    void showMovies (List<TvMovie> movieList);

    void showLoading ();

    void hideLoading ();

    void showError (String error);

    void hideError ();
}

MVP設計模式就是讓View變得盡可能的簡單,由Presenter決定它們的行為。(譯者注:View層應體現KISS原則,感興趣的同學可以了解一下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);
    }

    ...
}

這個usecase通過Presenter調用,并且Presenter接收相應結果,然后處理View上的表現。

通信##

對于這個項目,我選擇了Message Bus(譯者注:消息總線)系統,這個系統對于廣播事件,或者在兩個組件之間建立通信是非常有用的,尤其特別適用于后者。

基本上,通過Bus發送事件,對事件感興趣的類,需要訂閱Bus,才能消費那個事件。

適用這個系統可以降低模塊間的耦合度。

為了實現這個系統總線,我使用Square出品的Otto類庫。

我定義了兩個Bus,一個用來使usecase和REST API進行通信,另一個用來發送事件至Presentation
層。

REST_BUS使用任意線程處理事件,UI_BUS使用默認線程發送事件,這個線程就是主線程。

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;
    }
}

這個類通過common module管理。因為所有的模塊都需要訪問它,從而與Bus進行交互。

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

最后,想象一下這個場景,當用戶打開應用,顯示最受歡迎的電影。

View調用onCreate()方法時,Presenter訂閱UI_BUS接收事件。當onStop()方法被調用的時候Presenter取消訂閱。Presenter運行GetMoviesUseCase這個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需要實現一個方法,這個方法所接受參數的數據類型必須與Bus發送的事件的數據類型一致,兵器必須使用注解:@Subscribe

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

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

資源:##

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,811評論 25 708
  • 作者:李旺成### 時間:2016年4月3日### 上篇 5. 最佳實踐# 好了終于要點講自己的東西了,有點小激動...
    diygreen閱讀 30,296評論 54 493
  • 5. 最佳實踐 好了終于要點講自己的東西了,有點小激動。下面這些僅表示個人觀點,非一定之規,各位看官按需取用,有說...
    SnowDragonYY閱讀 2,401評論 4 36