前言
本文主要根据自身项目的使用和对MVP的理解,搭建符合自身项目情况的MVP架构。
关于MVP
- M(Model)负责数据的请求,解析,过滤等数据操作。
- V(View)负责处理UI,通常以Activity Fragment的形式出现。
- P(Presenter)View Model中间件,交互的桥梁。
MVP的好处
- 分离了UI逻辑和业务逻辑,降低了耦合。
- Activity只处理UI相关操作,代码变得更加简洁。
- UI逻辑和业务逻辑抽象到接口中,方便阅读及维护。
- 把业务逻辑抽到Presenter中去,避免复杂业务逻辑造成的内存泄漏。
具体实现
1.view
IView:一般情况下,做数据请求都有显示加载框、请求成功、请求失败等操作,我们把这些共有的功能封装到IView中。
public interface IView {
/**
* 显示加载框
*/
void showLoading();
/**
* 隐藏加载框
*/
void dismissLoading();
/**
* 网络请求失败
*
* @param ex 异常信息
* @param code 错误码
* @param msg 错误信息
*/
void onFail(Throwable ex, String code, String msg);
/**
* 网络错误
*/
void onNetError();
}
2.Presenter
IPresenter:为了避免持有View的Presenter做耗时操作而引起的内存泄漏,我们的Presenter应该和宿主Activity/Fragment生命周期绑定。
public interface IPresenter<T extends IView> {
/**
* 绑定view
* @param view view
*/
void attachView(T view);
/**
* 分离view
*/
void detachView();
/**
* 判断view是否已经销毁
* @return true 未销毁
*/
boolean isViewAttach();
}
BasePresenter:抽象的persenter业务处理层。关联抽象层view和抽象model。
public abstract class BasePresenter<T extends IView, K extends IModel> implements IPresenter<T> {
protected K mModel;
private WeakReference<T> weakReference;
@Override
public void attachView(T view) {
// 使用弱引用持有view对象,防止内存泄漏
weakReference = new WeakReference<>(view);
if (this.mModel == null) {
this.mModel = createModule();
}
}
@Override
public void detachView() {
if (isViewAttach()) {
weakReference.clear();
weakReference = null;
}
if (mModel != null) {
mModel.unSubscribe();
mModel = null;
}
}
@Override
public boolean isViewAttach() {
return weakReference != null && weakReference.get() != null;
}
protected T getView() {
return weakReference.get();
}
protected void showLoading() {
if (isViewAttach()) {
getView().showLoading();
}
}
protected void onFail(Throwable ex, String code, String msg) {
if (isViewAttach()) {
getView().onFail(ex, code, msg);
}
}
protected void onNetError() {
if (isViewAttach()) {
getView().onNetError();
}
}
protected void dismissLoading() {
if (isViewAttach()) {
getView().dismissLoading();
}
}
/**
* 由外部创建 module
*
* @return module
*/
protected abstract K createModule();
}
3.model
IModel:由于项目使用Rxjava+Retrofit2.0+Okhttp,所以我在model层对Rxjava进行绑定和解绑,防止内存泄漏。
public interface IModel {
void unSubscribe();
void addSubscribe(Subscription subscription);
}
BaseModel:实现对Rxjava绑定和解绑,初始化ApiService。
public class BaseModel implements IModel {
protected ApiService mApi;
private CompositeSubscription mCompositeSubscription;
public BaseModel() {
this.mApi = RetrofitHelper.getInstance().createApiService(AppConstants.BASE_SERVER_IP);
}
@Override
public void unSubscribe() {
if (mCompositeSubscription != null && !mCompositeSubscription.isUnsubscribed()) {
mCompositeSubscription.clear();
mCompositeSubscription.unsubscribe();
}
}
@Override
public void addSubscribe(Subscription subscription) {
if (mCompositeSubscription == null) {
mCompositeSubscription = new CompositeSubscription();
}
mCompositeSubscription.add(subscription);
}
}
4.BaseMvpActivity基类
通过泛型规定Presenter,并且暴露抽象方法createPresenter()给子类来创建Presenter,基类实现IView中的公共方法,减少子类代码的冗余。至于BaseMvpActivity功能根据项目业务需求进行封装。
public abstract class BaseMvpActivity<P extends BasePresenter> extends AppCompatActivity implements IView {
protected P mPresenter;
private Unbinder unbinder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
unbinder = ButterKnife.bind(this);
// 初始化Presenter
initPresenter();
}
private void initPresenter() {
mPresenter = createPresenter();
// 完成Presenter和view的绑定
if (mPresenter != null) {
mPresenter.attachView(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 将Presenter和view解绑
if (mPresenter != null) {
mPresenter.detachView();
mPresenter = null;
}
// 解绑ButterKnife
if (unbinder != null) {
unbinder.unbind();
}
}
@Override
public void showLoading() {
// 这里实现自己的加载弹框
}
@Override
public void dismissLoading() {
// 取消弹框
}
@Override
public void onFail(Throwable ex, String code, String msg) {
// 基础的网络请求失败处理
}
@Override
public void onNetError() {
// 网络错误处理
}
/**
* 页面初始化数据
*/
protected void initData() {
}
/**
* 创建Presenter
*
* @return Presenter
*/
protected abstract P createPresenter();
/**
* 获取当前activity的id
*
* @return 当前xml的布局res ID
*/
protected abstract int getLayoutId();
/**
* 初始化view控件
*/
protected abstract void initView();
}
使用MVP
1.契约类Contract
通过契约类来管理Model、View、Presenter的所有接口,这样使得Presenter和View有哪些功能一目了然,维护起来也方便,同时使得View与Presenter一一对应,并有效地减少类的数目。
public interface LoginContract {
interface View extends IView {
/**
* 登录成功
*/
void onLoginSuccess(UserInfo data);
/**
* 登录失败
*/
void onLoginFail(Throwable ex, String code, String msg);
}
interface Presenter {
void login(String phone, String password);
}
interface Model extends IModel {
/**
* 登录
*
* @param map 用户登陆信息
* @param subscriber 回调
*/
void login(Map map, Subscriber subscriber);
}
}
2.LoginPresenter
public class LoginPresenter extends BasePresenter<LoginContract.View, LoginContract.Model> implements LoginContract.Presenter {
@Override
protected LoginContract.Model createModule() {
return new LoginModel();
}
@Override
public void login(String phone, String password) {
showLoading();
Map<String, String> params = new HashMap<>();
params.put("phone", phone);
params.put("password", password);
mModel.login(params, new BaseSubscriber<UserInfo>(new CallBackListener<UserInfo>() {
@Override
public void onSuccess(String code, UserInfo data) {
dismissLoading();
if (isViewAttach()) {
getView().onLoginSuccess(data);
}
}
@Override
public void onFailed(Throwable ex, String code, String msg) {
dismissLoading();
if (isViewAttach()) {
getView().onLoginFail(ex, code, msg);
}
}
@Override
public void onError() {
onNetError();
}
}));
}
}
3.LoginModel
public class LoginModel extends BaseModel implements LoginContract.Model{
@Override
public void login(Map map, Subscriber subscriber) {
Subscription subscription = mApi.login(map).compose(RetrofitHelper.applySchedulers()).subscribe(subscriber);
addSubscribe(subscription);
}
}
4.LoginActivity
public class LoginActivity extends BaseMvpActivity<LoginPresenter> implements LoginContract.View {
@BindView(R.id.tv_service)
TextView tvService;
private UserInfo userInfo;
@Override
protected int getLayoutId() {
return R.layout.activity_login;
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter();
}
@Override
protected void initView() {
addHeadTitle("登陆");
}
@OnClick({R.id.login, R.id.register})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.login:
mPresenter.login(phone,password);
break;
case R.id.register:
// 注册
break;
}
}
@Override
public void onLoginSuccess(UserInfo data) {
// 登陆成功回调
}
@Override
public void onLoginFail(Throwable ex, String code, String msg) {
// 登陆失败回调
}
}
总结
至此,MVP搭建完成。其实还有很多可以优化的地方。每个人对MVP的理解不一样,而MVP架构也并不是一成不变,适合自己项目的才是最好的。