Logic-x 簡單好用 MVP 框架

大家好,我是小新
https://github.com/racofix

MVP,顧名思義:
View 對應(yīng)于Activity,fragment,負(fù)責(zé)View的繪制以及與用戶交互
Model 依然是業(yè)務(wù)邏輯和實(shí)體模型
Presenter 負(fù)責(zé)完成View于Model間的交互

優(yōu)點(diǎn)我就不介紹了,缺點(diǎn)可能都我一樣的困擾:

  • M/P 層都需要定義接口和實(shí)現(xiàn)類,創(chuàng)建對象占用內(nèi)存
  • 每一個(gè)頁面都需要?jiǎng)?chuàng)建P實(shí)例并綁定View(重復(fù)代碼)
  • P層邏輯比較多的時(shí)候,開發(fā)時(shí)不容易考慮其他頁面調(diào)用,復(fù)用難
  • 頁面銷毀,P層執(zhí)行異步操作,持有的View 引用造成內(nèi)存泄露,程序崩潰

我們既然知道它的缺點(diǎn)了,那我們就想辦法解決

  • 減少接口和類的定義,數(shù)據(jù)模型定義為Model,數(shù)據(jù)復(fù)雜的情況引入 Repository
  • 利用用注解和反射,自動實(shí)現(xiàn) P層實(shí)例化和View綁定/解綁
  • P層邏輯范圍減少,頁面(Activity/Fragment)可以多次復(fù)用,低耦合高復(fù)用
  • 生成代理View,頁面銷毀不需要 getView()!=null 并不會造成內(nèi)存泄露

Usage

Talk is cheap. Show me the code.

P層(邏輯)

public interface LoginI {
    interface Logic {
        void sign_in(String username, String password);
    }
    interface View {
        void sign_in_success();
    }
}
public class Login extends BaseLogicImpl<LoginI.View> implements LoginI.Logic {
    @Override
    public void sign_in(String username, String password) {
            if(successfully) getView().sign_in_success();
    }
}

V層(Activity/Fragment)

獲取P實(shí)例 getLogic(Login.class)
縮小邏輯層的方法范圍,降低到最小力度,比如說:登錄頁需要登錄和注冊功能,注冊頁需要注冊功能。
那么登錄和注冊就可以作為邏輯層來實(shí)現(xiàn),那登錄頁實(shí)現(xiàn) 登錄/注冊邏輯,注冊頁實(shí)現(xiàn)注冊邏輯。

@LogicArr({Login.class, Register.class})
public class LoginActivity extends BaseLogicActivity implements
        LoginI.View, RegisterI.View {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getLogic(Login.class).sign_in("用戶名", "密碼");
    }
    @Override
    public void sign_in_success() {
        Toast.makeText(this, "登錄成功", Toast.LENGTH_SHORT).show();
    }
    @Override
    public void sign_up_success() {
        Toast.makeText(this, "注冊成功", Toast.LENGTH_SHORT).show();
    }
}
@LogicArr(Register.class)
public class RegisterActivity extends BaseLogicActivity
        implements RegisterI.View {
    @Override
    public void sign_up_success() {
        Toast.makeText(this, "注冊成功", Toast.LENGTH_SHORT).show();
    }
}

這樣,就實(shí)現(xiàn)一個(gè)使用簡單,結(jié)構(gòu)清晰的MVP結(jié)構(gòu)。
解決這些缺點(diǎn)思考了很久,如果你覺得新穎、實(shí)用、可以幫到您的話,給一份關(guān)注和鼓勵(lì)。
同樣,歡迎各位大神相互指導(dǎo)改進(jìn)。準(zhǔn)備做一些常用的框架 Things2

更多示例:https://github.com/racofix/Basic
框架實(shí)現(xiàn):https://github.com/Things2/Logic-x

設(shè)計(jì)思路

核心類 LogicProvider

利用注解 @LogicArr 返回邏輯數(shù)組類,利用反射初始化邏輯類,然后綁定/解綁View放入Map中
上層通過 getLogic(xxx.class) 從Map中獲取邏輯實(shí)例,頁面銷毀邏輯實(shí)例會自動從Map中釋放

class LogicProvider {
    private static volatile LogicProvider logicProvider;
    private Map<Object, HashSet<BaseLogic>> logicCaches = new LinkedHashMap<>();
    static LogicProvider getInstance() {
        if (logicProvider == null) {
            synchronized (LogicProvider.class) {
                if (logicProvider == null) {
                    logicProvider = new LogicProvider();
                }
            }
        }
        return logicProvider;
    }
    <V> void put(V view) {
        LogicArr logicArr = view.getClass().getAnnotation(LogicArr.class);
        if (logicArr == null) return;
        if (logicCaches.containsKey(view)) return;
        HashSet<BaseLogic> logics = new HashSet<>();
        Class[] classes = logicArr.value();
        for (Class clazz : classes) {
            try {
                BaseLogic<V> baseLogic = Util.castTo(clazz.newInstance());
                baseLogic.bindView(view);
                baseLogic.onLogicCreated();
                logics.add(baseLogic);
            } catch (IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        }
        if (logics.isEmpty()) return;
        logicCaches.put(view, logics);
    }
    <V, T extends BaseLogic> T get(V view, Class<T> clazz) {
        HashSet<BaseLogic> logics = logicCaches.get(view);
        if (logics == null || logics.isEmpty()) {
            throw new NullPointerException(view.getClass().getName() + " @LogicArr is empty.");
        }
        T baseLogic = null;
        for (BaseLogic logic : logics) {
            String logicName = logic.getClass().getName();
            if (logicName.equals(clazz.getName())) {
                baseLogic = Util.castTo(logic);
                break;
            }
        }
        return baseLogic;
    }
    <V> void remove(V view) {
        HashSet<BaseLogic> logics = logicCaches.get(view);
        if (logics == null || logics.isEmpty()) {
            return;
        }
        for (BaseLogic logic : logics) {
            if (logic.isViewBind()) logic.unbindView();
            logic.onLogicDestroy();
        }
        logicCaches.remove(view);
    }
}

核心類 BaseLogicImpl

利用WeakReference存儲View,便于View對象回收,使用動態(tài)代理 bindView(view) 生成代理View,
頁面銷毀時(shí),代理對象還存在,調(diào)用方法時(shí)候發(fā)現(xiàn)View是空了, 代理對象就什么都不做了. 這樣既不用判斷 getView()!=null 并且不會內(nèi)存泄露。

public class BaseLogicImpl<V> implements BaseLogic<V> {
    private V viewProxy;
    private WeakReference<V> weakReference;
    @Override
    public void bindView(final V view) {
        weakReference = new WeakReference<>(view);
        viewProxy = Util.castTo(
                Proxy.newProxyInstance(
                        view.getClass().getClassLoader(),
                        view.getClass().getInterfaces(),
                        new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                if (isViewBind()){
                                    return method.invoke(weakReference.get(), args);
                                }
                                return null;
                            }
                        }));
    }
    @Override
    public void unbindView() {
        if (isViewBind()) {
            weakReference.clear();
            weakReference = null;
        }
    }
    @Override
    public boolean isViewBind() {
        return weakReference != null && weakReference.get() != null;
    }
    @Override
    public V getView() {
        return viewProxy;
    }
}

最后

希望利用業(yè)余時(shí)間和工作的積累,貢獻(xiàn)更多有意義、有價(jià)值的項(xiàng)目。
如果有待改進(jìn),請大神們多多指導(dǎo)。
感謝大家耐心的閱讀,如果項(xiàng)目對你有所幫助,希望大家給個(gè)關(guān)注,大家一起進(jìn)步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容