在弄清楚了MVP架构的基本原理后,我们就可以着手去自己实现一个MVP架构项目了。目前市面已有不少成熟的MVP框架,本篇仅作学习以及研究探讨使用,不作任何对比。
使用自定义注解实现注入:
由上篇的架构图可以看出,Presenter和Model之间会进行数据的交互,所以Presenter里往往会持有Model对象的引用,而在Activity层,我们是需要调用Presenter来触发View层的回调结果的。简而言之就是:Activity里有Presenter,Presenter里面又有Model。而为了处理这些复杂的依赖关系,我们可以统一使用注入的方式来解决。注解注入的本质其实就是反射,通过java的反射机制,可以方便我们快速地注入所需的对象。
在本篇中,通过使用@Inject注解来实现Model以及Presenter的自动注入。
小提示,实际开发中为了提升代码效率,请使用dagger替换反射来实现自定义注入。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {}
为了实现注入,我们还需定义一个注入器,既然是注入器,就会包含创建和销毁两个过程,对注入器Injector的定义如下:
public interface Injector {
void inject();
void destroy();
}
由于Model和Presenter的注入并不是全部一样的,所以我们需要使用两个注入器来对它们进行注入,分别对应为ModelInjector以及PresenterInjector,注入器需要实现Injector接口。
/**
* model注入器
*/
public class ModelInjector implements Injector {
// 注入到的presenter
private BasePresenter mPresenter;
// 需要注入的model
private List<BaseModel> mModels;
public ModelInjector(BasePresenter presenter) {
mPresenter = presenter;
}
@Override
public void inject() {
mModels = new ArrayList<>();
// 获得mPresenter的字段
Field[] fields = mPresenter.getClass().getDeclaredFields();
for (Field field : fields) {
// 获得字段里面使用了MvpInject注解的实例 这里指model
Inject inject = field.getAnnotation(Inject.class);
if(inject != null){
try {
// 获得该实例的类型
Class<? extends BaseModel> clazz = (Class<? extends BaseModel>) field.getType();
// 创建实例
BaseModel model = clazz.newInstance();
field.setAccessible(true);
field.set(mPresenter, model);
mModels.add(model);
} catch (IllegalAccessException | InstantiationException | ClassCastException e) {
e.printStackTrace();
throw new IllegalStateException("are you ok?");
}
}
}
}
@Override
public void destroy() {
mModels.clear();
mModels = null;
mPresenter = null;
}
}
/**
* Presenter注入器
*/
public class PresenterInjector implements Injector {
// 需要注入的地方(Activity)
private IBaseView mView;
// 需要注入的接口
private List<BasePresenter> mPresenters;
public PresenterInjector(IBaseView view){
mView = view;
}
@SuppressWarnings("unchecked")
@Override
public void inject() {
mPresenters = new ArrayList<>();
Field[] fields = mView.getClass().getDeclaredFields();
for (Field field : fields) {
Inject inject = field.getAnnotation(Inject.class);
if(inject != null){
try {
Class<? extends BasePresenter> clazz = (Class<? extends BasePresenter>) field.getType();
BasePresenter presenter = clazz.newInstance();
field.setAccessible(true);
field.set(mView, presenter);
presenter.viewAttach(mView);
mPresenters.add(presenter);
} catch (IllegalAccessException | InstantiationException | ClassCastException e) {
e.printStackTrace();
throw new IllegalStateException("inject wrong!");
}
}
}
}
@Override
public void destroy() {
for (BasePresenter presenter : mPresenters) {
presenter.viewDetach();
}
mPresenters.clear();
mPresenters = null;
mView = null;
}
}
注入器创建完毕之后,下一步就是如何将Model以及Presenter进行更好的封装了。这里受到安卓轮子哥AndroidProject项目的启发轮子哥:AndroidProject,使用代理的方式巧妙避免getView过程中的大量判空。设计的Presenter封装基类如下:
public class BasePresenter<V extends IBaseView>
implements InvocationHandler {
/** View 层 */
private V mView;
/** 代理对象 */
protected V view;
// model注入器
private Injector mInjector;
@SuppressWarnings("unchecked")
public void viewAttach(V v) {
mView = v;
// V 层解绑了 P 层,那么 getView 就为空,调用 V 层就会发生空指针异常
// 如果在 P 层的每个子类中都进行 getView() != null 防空判断会导致开发成本非常高,并且容易出现遗漏
view = (V) Proxy.newProxyInstance(v.getClass().getClassLoader(),
v.getClass().getInterfaces(), this);
mInjector = new ModelInjector(this);
mInjector.inject();
}
public void viewDetach() {
mView = null;
mInjector.destroy();
}
private boolean isAttached() {
return view != null && mView != null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return isAttached() ? method.invoke(mView, args) : null;
}
}
其中的IBaseView作为一切View父类的存在,内部没有任何内容。
至于Model的进一步封装,主要用以处理网络请求为主,可通过自定义的BaseModel封装get/post方式的实现。
public class BaseModel{
/**
* json post 请求
* @param url
* @param request
* @param listener
*/
protected void postJson(String url, JSONObject request, IResponseListener listener) {
HttpManager.getInstance().postJson(url,request,listener);
}
/**
* post 请求
* @param url
* @param request
* @param listener
*/
protected void doPost(String url, RequestBody request, IResponseListener listener) {
HttpManager.getInstance().post(url,request,listener);
}
/**
* 带参 get请求
* @param url
* @param paramsMap
* @param listener
*/
protected void doGet(String url, Map<String, String> paramsMap, IResponseListener listener) {
HttpManager.getInstance().get(url, paramsMap, listener);
}
/**
* 无参 get请求
* @param url
* @param listener
*/
protected void doGet(String url, IResponseListener listener) {
doGet(url,null, listener);
}
}
至此,整个MVP框架的封装已经基本完成,只需要在自定义的Activity基类的基础上,对创建的注解使用注入器进行注入即可。
示例的MvpActivity如下:
public abstract class MvpActivity
extends BaseActivity implements IBaseView{
private Injector pInjector;
@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
pInjector = new PresenterInjector(this);
pInjector.inject();
initView();
initData();
}
protected void initData(){};
protected void initView(){};
@Override
protected void onDestroy() {
pInjector.destroy();
super.onDestroy();
}
}
本篇主要讲解了简单的MVP框架的创建,篇幅原因,下一篇中将会完善该框架,使其能够完成基本的网络请求。