頻道欄目
首頁 > 資訊 > Java > 正文

MVP架構-官方MVP項目和MVP-RxJava項目架構對比分析解讀

16-05-28        來源:[db:作者]  
收藏   我要投稿

介紹

MVP這個架構一直是Android開發社區討論的焦點,每個人都有自己的分析理解眾說紛紜。直到GitHub上Google官方發布用MVP架構搭建的項目。感覺是時候分析了。

MVP架構簡介

MVP架構簡介

  對于一個應用而言我們需要對它抽象出各個層面,而在MVP架構中它將UI界面和數據進行隔離,所以我們的應用也就分為三個層次。

View:對于View層也是視圖層,在View層中只負責對數據的展示,提供友好的界面與用戶進行交互。在Android開發中通常將Activity或者Fragment作為View層。 Model:對于Model層也是數據層。它區別于MVC架構中的Model,在這里不僅僅只是數據模型。在MVP架構中Model它負責對數據的存取操作,例如對數據庫的讀寫,網絡的數據的請求等。 Presenter:對于Presenter層他是連接View層與Model層的橋梁并對業務邏輯進行處理。在MVP架構中Model與View無法直接進行交互。所以在Presenter層它會從Model層獲得所需要的數據,進行一些適當的處理后交由View層進行顯示。這樣通過Presenter將View與Model進行隔離,使得View和Model之間不存在耦合,同時也將業務邏輯從View中抽離。更好的使得單元測試得以實現。

下圖很好的展示了MVP各個組件間的關系。
MVP架構

從圖中可以看出,View層不再和Model層關聯,他們之間通過Presenter層關聯,這里就出明顯的感覺出P層的任務會比較重,邏輯會相對其他層復雜,同時也是MVP中最關鍵的層。

  在MVP架構中將這三層分別抽象到各自的接口當中。通過接口將層次之間進行隔離,而Presenter對View和Model的相互依賴也是依賴于各自的接口。這點符合了接口隔離原則,也正是面向接口編程。在Presenter層中包含了一個View接口,并且依賴于Model接口,從而將Model層與View層聯系在一起。而對于View層會持有一個Presenter成員變量并且只保留對Presenter接口的調用,具體業務邏輯全部交由Presenter接口實現類中處理。

面向接口編程:每個層次不是直接向其上層提供服務(即不是直接實例化在上層中),而是通過定義一組接口,僅向上層暴露其接口功能,上層對于下層僅僅是接口依賴,而不依賴具體類。

代碼分析

項目說明

本文主要分析:
基礎MVP架構todo-mvp和響應式MVP架構todo-mvp-rxjava

目前Google在GitHub上面公布7個項目:
7個項目
每個項目都是便簽App,都采用MVP架構但是每個項目都會有些不同。目前網絡上大多數都是分析第一個todo-mvp,作為其他項目的基礎,對比分析todo-mvp-rxjava找出兩者的差異和相同點,是本文的主要內容。
為了簡潔,約定有:

mvp:指代todo-mvp項目
mvp-rxjava:指代todo-mvp-rxjava項目
響應式MVP:作為mvp-rxjava的中文描述

基礎類分析

本章主要分析mvp和mvp-rxjava兩個項目基礎類的差異,以查看任務模塊taskdetail包中的4個類和2個基礎父類作為分析點。分析同樣的功能MVP和響應式MVP的差異。

基礎類BaseView

BaseView作為所有的View層的父類,功能是實現P層的依賴注入。mvp和mvp-rxjava都采用一樣邏輯。
代碼如下:

public interface BaseView {
    void setPresenter(T presenter);
}

View層的具體實現類xxFragment實現接口,就能夠得到和它關聯的P層的注入。


    //內部變量 從setPresenter方法注入
    private AddEditTaskContract.Presenter mPresenter;
@Override

    public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
        mPresenter = checkNotNull(presenter);
    }

而注入的時機肯定就是在P層已經得到實例化之后,所以我們在對應的P層構造方法中可以看到這樣的代碼:

public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
            @NonNull AddEditTaskContract.View addTaskView) {
        mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository);
        mAddTaskView = checkNotNull(addTaskView);
        //向V層注入自己 自己就是對應的P層實例
        mAddTaskView.setPresenter(this);
    }

基礎類BasePresenter

BasePresenter作為所有P層的父類,主要實現V層和P層生命周期同步。
這里響應式MVP明顯的和MVP不同,
MVP的P層父類代碼:

public interface BasePresenter {
    void start();
}

在View層的具體實現類xxFragment的生命周期onResume中啟動通過注入得到的P層實例開始P層的工作。

@Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

響應式MVP的P層父類:

public interface BasePresenter {
    void subscribe();//開啟訂閱
    void unsubscribe();//結束訂閱
}

在View層的具體實現類xxFragment中就需要做兩步操作,同步P層和V層的生命周期

 @Override
    public void onResume() {
        super.onResume();
        mPresenter.subscribe();//V層獲得焦點 開始訂閱
    }

    @Override
    public void onPause() {
        super.onPause();
        mPresenter.unsubscribe();//V層失去焦點 取消訂閱
    }

這么寫的原因是,RxJava的特點決定的。

響應式編碼中數據Model是可以觀察到的數據流,已經準備好數據,隨時等待發射。

我們需要做的就是,在需要數據的點開始訂閱數據,接收數據。不再需要數據就取消訂閱數據,讓數據不再發送。這在使用RxJava是很重要的操作。

在一般的MVC的項目中,如果使用Fragment做為數據的主要展示類,就直接定義內部變量CompositeSubscription對象訂閱者集合,在onDestroy生命周期回調中統一操作,取消正在等待的訂閱,因為當前View已經不可見了。

一般的代碼是這樣的:

//父類統一提供管理方法 
public abstract class BaseFragment extends Fragment {
         private CompositeSubscription mCompositeSubscription; //這個類的內部是由Set 維護訂閱者

    //提供給子類的方法
    public void addSubscription(Subscription s) {
        if (this.mCompositeSubscription == null) {
            this.mCompositeSubscription = new CompositeSubscription();
        }

        this.mCompositeSubscription.add(s);
    }

     @Override
    public void onDestroy() {
        super.onDestroy();
          //在銷毀時統一取消
        if (this.mCompositeSubscription != null) {
            this.mCompositeSubscription.unsubscribe();
        }

    }

}

而在響應式MVP架構中P層作為控制邏輯的主要實現,就需要和V層的生命周期同步,把這段代碼搬到P層中。

在我的另一篇博文RxAndroid和Retrofit使用記錄-有關網絡調用和生命周期中有具體代碼和分析。

Contract契約類

不同于其他的MVP項目,官方的MVP架構中都定義有xxContract契約類,把P層和V層的接口統一寫在契約類中,能夠更清晰的看到在Presenter層和View層中有哪些功能,方便我們以后的維護。這是其他MVP架構沒有的類。mvp和mvp-rxjava都采用一樣邏輯。

每個契約類都定義了P層的數據操作方法和V層控制UI的方法,
并能夠通過參數傳入需要的值。
每個模塊的契約類都是需要我們根據具體的需求進行抽象,定義方法和參數的。
下面的代碼是,添加任務模塊的契約類,通過方法名可以大概了解V層和P層需要具體是實現的邏輯功能。

public interface AddEditTaskContract {

    interface View extends BaseView {

        void showEmptyTaskError();

        void showTasksList();

        void setTitle(String title);

        void setDescription(String description);

        boolean isActive();
    }

    interface Presenter extends BasePresenter {

        void createTask(String title, String description);

        void updateTask( String title, String description);

        void populateTask();
    }
}

Activity綁定類

在官方的MVP架構中Activity類不再負責任何的View層功能。mvp和mvp-rxjava都采用一樣邏輯。

普通的View控件都包含在V層的Fragment中。 甚至在布局文件中和Fragment同級的FloatingActionButton控件也由Fragment控制 同樣布局文件中和Fragment同級的Menu菜單視圖,也由Fragment控制。

這樣使得Fragment才變成真正的View層。而使得Activity符合面向對象設計原則的SRP(單一職責原創,Single Responsibility Principle)。

而Activity最重要的功能就是P層對M/V層的綁定。

 // Create the presenter
        //P層的構造 依賴注入
        new TaskDetailPresenter(
                taskId,//P層需要的關鍵數據 任務id 
                Injection.provideTasksRepository(getApplicationContext()),//Model層的注入
                taskDetailFragment//View層
        );

看到上面的代碼,感覺下圖非常符合
MV層注入P層

View層

說了這么多終于到MVP的View層了,官方MVP架構中Fragment作為View層實現類。
分層之后Fragment的代碼就簡潔多了。
implements實現相關接口方法,做視圖操作,分發給P層做處理。得到P層回調展示數據。
下面的代碼,作為示例,它實現同級視圖控制。

上文提到: 甚至在布局文件中和Fragment同級的FloatingActionButton控件也由Fragment控制

//得到和自己同級的View 
// Set up floating action button
        FloatingActionButton fab =
                (FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task);

        //響應點擊事件 分發給P層
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.editTask();
            }
        });

同樣Menu也重寫了onCreateOptionsMenu()方法和onOptionsItemSelected()方法控制菜單視圖。

Presenter層

每個包中的xxPresenter類是某個具體的P層控制類。因為Model數據層有負責了數據的讀取功能,P層的代碼邏輯也很簡單。
從M層得到數據,做邏輯判斷分發給V層;蛘呤窍騇層發送某個讀寫操作,回調操作是否成功的結果。
而它的數據來源就是剛才Activity類里面的依賴注入進來的。

//通過 構造方法得到的依賴注入
private final TasksRepository mTasksRepository;

值得一提的是,響應式MVP和MVP在獲取數據方面會有不同。

MVP的P層獲取數據邏輯

數據層讀寫操作會發生在子線程,要在Model層某個具體的數據發送類,做線程處理。P層的回調才能將數據分發給V層顯示。
MVP中P層通過接口回調得到數據,如下代碼:

mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
            @Override
            public void onTaskLoaded(Task task) {
                // The view may not be able to handle UI updates anymore
                if (!mTaskDetailView.isActive()) {
                    return;
                }
                mTaskDetailView.setLoadingIndicator(false);
                if (null == task) {
                    mTaskDetailView.showMissingTask();
                } else {
                    showTask(task);
                }
            }

響應式MVP的P層數據獲取邏輯

如果你用過RxJava,下面的代碼,相信就不需要我說什么了?梢蕴^下面的說明。

   mTaskDetailView.setLoadingIndicator(true);
        Subscription subscription = mTasksRepository
                .getTask(mTaskId)//取出可觀察數據 Observable
                .subscribeOn(Schedulers.io())//在IO線程 產生數據
                .observeOn(AndroidSchedulers.mainThread())//在UI線程 分發數據 
                .subscribe(new Observer() {
                    @Override
                    public void onCompleted() {
                        mTaskDetailView.setLoadingIndicator(false);
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onNext(Task task) {
                        showTask(task);
                    }
                });
        mSubscriptions.add(subscription);//加入集合 才能在及時取消訂閱

代碼說明:

subscribeOn(): 指定 subscribe() 所發生的線程,即 Observable.OnSubscribe 被激活時所處的線程;蛘呓凶鍪录a生的線程。

observeOn(): 指定 Subscriber 所運行在的線程;蛘呓凶鍪录M的線程。

所以mTasksRepository數據M層的數據,就不需要做線程處理了,兩行代碼搞定線程切換,然后直接訂閱就等待數據的到達。

Model層

感覺上面說了這么多,Model層幾乎都說完了。
數據層負責數據的在本地或者遠程讀寫數據,每個應用的數據結構表示和存儲形式都不會有些不同。
官方的MVP數據使用SQL數據庫存儲,外部再維護一個懶漢式單例TasksRepository做數據緩存。
MVP架構通過接口回調分發數據,響應式MVP通過Observable得到可觀察數據。

如果結合Retrofit網絡框架,哪響應式MVP的Model層數據來源。就是可以是ServiceGenerator。
Retrofit的靜態構造方法構造網絡請求對象,傳入接口,代理模式生成出數據,P層就可以直接拿到數據了。
當然這只是我的初步想法,打算正用于我的個人項目。

總結

通過對比分析清晰了官方MVP的架構邏輯。 每個類都盡量符合面向對象設計原則,采用單一職責原則。整個項目架構清晰分工明確。 水平有限,請大家指正。依賴注入這塊我也不是很懂,具體的需要大家去Google。 最后對響應式MVP結合Retrofit提出一點自己的構想。

 

相關TAG標簽
上一篇:臺積電:絕大多數7nm客戶都會轉向6nm_IT新聞_博客園
下一篇:最后一頁
相關文章
圖文推薦

關于我們 | 聯系我們 | 廣告服務 | 投資合作 | 版權申明 | 在線幫助 | 網站地圖 | 作品發布 | Vip技術培訓 | 舉報中心

版權所有: 紅黑聯盟--致力于做實用的IT技術學習網站

美女MM131爽爽爽毛片