MVP模式之Presenter和View解耦

前言

關於MVP模式系列的文章,前面已經寫了3篇了,本來想等以後對MVP模式有了更深層次的理解後再來總結一下的,但是最近在研究Adapter和Activity或Fragment解耦的時候,突然想到了View和Presenter之間的解耦,索性就嘗試了一下,然後來和大家分享一下。

鄭重聲明:

由於我對架構的經驗不足,所以在這裏只在MVP框架體系下討論Presenter和View之間的關係解耦(也可能只能稱得上是弱化了他們之間的聯繫),如果修改到了框架本身的屬性,請大家忽略,或者大家提供一個在不改變MVP模式本身的前提下解耦Presenter和View的方式,感激不盡。

一、原理簡介

我們先看一下基本的MVP的原理圖

在這種模式下,Presenter不可避免的需要持有View的引用,同時View也不可避免的需要持有Presenter,當然,這是MVP模式本身的一個特性,一個Model和一個Presenter還有一個View構成了MVP模式的最小單元。但是在工作中的業務不可能總是能侷限於某種模式,所以我們需要變化,需要更適合業務的模式。

假如我們思考這樣一個問題,我們有一個魚塘用來養魚,Presenter裏面有個叫(捕魚)fishing的方法提供給View,後來由於市場原因,魚不好賣了,我們開始在魚塘裏養蝦,那麼,捕魚這個方法要改名字,要改成(捕蝦)shrimp。大家一想,這個很簡單啊,用編輯器的ReName一下就搞定了。但是這個改動的地方可不只是Presenter,還有View,因爲View以前是調用Presenter的fishing方法,現在要改成調用Presenter的shrimp方法了,代碼肯定會發生變化。

那麼有沒有一種辦法,讓View和Presenter互相不關注他們之間聯繫的方法的名字,而只關心對方提供的功能呢?答案是肯定的,那就是一定有這樣的方法,並且不止一種,那麼我們今天要談的就是其中的一種。先看一下原理圖

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-cV9GeB5m-1572250453736)(https://ykbjson.github.io/blogimage/mvppicture4/mmvp.png)]

我們不妨讓一箇中間件來集中管理View和Presenter的關係,在View層用一個註解告訴中間件我要綁定哪個或哪幾個Presenter,View和Presenter提供的每一個業務方法都用一個唯一標識來表示,Presenter和View之間某個方法的調用就是通過中間件發送一個包含某個方法持有的特定標識的Action來找到某個方法,而方法的調用是基於註解提供的標識,所以可以不關心彼此提供的方法的名稱。這似乎很難以理解,也很難想象,那麼,我就換種方式闡述一下。

我們把這個中間件看成一個Presenter和View的功能映射關係提供者即可,這個這個中間件提供一個註冊View的方法,然後根據View的需求(即View上的註解)去生成對應的Presneter,並把他們的關係存儲起來,然後提供一個可以發送一個特定Action的方法,這個Action裏面有一個發送者的class、接收者class還有一個code和一些參數,這個code應着接收者裏某個方法註解上的code,當中間件通過剛纔存儲的關係,找到發送者的class和接收者class的關係後,然後根據Action裏面的code決定執行接收者的哪個方法。這所有的邏輯都在中間件裏面完成,無需外部實現,View和Presenter的關係就變得更模糊了。

如果說文字闡述不清楚這個原理,那麼就只能上代碼了,代碼是最好的老師…(真的嗎???)

二、代碼實現

庫代碼結構圖

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uIRnD5UU-1572250453736)(https://ykbjson.github.io/blogimage/mvppicture4/mmvpjiegou.png)]

接下來,我們一個一個的來爲大家解釋每一個類的定義與作用,我們先從那兩個註解開始吧。

BindPresenter,顧名思義,就是綁定Presneter

/**
 * 包名:com.ykbjson.lib.mmvp.internal
 * 描述:View綁定與Presenter的註解
 * 創建者:yankebin
 * 日期:2018/4/12
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindPresenter {
    /**
     * View需要綁定的Presenter數組
     */
    Class<? extends MMVPPresenter>[] value();
}

這裏爲什麼是一個數組呢?因爲嚴格意義上來講,MVP模式下,Presenter和View是一對一的關係,但是我在想,既然View和Presenter的關係已經變得模糊了,那麼我是否可以把Presenter當成一種或一類功能的提供者,供不同的View綁定和調用。但是這樣似乎又違背了MVP模式的定義,所以先暫且聲明成一個數組吧。

ActionProcess,這個可能不太好理解,因爲我也沒有想好特別能解釋它作用的名字

/**
 * 包名:com.ykbjson.lib.mmvp.internal
 * 描述:Presenter或View提供的可供反射調用的方法的註解
 * 創建者:yankebin
 * 日期:2018/4/12
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionProcess {
    /**
     * action
     **/
    String value() default "";

    /**
     * 是否需要{@link com.ykbjson.lib.mmvp.MMVPAction}參數
     **/
    boolean needActionParam() default false;

    /**
     * 是否需要轉換{@link com.ykbjson.lib.mmvp.MMVPAction},即交換sourceClass和targetClass
     **/
    boolean needTransformAction() default false;

    /**
     * 是否需要{@link com.ykbjson.lib.mmvp.MMVPAction}裏面的param參數
     **/
    boolean needActionParams() default false;

}

其實這就是剛纔我所說的“Presenter和View之間某個方法的調用就是通過中間件發送一個包含某個方法持有的特定標識的Action來找到某個方法,而方法的調用是基於註解提供的標識”,那個註解就是ActionProcess,裏面的value對應我所說的code(在代碼設計的時候我還是命名成了action),其他的都有詳細的註釋,後面Demo裏也會用到,這裏我就不在贅述了。總之,這個註解就是註冊於方法之上,告訴中間件,我註冊了某個action,需要些什麼參數,然後你就可以調用我了。

既然都說到了Action,那我們就繼續看看他的內部實現。

MMVPAction,很明顯,這是一個動作或者叫做行爲

/**
 * 包名:com.ykbjson.lib.mmvp
 * 描述:View和Presenter之間的通信攜帶
 * 創建者:yankebin
 * 日期:2018/4/12
 */
public class MMVPAction implements Serializable, Cloneable {
    private Class<?> sourceClass;
    private Class<?> targetClass;
    private MMVPActionDescription action;
    private Map<String, Object> params;
    private IMMVPOnDataCallback onDataCallback;

    MMVPAction(Class<?> sourceClass, Class<?> targetClass) {
        this.sourceClass = sourceClass;
        this.targetClass = targetClass;
    }

    public MMVPAction setSourceClass(Class<?> sourceClass) {
        this.sourceClass = sourceClass;
        return this;
    }

    public MMVPAction setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        return this;
    }

    public MMVPAction setAction(MMVPActionDescription action) {
        this.action = action;
        return this;
    }

    public MMVPAction setParams(Map<String, Object> params) {
        this.params = params;
        return this;
    }

    public MMVPAction setOnDataCallback(IMMVPOnDataCallback onDataCallback) {
        this.onDataCallback = onDataCallback;
        return this;
    }


    public Class<?> getSourceClass() {
        return sourceClass;
    }


    public Class<?> getTargetClass() {
        return targetClass;
    }


    public MMVPActionDescription getAction() {
        return action;
    }


    public Map<String, Object> getParams() {
        return params;
    }

    public IMMVPOnDataCallback getOnDataCallback() {
        return onDataCallback;
    }

    public void send() {
        send(0);
    }

    public void send(long delayMills) {
        MMVPArtist.sendAction(this, delayMills);
    }

    public MMVPAction transform() {
        Class<?> exchange = getTargetClass();
        setTargetClass(getSourceClass());
        setSourceClass(exchange);
        return this;
    }

    public MMVPAction clearParam() {
        if (null != params && !params.isEmpty()) {
            params.clear();
        }

        return this;
    }

    public MMVPAction putParam(String key, Object value) {
        if (null == params) {
            params = new HashMap<>();
        }
        params.put(key, value);
        return this;
    }

    public <T> T getParam(String key) {
        if (null == params || params.isEmpty()) {
            return (T) null;
        }
        return (T) params.get(key);
    }

    void recycle() {
        sourceClass = null;
        targetClass = null;
        action = null;
        params = null;
        onDataCallback = null;
    }
}

其實我知道,如果把它設計成一個接口,那麼擴展性會好一些,但是,我還沒有想好該如何去把它抽的很完美,所以就留給大家發揮啦。這個Action很重要,它包含了一次行爲或動作裏,發起者是誰,接收者是誰,需要什麼參數。至於MMVPActionDescription,這是對Action實際需要傳達的動作的封裝,本來就是一個字符串,但是我在想,萬一一個字符串不夠表達一個行爲該怎麼辦呢?所以就設計成了一個對象,裏面就倆字段

/**
 * 包名:com.ykbjson.lib.mmvp
 * 描述:View和Presenter之間的通信攜帶描述
 * 創建者:yankebin
 * 日期:2018/4/13
 */
public class MMVPActionDescription implements Serializable {
    private String action;
    private String code;

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

是不是非常簡潔,根本沒有必要設計成對象。

至於IMMVPOnDataCallback,我是這樣想的。因爲後面大家會看到中間件執行這些方法的時候,都是通過反射執行的,如果不想用反射執行,也不想用這個Action註解方法,而僅僅是需要通過一個Callback把數據返回給我就可以了,那麼,這個IMMVPOnDataCallback就有了用處。比如,我在View裏發送了一個Action,Action裏帶着一個IMMVPOnDataCallback,當中間件解析完這個Action後,執行Presenter的方法,Presenter可以根據收到的Action裏是否有IMMVPOnDataCallback,如果有,就不用在回傳Action,而是直接用這個Callback回傳請求到的數據,這樣,至少就了一次反射執行View裏的方法。

/**
 * 包名:com.ykbjson.lib.mmvp
 * 描述:數據回調接口
 * 創建者:yankebin
 * 日期:2018/4/12
 */
public interface IMMVPOnDataCallback<T> extends Serializable{
    void onSuccess(T data);

    void onError(String msg);
}

接下來我們來看一下IMMVPActionHandler

/**
 * 包名:com.ykbjson.lib.mmvp
 * 描述:{@link MMVPAction}處理接口
 * 創建者:yankebin
 * 日期:2018/4/13
 */
public interface IMMVPActionHandler {

    void handleAction(@NonNull MMVPAction action);

    @NonNull
    IMMVPActionHandler get();
}

爲什麼會有這麼個接口呢?我是這麼想的。如果你不想在View或Presneter的方法上加上Action註解,那麼也沒有關係,我們的View和Presenter基類都是實現了這個接口的,你可以在View或Presenter的handleAction方法裏調用任何你想調用的代碼(我是不是很機智_)。

至於View和Presenter的代碼,我都不想貼,又不得不貼

/**
 * 包名:com.ykbjson.lib.mmvp
 * 描述:Presenter接口
 * 創建者:yankebin
 * 日期:2018/4/12
 */
public interface MMVPPresenter extends IMMVPActionHandler {

}

你看看,Presneter空空如也。在看看View

/**
 * 包名:com.ykbjson.lib.mmvp
 * 描述:View接口
 * 創建者:yankebin
 * 日期:2018/4/12
 */
public interface MMVPView extends IMMVPActionHandler {
    String getScope();
}

View這裏就多了一個方法,關於這個方法,稍微給大家解釋一下。最開始的時候,我所構思的解耦方式設這樣的。View和Presenter屬於一個作用域,只有作用域相同的View和Presenter纔可以交互,可是我思來想去,這個笨拙的腦袋也沒有想出什麼好的方法去實現作用域這個概念,所以…請大家原諒我,以及這笨拙的腦袋。

接下來就是這重頭戲了——MMVPArtist,這個類有點長,有多長呢?

/**
 * 包名:com.ykbjson.lib.mmvp
 * 描述:View和Presenter層交互處理器
 * 創建者:yankebin
 * 日期:2018/4/12
 */
public final class MMVPArtist {
    private static final String TAG = "MMVPArtist";

    private static final int FLAG_HANDLE_ACTION = 100000;
    /**
     * 註冊View緩存
     */
    private static final List<MMVPView> VIEW_CACHE = new LinkedList<>();
    /**
     * View和Presenter關係緩存
     */
    private static final Map<Class<?>, List<MMVPPresenter>> VIEW_PRESENTERS_CACHE = new LinkedHashMap<>();

    /**
     * 註冊了{@link ActionProcess}註解的方法緩存
     */
    private static final Map<Class<?>, Map<String, Method>> METHODS_CACHE = new LinkedHashMap<>();

    private static boolean enableLog = true;

    @SuppressLint("HandlerLeak")
    private static Handler dispatchActionHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case FLAG_HANDLE_ACTION:
                    MMVPAction action = (MMVPAction) msg.obj;
                    handleAction(action);
                    break;
            }
        }
    };

    private MMVPArtist() {
        throw new AssertionError("No instances.");
    }

    /**
     * 日誌開關
     *
     * @param enableLog
     */
    public static void setEnableLog(boolean enableLog) {
        MMVPArtist.enableLog = enableLog;
    }

    /**
     * 創建 {@link MMVPAction}
     *
     * @param sourceClass 創建Action的class
     * @param targetClass 接收Action的class
     * @param action      需要執行的操作
     * @return {@link MMVPAction}
     */
    public static MMVPAction buildAction(Class<?> sourceClass, Class<?> targetClass, String action) {
        MMVPActionDescription actionContent = new MMVPActionDescription();
        actionContent.setAction(action);
        return new MMVPAction(sourceClass, targetClass).setAction(actionContent);
    }

    /**
     * 發送HVPAction
     *
     * @param action {@link MMVPAction}
     */
    static void sendAction(@NonNull MMVPAction action) {
        sendAction(action, 0);
    }

    /**
     * 發送HVPAction
     *
     * @param action     {@link MMVPAction}
     * @param delayMills 延時毫秒數
     */
    static void sendAction(@NonNull MMVPAction action, long delayMills) {
        if (null == action.getAction()
                || TextUtils.isEmpty(action.getAction().getAction())
                || null == action.getSourceClass()
                || null == action.getTargetClass()) {
            throw new IllegalArgumentException("Invalid MMVPAction");
        }
        Message message = dispatchActionHandler.obtainMessage(FLAG_HANDLE_ACTION, action);
        dispatchActionHandler.sendMessageDelayed(message, delayMills);
    }


    /**
     * 處理HVPAction
     *
     * @param action {@link MMVPAction}
     */
    private static void handleAction(@NonNull MMVPAction action) {
        Class<?> sourceClass = action.getSourceClass();
        if (MMVPPresenter.class.isAssignableFrom(sourceClass)) {
            handleActionFromPresenter(action);
        } else if (MMVPView.class.isAssignableFrom(sourceClass)) {
            handleActionFromView(action);
        } else {
            throw new IllegalArgumentException("Invalid class type of the MMVPAction's targetClass and sourceClass");
        }
    }

    /**
     * 處理Presenter發送來的Action
     *
     * @param action {@link MMVPAction}
     */
    private static void handleActionFromPresenter(MMVPAction action) {
        if (VIEW_CACHE.isEmpty()) {
            if (enableLog) {
                Log.d(TAG, " Can not find the MMVPAction's targetClass [ " +
                        action.getTargetClass().getName() + " ],because the VIEW_CACHE is empty");
            }
            return;
        }
        IMMVPActionHandler find = null;
        for (MMVPView hvpView : VIEW_CACHE) {
            if (hvpView.getClass().equals(action.getTargetClass())) {
                find = hvpView;
                break;
            }
        }
        if (null == find) {
            if (enableLog) {
                Log.w(TAG, " Can not find the MMVPAction's targetClass [ " +
                        action.getTargetClass().getName() + " ] , it is not registered or has been destroyed ");
            }
            return;
        }
        if (!execute(action, find)) {
            find.handleAction(action);
        }
    }

    /**
     * 處理View發送來的Action
     *
     * @param action {@link MMVPAction}
     */
    private static void handleActionFromView(MMVPAction action) {
        if (!VIEW_PRESENTERS_CACHE.containsKey(action.getSourceClass())) {
            if (enableLog) {
                Log.w(TAG, " The MMVPAction's sourceClass [ " + action.getSourceClass().getName() +
                        " ]  is not registered  or has been destroyed ");
            }
            return;
        }
        List<MMVPPresenter> hvpPresenterList = VIEW_PRESENTERS_CACHE.get(action.getSourceClass());
        if (null == hvpPresenterList || hvpPresenterList.isEmpty()) {
            if (enableLog) {
                Log.w(TAG, "PresenterList is empty ,have you ever add annotation BindPresenter" +
                        " for this view [ " + action.getSourceClass().getName() + " ] ?");
            }
            return;
        }
        IMMVPActionHandler find = null;
        for (MMVPPresenter hvpPresenter : hvpPresenterList) {
            if (action.getTargetClass().equals(hvpPresenter.getClass())) {
                find = hvpPresenter;
                break;
            }
        }

        if (null == find) {
            if (enableLog) {
                Log.w(TAG, " Can not find the MMVPAction's targetClass [ "
                        + action.getTargetClass().getName() + " ] ");
            }
            return;
        }
        if (!execute(action, find)) {
            find.handleAction(action);
        }
    }

    /**
     * 執行action裏目標類需要執行的方法
     *
     * @param action {@link MMVPAction}
     * @param find   {@link MMVPView}或{@link MMVPPresenter}
     * @return
     */
    private static boolean execute(MMVPAction action, IMMVPActionHandler find) {
        Method executeMethod = findRegisterMMVPActionMethod(action);
        if (null == executeMethod) {
            if (enableLog) {
                Log.d(TAG, " Find " + find.getClass().getName() + "'s execute method failure");
            }
            return false;
        }
        if (enableLog) {
            Log.d(TAG, " Find  method " + find.getClass().getName() + "." + executeMethod.getName() + " success");
        }

        List<Object> paramList = new ArrayList<>();
        ActionProcess methodAnnotation = executeMethod.getAnnotation(ActionProcess.class);
        if (methodAnnotation.needActionParam()) {
            if (methodAnnotation.needTransformAction()) {
                action = action.transform();
            }
            paramList.add(action);
        }
        if (methodAnnotation.needActionParams() && null != action.getParams() && !action.getParams().isEmpty()) {
            for (String key : action.getParams().keySet()) {
                paramList.add(action.getParam(key));
            }
        }
        Object[] params = paramList.isEmpty() ? null : paramList.toArray();
        try {
            executeMethod.setAccessible(true);
            executeMethod.invoke(find, params);
            if (enableLog) {
                Log.d(TAG, " Execute "
                        + find.getClass().getName() + "." + executeMethod.getName() + " success");
            }
            return true;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            if (enableLog) {
                Log.d(TAG, " Execute "
                        + action.getTargetClass().getName() + "." + executeMethod.getName() + " failure", e);
            }
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            if (enableLog) {
                Log.d(TAG, " Execute "
                        + action.getTargetClass().getName() + "." + executeMethod.getName() + " failure", e);
            }
        }

        return false;
    }

    /**
     * 註冊View
     *
     * @param view {@link MMVPView}
     */
    @UiThread
    public static void registerView(@NonNull MMVPView view) {
        if (VIEW_CACHE.contains(view)) {
            if (enableLog) {
                Log.w(TAG, " ReRegister [ " + view.getClass().getName() + " ]");
            }
            return;
        }
        BindPresenter annotation = view.getClass().getAnnotation(BindPresenter.class);
        if (null == annotation) {
            throw new IllegalArgumentException("Can not find the annotation : BindPresenter, for [ "
                    + view.getClass().getName() + " ] ");
        }
        Class<? extends MMVPPresenter> presenterClasses[] = annotation.value();
        if (presenterClasses.length < 1) {
            throw new IllegalArgumentException(" Invalid presenter size for [ " + view.getClass().getName() + " ]");
        }
        final LinkedList<MMVPPresenter> presenterLinkedList = new LinkedList<>();
        for (Class<? extends MMVPPresenter> presenterClass : annotation.value()) {
            try {
                MMVPPresenter presenter = presenterClass.newInstance();
                presenterLinkedList.add(presenter);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        VIEW_CACHE.add(view);
        VIEW_PRESENTERS_CACHE.put(view.getClass(), presenterLinkedList);
        if (enableLog) {
            Log.d(TAG, " RegisterView [ " + view.getClass().getName() + " ]");
        }
    }


    /**
     * 註銷View
     *
     * @param view {@link MMVPView}
     */
    @UiThread
    public static void unregisterView(@NonNull MMVPView view) {
        VIEW_CACHE.remove(view);
        for (Class<?> clazz : VIEW_PRESENTERS_CACHE.keySet()) {
            List<MMVPPresenter> presenterList = VIEW_PRESENTERS_CACHE.get(clazz);
            if (null == presenterList || presenterList.isEmpty()) {
                continue;
            }
            for (MMVPPresenter presenter : presenterList) {
                METHODS_CACHE.remove(presenter.getClass());
            }
        }
        METHODS_CACHE.remove(view.getClass());
        VIEW_PRESENTERS_CACHE.remove(view.getClass());
        if (enableLog) {
            Log.d(TAG, " unregisterView [ " + view.getClass().getName() + " ]");
        }
    }


    /**
     * 獲取某個Presenter
     *
     * @param viewClass      當前viewClass
     * @param presenterClass 需要的presenterClass
     * @param <T>            需要的presenter
     * @return 需要的presenter
     */
    @Nullable
    public static <T extends MMVPPresenter> T getPresenter(@NonNull Class<? extends MMVPView> viewClass,
                                                           @NonNull Class<? extends MMVPPresenter> presenterClass) {
        List<MMVPPresenter> presenterList = VIEW_PRESENTERS_CACHE.get(viewClass);
        if (null == presenterList || presenterList.isEmpty()) {
            return (T) null;
        }
        for (MMVPPresenter presenter : presenterList) {
            if (presenterClass.equals(presenter.getClass())) {
                return (T) presenter;
            }
        }
        return (T) null;
    }


    /**
     * 找到某個類裏註冊了{@link ActionProcess}的方法,該方法註冊的action爲傳入的{@link MMVPAction}裏的action
     *
     * @param action {@link MMVPAction}
     * @return
     */
    private static Method findRegisterMMVPActionMethod(@NonNull MMVPAction action) {
        final Class<?> targetClass = action.getTargetClass();
        final String methodKey = String.format("%s_$$_$$_%s", targetClass.getCanonicalName(),
                action.getAction().getAction());
        Map<String, Method> methodMap = METHODS_CACHE.get(targetClass);
        Method executeMethod = null;
        if (null != methodMap && !methodMap.isEmpty()) {
            executeMethod = methodMap.get(methodKey);
        }
        if (null == executeMethod) {
            Method[] methods = targetClass.getMethods();
            for (Method method : methods) {
                ActionProcess methodAnnotation = method.getAnnotation(ActionProcess.class);
                if (null == methodAnnotation) {
                    continue;
                }
                if (!TextUtils.equals(methodAnnotation.value(), action.getAction().getAction())) {
                    continue;
                }
                executeMethod = method;
                break;
            }
            if (null != executeMethod) {
                if (null == methodMap) {
                    methodMap = new LinkedHashMap<>();
                    METHODS_CACHE.put(targetClass, methodMap);
                }
                methodMap.put(methodKey, executeMethod);
            }
        }
        return executeMethod;
    }
}

不到400行,短小精悍。方法都有註釋,命名還算規範吧,後面還會給項目的Github鏈接地址,所以這裏就不打算詳述其功能了,大致看一下他們的類圖結構吧

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-IpwvdnmQ-1572250453737)(https://ykbjson.github.io/blogimage/mvppicture4/mmvpuml.png)]

最後,整個框架執行的大致的流程如下:

  1. View通過registerView方法註冊到MMVPArtist裏面,然後MMVPArtist根據View的註解,生成對應的Presenter,並保存到一個和View有關的Map裏。

  2. View調用MMVPArtist的buildAction方法,創建一個MMVPAction,設置好參數,然後調用MMVPAction的send方法,發送到MMVPArtist裏。

  3. MMVPArtist根據收到的MMVPAction裏的sourceClass判斷,是View發起的請求還是Presenter發起的請求,如果是View發起的,那就調用handleAcctionFromView,如果是Presenter發起的,則調用handleActionFromPresenter。

  4. 在handleAcctionFromView方法裏,會找到一個Class是MMVPAction裏指定的targetClass的Pressenter,在handleActionFromPresenter方法裏,會找到一個Class是MMVPAction裏指定的targetClass的View,最終都會執行execute方法.

  5. 在execute方法裏,會通過findRegisterMMVPActionMethod方法,找到一個View或Presenter用AcctionProcess註解的方法,然後根據註解裏的屬性,構造該方法需要的參數,反射執行該方法。如果execute方法執行失敗(如有異常之類的),那麼就會執行Presenter或View的handleAction方法,這個方法繼承至IMMVPActionHandler。

  6. 如果是View發起的調用請求,Presenter收到該MMVPAction並執行對應的方法之後,要通知發起該動作的View,Presenter只需要交換一下MMVPAction的targetClass和sourceClass,並重設MMVPAction的參數,然後調用MMVPAction的send方法,即可傳遞給最初動作的發起者View,完成一次信息傳遞。當然MMVPAction裏還有一個IMMVPOnDataCalllback,Presenter也可以直接把數據傳遞給該Callback,捨棄MMVPAction。

  7. 在View銷燬的時候,調用MMVPArtist的unregister方法,移除MMVPArtist裏關於View的緩存。

三、存在的一些問題

  1. 如果大家都用過EventBus的話,可能就會覺得我這麼做顯得很多餘,那些註解與Action分發,完全可以用EventBus框架完成。但是我這個思路和EventBus還是有所區別的,至少在針對Action的處理上,不會在發送一個Action後,所有註冊該Action的方法都會調用,只有和某個View有關聯的Presneter裏的方法纔會被篩選調用。

  2. 就MVP模式而言,這種設計雖然讓View和Presenter的關係變得更模糊,但是Presenter和View的設計就有點背離MVP模式了,這不是最初的目的。

  3. 基於反射調用,性能問題總是不可避免,但是這裏其實也可以像Butterknife一樣,用APT技術去解決性能問題,等後面我研究完了再和大家分享。

嗯,暫時我這個笨腦袋能想到的就這麼多,如果大家發現了別的什麼問題,請不吝賜教,讓我可以吃一塹長一智,謝謝。

最後,是項目的Github地址

Demo地址

Library地址

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