基於Retrofit2+OkHttp3+RxJava實現的MVP框架(1)

基於項目需要,特別設計了一套簡單的MVP開發框架,用以描述基於REST接口協議的業務數據流向-獲取到展示的過程。

  • V模塊-視圖模塊,是由Activity、Fragment等窗口組件來實現。
  • P模塊-展現模塊,是由自定義Presenter類及子類實現。
  • M模塊-業務數據模塊,則是基於Retrofit2+OkHttp3+RxJava實現。Retrofit2+OkHttp3主要是爲了方便快速的業務接口的實現,RxJava是爲了解決跨線程的數據交互。

基於以上描述,我們先來介紹一下View和Presenter的定義:

1. View模塊

    View的相關類設計包括一個接口IView,一個基類BaseView。

IView

public interface IView {
    public void onActionStart();
    public void onActionSuccess();
    public void onActionFailure();
}

IView定義了一些抽象方法,用來描述View類型應該具備的基本行爲(方法),作爲與Presenter進行交互的渠道。這裏定義的三個方法描述了開始、成功、失敗共三種行爲,則具體的展示方式則有其實現類完成。

BaseView

public abstract class BaseView extends Activity implements IView {
@Override
    public void onActionStart() {
        Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionStart");
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "Action starts...", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onActionSuccess() {
        Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionSuccess");
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "Action success!", Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void onActionFailure() {
        Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionFailure");
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "Action failure!", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

BaseView實現了IView的三個通用行爲方法,以Toast展示成功失敗,以ProgressDialog展示等待狀態。更多的樣式及行爲完全自定義。考慮到APP交互設計過程中,特別的網絡請求過程,大致行爲相同,所以可以抽象一部分在基類中實現,業務模塊子類則不用單獨實現。

2. Presenter

    Presenter部分也包含一個IPresenter接口,一個BasePresenter基類。

IPresenter

public interface IPresenter {
    void destroy();
}

IPresenter接口,定義了通用行爲-destroy。

BasePresenter

public abstract class BasePresenter implements IPresenter {    
    public void destroy() {
        // TODO destroy
    }
}

BasePresenter實現了IPresenter的destroy方法,同時可以增加自己的通用行爲。

我們都知道,View和Presenter的交互是雙向的,View通過用戶動作向Presenter發起指令,Presenter執行完之後,將結果告知View做進一步的展示。那麼View和Presenter是如何完成交互,它們的關係是如何建立的呢?

首先,我們定義一個原則:

  • 一個Presenter只能對應一個View,反之亦然

這樣定義的考慮是,更加明確清晰具體的業務功能。比如,登錄功能,則對應一個LoginView和LoginPresenter。基於這個原則,我們可以通過如下方式,將Presenter與View關聯起來,

public abstract class BasePresenter<V extends IView> implements IPresenter {
    protected V mView;

    public BasePresenter(V view) {
        mView = view;
    }

    public void destroy() {
        mView = null;
    }
}

我們對BasePresenter增加範型限制,並且該類型必須是IView的實現類,這樣就可以將具體業務的Presenter和View給關聯起來。同時,Presenter中包含有一個IView實現類的對象mView,在創建Presenter時傳入作爲屬性,那麼在需要告知View該展示何種狀態時,則可以通過該View屬性,完成消息傳遞-方法調用。還是以登錄作爲例子,

public class LoginPresenter extends BasePresenter<LoginPresenter.LoginView> {
    public void login(String username, String password) {
        // TODO
    }
    public interface LoginView extends IView {
        public void onLoginSuccess(String token);
        public void onLogining();
        public void onLoginFailure();
    }
}

此時,LoginPresenter聲明瞭範型LoginView,而LoginView是作爲內部接口來定義的,並且繼承了IView。這麼做就是考慮到,具體的展示策略是由Presenter來規範的,那麼將具體業務的View定義在該業務的Presenter中,是很合理的。想要具備此業務功能,則實現該View接口。LoginPresenter對外提供了登錄功能的對應方法login(), 入餐爲用戶名和密碼;在完成登錄後,LoginPresenter則可以通過LoginView對象,來完成消息傳遞,告知成功、失敗。

上面講到的是從Presenter到View的關聯,那麼接下來我們來看View到Presenter的關聯。

public abstract class BaseView extends Activity implements IView {
    private IPresenter[] mAllPresenters = null;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // init all presenter will be used
        mAllPresenters = getPresenter();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // destroy all presenters when destroy view
        if (mAllPresenters != null && mAllPresenters.length > 0) {
            for (IPresenter presenter : mAllPresenters) {
                presenter.destroy();
            }
        }
    }
}

這裏看到,BaseView中定義了Presenter類型的數組對象,該對象保存着當前所需業務對應的所有Presenter對象。並且在onDestroy方法中全部調用destory。可能有人會疑問,上面講到了一個View-Presenter的一對一關聯原則,這裏爲何是一對多?

其實剛纔講的一對一,是在具體的單個業務功能的角度來看。View的具體業務實現類定義由Presenter的對應業務實現類來完成定義,這樣都對應一個具體功能,站在具體功能業務角度,就是一對一的關係。而這裏的代碼看起來是一對多的關係,是因爲用到了數組,這裏是站在代碼實現的角度。假設這個BaseView的具體實現類需要僅完成一個功能,那麼Presenter數組個數爲1,那麼就是一對一關係;如果要完成多個功能,那麼數組個數就爲N,這裏它已經集成了多個功能業務,那麼這個View既是功能A的View,也是功能B的V,這裏已經不是站在一個業務功能上來看待這個View了。BaseView是Activity的子類,但不能夠完全對等於MVP模式中的View。

public class LoginActivity extends BaseView implements LoginPresenter.LoginView{
    private LoginPresenter mPresenter = new LoginPresenter(this);
@Override
    protected BasePresenter[] getPresenter() {
        return new BasePresenter[]{mPresenter};
    }
    /*-----------Implements of LoginView-----------*/
    @Override
    public void onLoginSuccess(String token) {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
        Toast.makeText(
                getApplicationContext(), "登錄成功 token:" + token, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onLogining() {
        mProgressDialog =
                ProgressDialog.show(this, null, "登錄中...");
    }

    @Override
    public void onLoginFailure() {
        if (mProgressDialog != null) {
            mProgressDialog.dismiss();
        }
        Toast.makeText(
                getApplicationContext(), "登錄失敗!", Toast.LENGTH_SHORT).show();
    }
}

上面這段代碼,則是以登錄的Activity爲例,按照上述思想,很容易理解。所以,在實現View和Presenter的時候,分別繼承BaseView和BasePresenter來完成具體業務功能開發即可。

至此,V和P的關聯關係的設計思路已經說明完畢,下一篇將介紹核心-業務數據層M的設計思路及實現。


發佈了7 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章