Android MVP架构(2)使用注解实现一个MVP框架

上篇我们说到MVP的一些相关概念:Android MVP架构(1)概念介绍

在弄清楚了MVP架构的基本原理后,我们就可以着手去自己实现一个MVP架构项目了。目前市面已有不少成熟的MVP框架,本篇仅作学习以及研究探讨使用,不作任何对比。

使用自定义注解实现注入:

由上篇的架构图可以看出,Presenter和Model之间会进行数据的交互,所以Presenter里往往会持有Model对象的引用,而在Activity层,我们是需要调用Presenter来触发View层的回调结果的。简而言之就是:Activity里有Presenter,Presenter里面又有Model。而为了处理这些复杂的依赖关系,我们可以统一使用注入的方式来解决。注解注入的本质其实就是反射,通过java的反射机制,可以方便我们快速地注入所需的对象。类似Spring的注入机制,对反射还不太了解的小伙伴可以移步我的另一篇博客:仿springboot @Autowired自动注入

在本篇中,通过使用@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框架的创建,篇幅原因,下一篇中将会完善该框架,使其能够完成基本的网络请求。

Android MVP架构(3)MVP框架的使用

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章