基於項目需要,特別設計了一套簡單的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的設計思路及實現。