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框架的使用

 

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