这几天都在研究如何搭建一个实用稳固的MVP架构做为快速开发的基底。 也纠结了好久Presenter层该如何复用,在网上查阅了不少资料以后仍然没能找到一个适用的办法,有的写法单纯是为了presenter的复用而写,却给其余模块增负担;有的实现的手法过于僵硬,不符合写代码的原则。 在看完各类奇奇怪怪的实现思路以后,本身心里也有了一个实现presenter复用的一套方法,不过还不知道可不可行,到时撸完了可行再贴出来。git
走过路过点歌Start O(∩_∩)O Github项目地址github
这篇文章先撸一遍MVP的基本框架搭建,看完这篇文章你能学会:bash
顺着个人思路来一遍,先构造基类: 首相是对View层的基类下手, IBaseView网络
package com.example.administrator.mvpframedemo.base;
public interface IBaseView {
}
复制代码
实不相瞒,这个我一个方法都没定义。看到网上有不少人会把showToast()等这样的方法定义在这里喔我就不一样意了,由于这些过重复固定的我以为放在基类让每一个子类去实现实在麻烦,因此我怎么作呢?我把showToast这样重复的实现放在BaseActivity;下面一块儿看一下 BaseActivity架构
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
initBeforeCreate();
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
init(savedInstanceState);
initData();
initView();
logic();
}
protected abstract void initBeforeCreate();
protected abstract int getLayoutId();
protected abstract void init(Bundle savedInstanceState);
protected abstract void initData();
protected abstract void initView();
protected abstract void logic();
protected void showToast (String toastStr) {
Toast.makeText(this, toastStr, Toast.LENGTH_SHORT).show();
};
}
复制代码
这是在没啥好说,这里释放了更BaseActivity有关的,而不是跟Mvp有关的。至于你了解的BaseActivity中要处理presenter绑定,解绑这样的操做我会另外建一个BaseMvpActivity来作专门针对Mvp的处理的。保持这样结构的整洁仍是感受神清气爽的;固然BaseMvpActivity仍是要继承BaseActivity的;万一哪每天收的说这个模块我要用MVC之类的时候也好处理一点。 接下来就是 BasePresenter框架
public class BasePresenter<V extends IBaseView> {
private WeakReference<V> mViewRef;
public V mView;
public void attachView(V view) {
mViewRef = new WeakReference<V>(view);
mView = mViewRef.get();
}
public void detachView() {
mViewRef.clear();
mView = null;
}
}
复制代码
这里定义了attachView()以及detachView()两个接口;这是为啥?还有为何要有mViewRef? 首先说为何要attachView()?这个其实只要你敲过一些简单的mvp的代码就会知道,每一次都要写这样的代码:异步
MainActivity{
// 这一句代码作了两个事情,①View层建立本身适合的Presenter,②而后把本身传给Presenter完成二者的绑定
Presenter presenter = new Presenter(MainActivity.this);
}
复制代码
那么attachView()就是完成②的事情,至于①是留给View层本身去实现的,后面会说到。 因此attachView()诞生的缘由就了解了,那么detachView()设计的缘由呢? detachView()还有mViewRef的出现都是为了解决内存泄漏而存在的。那么是怎么解决内存泄漏的存在呢? 当一个Activity在显示的时候退出了,GC在感受内存紧张的时候会想把这个Activity给回收掉,可是此时presenter对象是持有Activity对象的,因此GC就没办法回收了,这样就存在泄漏的隐患了。这种持有activity对象而引发内存泄漏是很是常见的缘由。 因此咱们使用deacttach()以及mViewRef(弱引用)来解除二者的绑定,让GC为所欲为。那何时解绑呢?在Activity的onDestroy()生命周期的时候合适。 那么接下来就是: BaseMvpActivityide
public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity implements IBaseView {
protected T mPresenter;
@Override
protected void init(Bundle savedInstanceState) {
mPresenter = bindPresenter();
mPresenter.attachView(this);
}
protected abstract T bindPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}
}
复制代码
这个BaseMvpActivity里面作了针对Mvp的事情,包括定义bindPresenter()建立本身合适的presenter,而后执行presenter.attachView(this),将二者进行绑定;最后在onDestroy()方法中接触二者的绑定。ui
--------------------------------------人工分割线---------------------------------------------------------------------- 到这里,关于Mvp的基类设计好像就差很少了。 而后来模拟看看实际上要进行的业务: 登陆页面要请求登陆 第一步:在constract(合约层,维护P层与V层的关系) 创建一个LoginConstractthis
public interface LoginContract {
abstract class LoginPresenter extends BasePresenter<LoginView>{
public abstract void login(String name, String password);
}
interface LoginView extends IBaseView {
void showTips(String str);
}
}
复制代码
在合约里面定义LoginPresenter以及LoginView的接口;实现交给其余地方。 在这里开发者就要明白登陆的逻辑,例如:首先View利用presetner发起登陆(login)请求,请求完成以后View要给用户显示结果(showTips);因此在上面定义的接口也是这么来的。 实现: LoginPresenter:
public class LoginPresenter extends LoginContract.LoginPresenter {
ILoginModel loginModel;
@Override
public void login(String name, String password) {
loginModel = new LoginModel();
loginModel.login(name, password, new LoginCallBack());
}
private class LoginCallBack implements ICallBack<LoginDomain, Exception> {
@Override
public void onSuccess(LoginDomain result) {
mView.showTips("登陆成功");
}
@Override
public void onFail(Exception error) {
mView.showTips("登陆失败");
}
}
}
复制代码
简简单单,login()方法须要用到model层去帮忙获取数据。因此实例化合适的model,而后调用它取数据的接口。 我通常会把model层分为interface以及impl两层,一个定义接口,一个实现。 不过关于Presenter与Model层的交互问题须要说明一下:由于Model层取数据大多未异步操做,因此一般使用接口回调的方式。因此在调用Model的login()取数据的时候传递一个回调对象来实现异步。 LoginModel:
public class LoginModel implements ILoginModel {
@Override
public void login(String username, String password, ICallBack<LoginDomain, Exception> callBack) {
// 模拟一部网络获取
try {
Thread.sleep(2000);
callBack.onSuccess(new LoginDomain("周润发","123"));
} catch (InterruptedException e) {
e.printStackTrace();
callBack.onFail(new Exception());
}
}
}
复制代码
到了这里其实只有一些细节的问题了,关于presenter与model层的ICallBack回调如何统一规范等等都是小事。 最重要的是presenter如何复用?敬请期待我以后的博文。
最后是个人项目的目录结构: