前言
前段时间做了一个项目,好朋友搭好了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也需要硬性写入,后期可以考虑使用依赖注入。使代码更灵活。