本文主要是结合本身对MVP的理解搭建了符合自身业务场景的MVP框架。html
先放一个Demo地址,文章末尾也有android
Activity
Fragment
的形式出现。1.对View进行封装 通常状况下,作数据请求都有显示加载框、请求成功、请求失败等操做,咱们把这些共有的功能封装到BaseView中。git
public interface IBaseView {
/**
* 显示加载框
*/
void showLoading();
/**
* 隐藏加载框
*/
void dismissLoading();
/**
* 空数据
*
* @param tag TAG
*/
void onEmpty(Object tag);
/**
* 错误数据
*
* @param tag TAG
* @param errorMsg 错误信息
*/
void onError(Object tag, String errorMsg);
/**
* 上下文
*
* @return context
*/
Context getContext();
}
复制代码
2.对Presenter封装 为了不持有View的Presenter作耗时操做而引发的内存泄漏,咱们的Presenter应该和宿主Activity/Fragment
同建立、同销毁。github
public abstract class BasePresenter{
...
/**
* 绑定View
*/
public void attachView(View view) {
this.view=view;
}
/**
* 解绑View
*/
public void detachView() {
this.view=null;
}
...
}
public abstract class MvpActivity<P extends BasePresenter> extends BaseActivity implements IBaseView{
...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//建立present
presenter = createPresenter();
if (presenter != null) {
presenter.attachView(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null) {
presenter.detachView();
presenter = null;
}
}
...
}
复制代码
如上操做当然能够解决内存泄漏问题,但又会引起行的问题:bash
场景:用户打开商品列表页,网络很差获取数据比较慢,用户离开该页面,继续浏览其余页面,忽然应用崩溃了。网络
分析问题: 在用户打开页面的时候绑定P和V,离开页面的时候解绑P和V,当耗时操做完成调用V更新界面,此时因为P和V已经解绑V处于null,调用V的更新页面方法就会引发空指针异常。架构
解决问题: 使用动态代理对View作弱引用,完整的BasePresenter以下:框架
public abstract class BasePresenter<M extends IBaseModel, V extends IBaseView> {
private V mProxyView;
private M module;
private WeakReference<V> weakReference;
/**
* 绑定View
*/
@SuppressWarnings("unchecked")
public void attachView(V view) {
weakReference = new WeakReference<>(view);
mProxyView = (V) Proxy.newProxyInstance(
view.getClass().getClassLoader(),
view.getClass().getInterfaces(),
new MvpViewHandler(weakReference.get()));
if (this.module == null) {
this.module = createModule();
}
}
/**
* 解绑View
*/
public void detachView() {
this.module = null;
if (isViewAttached()) {
weakReference.clear();
weakReference = null;
}
}
/**
* 是否与View创建链接
*/
protected boolean isViewAttached() {
return weakReference != null && weakReference.get() != null;
}
protected V getView() {
return mProxyView;
}
protected M getModule() {
return module;
}
protected Context getContext() {
return getView().getContext();
}
protected void showLoading() {
getView().showLoading();
}
protected void dismissLoading() {
getView().dismissLoading();
}
/**
* 经过该方法建立Module
*/
protected abstract M createModule();
/**
* 初始化方法
*/
public abstract void start();
/**
* View代理类 防止 页面关闭P异步操做调用V 方法 空指针问题
*/
private class MvpViewHandler implements InvocationHandler {
private IBaseView mvpView;
MvpViewHandler(IBaseView mvpView) {
this.mvpView = mvpView;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//若是V层没被销毁, 执行V层的方法.
if (isViewAttached()) {
return method.invoke(mvpView, args);
} //P层不须要关注V层的返回值
return null;
}
}
}
复制代码
3.契约类Contract的出现 经过契约类来管理Model、View、Presenter的全部接口,这样使得Presenter和View有哪些功能一目了然,维护起来也方便,同时使得View与Presenter一一对应,并有效地减小类的数目。异步
public interface Contract {
interface Model extends IBaseModel {
void login(User user, ResponseCallback callback);
}
interface View extends IBaseView {
User getUserInfo();
void loginSuccess(User user);
}
interface Presenter {
void login();
}
}
复制代码
4.对Activity
的封装,Fragment
封装同理ide
public abstract class BaseMvpActivity<P extends BasePresenter> extends Activity implements IBaseView {
protected P presenter;
@SuppressWarnings("unchecked")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//建立present
presenter = createPresenter();
if (presenter != null) {
presenter.attachView(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (presenter != null) {
presenter.detachView();
presenter = null;
}
}
@Override
public void showLoading() {
if (loadingDialog != null && !loadingDialog.isShowing()) {
loadingDialog.show();
}
}
@Override
public void dismissLoading() {
if (loadingDialog != null && loadingDialog.isShowing()) {
loadingDialog.dismiss();
}
}
@Override
public void onEmpty(Object tag) {
}
@Override
public void onError(Object tag, String errorMsg) {
}
@Override
public Context getContext() {
return mContext;
}
/**
* 建立Presenter
*/
protected abstract P createPresenter();
}
复制代码
经过泛型规定Presenter,而且暴露抽象方法createPresenter()给子类来建立Presenter,基类实现BaseView中的公共方法,减小子类代码的冗余。 5.登陆案例
public interface LoginContract {
interface Model extends IBaseModel {
/**
* 登陆
*
* @param user 用户信息
* @param callback 回调
*/
void login(User user, ResponseCallback callback);
}
interface View extends IBaseView {
/**
* 返回用户信息
*/
User getUserInfo();
/**
* 登陆成功
*/
void loginSuccess(User user);
}
interface Presenter {
/**
* 登陆
*/
void login();
}
}
复制代码
Model
public class LoginModel implements LoginContract.Model {
@Override
public void login(User user, ResponseCallback callback) {
if (user == null) {
callback.onError("", (Throwable) new Exception("用户信息为空"));
}
RequestParam param = new RequestParam();
param.addParameter("username", user.getUsername());
param.addParameter("password", user.getPassword());
HttpUtils.getInstance()
.postRequest(Api.LOGIN, param, callback);
}
}
复制代码
Presenter
public class LoginPresenter extends BasePresenter<LoginContract.Model, LoginContract.View>
implements LoginContract.Presenter {
@Override
public void login() {
if (isViewAttached()) {
getView().showLoading();
getModule().login(getView().getUserInfo(), new OnResultObjectCallBack<User>() {
@Override
public void onSuccess(boolean success, int code, String msg, Object tag, User response) {
if (code == 0 && response != null) {
getView().loginSuccess(response);
} else {
getView().onError(tag, msg);
}
}
@Override
public void onFailure(Object tag, Exception e) {
getView().onError(tag, msg);
}
@Override
public void onCompleted() {
getView().dismissLoading();
}
});
}
}
@Override
protected LoginModel createModule() {
return new LoginModel();
}
@Override
public void start() { }
}
复制代码
登陆Activity
public class LoginActivity extends ActionBarActivity<LoginPresenter> implements LoginContract.View {
@BindView(R2.id.edt_name)
EditText edtName;
@BindView(R2.id.edt_pwd)
EditText edtPwd;
@BindView(R2.id.ob_login)
ObserverButton obLogin;
@BindView(R2.id.ob_register)
TextView obRegister;
@Override
protected int getLayoutId() {
return R.layout.user_activity_login;
}
@Override
protected void initView() {
setTitleText("登陆");
obLogin.observer(edtName, edtPwd);
}
@OnClick({R2.id.ob_login, R2.id.ob_register})
public void onViewClicked(View view) {
int i = view.getId();
if (i == R.id.ob_login) {
presenter.login();
} else if (i == R.id.ob_register) {
ActivityToActivity.toActivity(mContext, RegisterActivity.class);
}
}
@Override
public void loginSuccess(User user) {
UserInfoUtils.saveUser(user);
EventBusUtils.sendEvent(new Event(EventAction.EVENT_LOGIN_SUCCESS));
finish();
}
@Override
public void onError(Object tag, String errorMsg) {
super.onError(tag, errorMsg);
ToastUtils.showToast(mContext, errorMsg);
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter();
}
@Override
public void onEventBus(Event event) {
super.onEventBus(event);
if (TextUtils.equals(event.getAction(), EventAction.EVENT_REGISTER_SUCCESS)) {
finish();
}
}
@Override
protected boolean regEvent() {
return true;
}
@Override
public User getUserInfo() {
return new User(edtName.getText().toString().trim(), edtPwd.getText().toString().trim());
}
}
复制代码
不管是MVP仍是MCV或者MVVM,都是为把业务与UI分离,避免在一个Activity里把全部的操做都塞进来,各自在各自的领域工做。每一个人对于层级结构都有不一样的理解和见解,封装一个适合本身、适合当下业务场景的框架才是最重要的。
最后放上Demo地址,共同窗习,有什么很差的地方,欢迎你们指出!
参考文献 Google爸爸的案例 JesseBraveMan的 Android MVP架构搭建 浅谈Android中的MVP架构 深刻讲解Android MVP框架