前言
前段時間做了一個項目,好朋友搭好了MVP框架,現在項目結束了,我想總結一下其中的關鍵點,方便以後自己搭建架構。
MVP介紹
什麼是MVP架構呢,以我本人的理解,項目代碼模塊分爲模型、視圖和展現層。視圖層不能直接和模型層直接關聯或調用。他們直接需要通過展現層來交互。MVP的前身是MVC。爲什麼要分層呢,不分層不是一樣可以實現功能,我在activity中寫視圖,獲取數據,顯示數據,保存數據。這樣也ok啊。那我自己的感觸來說,分層的好處比不分層的多。我本人長期維護ANDROID原生的APP,比如瀏覽器,聯繫人,短信,電子郵件,這些app基本上沒有分層架構,如果我要加一個功能,那麼修改的時間是非常多的,修改了這個功能還需要進行測試,因爲我不能保證是否影響到其他模塊。同時要掌握整個APP的代碼模塊,才能夠比較順利的完成需求。
09年的時候,我在廈門培訓,當時培訓機構就是給我們講解MVC架構,實現架構的3個技術是 Struts Spring Hibernate,我當時覺得這個東西沒有什麼了不起,也就那樣,我在寫代碼的時候,發現就是按照這些模板來寫就可以了,完全不要東腦子,我覺得沒有前途。特別是那些javebean,寫的特別簡單,幾個屬性,加上get set方法,就ok了。但是現在想想,自己當時非常的圖樣 圖森破。
- Model 模型層
modele層主要做哪些事情呢?主要是獲取數據。數據存儲大概有這麼幾種:數據庫,網絡服務器數據,sharepreference。最後,我們還可以對數據的來源進行抽象,通過代理模式來進行獲取對應的實例對象,實例對象再實際的返回數據。
Model 與 Presenter層如何交互呢?考慮這樣一種情況,當展現層調用模型層方法,那麼模型層要方法網絡服務器,訪問網絡服務器需要時間,那麼展現層必須等等;而當模型層獲取數據時,如何通知展現層呢;所以這些情況就比較複雜,那麼我們需要使用觀察者模式,展現層將請求提交給模型層,模型層在獲取到信息後回調展現層。目前非常流行的技術是RXJava, - Presenter 展現層
Presenter展現層是用來隔離視圖層與模型層。視圖層對模型層的操作主要有對數據的請求,保存,修改等。展現層通過對這些操作進行統一管理,減少view層的邏輯處理,從而達到簡化邏輯。比如我們用戶點擊了一個選項,需要顯示此選項的詳細信息。視圖層只傳送選項的id,模型層再加上通用請求參數(用戶id,設備信息,時間),去服務器請求數據。模型層這些通用參數,模型層可以繼承一個父類,父類裏面實現通用參數創建、管理。 - View 視圖層
視圖層,很明顯,就是要現實數據給用戶,用戶在視圖界面可以進行操作。我們展現數據時,一般會用到RecyclerView控件,和RecyclerView的搭檔,RecyclerView.Adapter。如果要在一個activity中展示多個不同的界面,那麼我們需要Fragment。在很多時候,關鍵功能使用標準控件無法達到效果,那麼我們需要進行自定義控件。
簡單代碼
下面我就貼出部分代碼,說明MVP的簡單實現。
- model層
protected ApiService api = Api.getApiService();//在BaseModel父類中定義
public class AppUpdateModel<T> extends BaseModel {
public void getUpdateInfo(Context ctx, RequestBody currentInfo, ObservableTransformer<T, T> transformer,
final ObserverResponseListener<AppUpdateBean> listener) {
Observable<AppUpdateBean> obs = api.getUpdateInfo(currentInfo);
subscribe(ctx,obs,transformer,listener,false,false);
}
}
- 展現層 Presenter
public class AppUpdateContact {
public interface View extends BaseView{
void updateView(AppUpdateBean bean);
void onDownLoad(File apkFile);
}
public interface Presenter extends BasePresenter{
void downloadApk(AppUpdateBean appInfo);
}
}
public class AppUpdatePresenter<T extends AppUpdateContact.View> extends
BasePresenterImpl<T> implements AppUpdateContact.Presenter {
AppUpdateModel mModel;
public AppUpdatePresenter(Context ctx, T view) {
super(ctx, view);
mModel = new AppUpdateModel<>();
}
@Override
public void loadData() {
Map<String,String> requestParam = getBaseRequestParam();
mModel.getUpdateInfo(mContext, createRequestBody(requestParam), mView.bindLifecycle(), new ObserverResponseListener<AppUpdateBean>() {
@Override
public void onNext(AppUpdateBean appUpdateBean) {
mView.updateView(appUpdateBean);
}
@Override
public void onError(ExceptionHandle.ResponeThrowable e) {
}
});
}
- View 視圖層
public class AppUpdateActivity extends BaseActivity<AppUpdateContact.View,
AppUpdateContact.Presenter> implements AppUpdateContact.View {
private final String TAG = AppUpdateActivity.class.getSimpleName();
@BindView(R.id.version_detail)
TextView mVersionDetail;
@BindView(R.id.version)
TextView appVersion;
@BindView(R.id.version_date)
TextView versionDate;
@BindView(R.id.current_version)
TextView currentVersion;
@BindView(R.id.download_app)
Button download;
private AppUpdateBean appInfo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_app_update);
}
@Override
public AppUpdateContact.View createView() {
return null;
}
@Override
public AppUpdateContact.Presenter createPresenter() {
return new AppUpdatePresenter(mContext, this);
}
@Override
public int getLayout() {
return R.layout.activity_app_update;
}
public void init() {
iv_title.setText("版本更新");
getPresenter().loadData();
updateDownloadBtn();
}
...
@Override
public <T> ObservableTransformer<T, T> bindLifecycle() {
return bindToLifecycle();
}
@Override
public void updateView(AppUpdateBean bean) {
if (bean.getSuccess() == AppUpdateBean.SUCCESS) {
appInfo = bean;
mVersionDetail.setText(bean.getRemark());
appVersion.setText(bean.getVersion());
CharSequence txVersion = getResources().getString(R.string.tx_current_version, BuildConfig.VERSION_NAME);
currentVersion.setText(txVersion);
versionDate.setVisibility(View.GONE);
Log.i(TAG, bean.toString());
}
updateDownloadBtn();
}
@Override
public void onDownLoad(File apkFile) {
Log.i(TAG, "installApk");
installApk(apkFile);
ProgressDialogUtil.dismiss();
}
@OnClick(R.id.download_app)
public void onClick(View view) {
switch (view.getId()) {
case R.id.download_app:
if (appInfo != null && isVersionLate()) {
getPresenter().downloadApk(appInfo);
ProgressDialogUtil.showProgressDialog(mContext);
}
break;
}
}
private boolean isVersionLate() {
String current = BuildConfig.VERSION_NAME;
String newVersion = appInfo != null ? appInfo.getVersion() : "";
return newVersion.compareTo(current) > 0;
}
private void updateDownloadBtn(){
if (isVersionLate()) {
download.setText(R.string.tx_download_apk);
} else {
download.setText(R.string.tx_download_new);
}
}
}
展現層中使用了butterknife技術,控件的初始化全部靠他,不用我們自己去寫findviewbyId. @OnClick(R.id.download_app)使控件注入點擊事件。
結語
目前我們的項目中沒有使用Dagger2,所以view層的 presenter 需要硬編碼寫入,同樣presenter層的model也需要硬性寫入,後期可以考慮使用依賴注入。使代碼更靈活。