android 源碼設計模式讀書筆記(七)觀察者模式+註解

定義:定義對象之間的一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於他的UI依賴於他的對象都會得到通知並自動更新。
使用場景:
(1)關聯行爲場景,需要注意的是,關聯行爲是可拆分的,而不是組合關係
(2)事件多級觸發場景
(3)事件總線處理機制
基礎UML



關於註解
定義:註解用於爲Java提供元數據,作爲元數據,註解不影響代碼執行,但某些類型註解也可以用於這一目的,註解從Java5開始引入
註解語法

@Retention(RetentionPolicy.CLASS)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface MyInterface {}

元註解
元註解是可以註解到註解上的註解,簡單來說就是一種基本註解,可以作用到其他註解上。

Java中總共有5中元註解:@Retention,@Documented,@Target,@Inherited,@Repeatable。

@Retention

用來說明註解的存活時間,有三種取值:
RetentionPolicy.SOURCE:註解只在源碼階段保留,編譯器開始編譯時它將被丟棄忽視

RetentionPolicy.CLASS:註解會保留到編譯期,但運行時不會把它加載到JVM中

RetentionPolicy.RUNTIME:註解可以保留到程序運行時,它會被加載到JVM中,所以程序運行過程中可以獲取到它們

@Target

指定註解可作用的目標
ElementType.PACKAGE:可作用在包上
ElementType.TYPE:可作用在類、接口、枚舉上
ElementType.ANNOTATION_TYPE:可以作用在註解上
ElementType.FIELD:可作用在屬性上
ElementType.CONSTRUCTOR:可作用在構造方法上
ElementType.METHOD:可作用在方法上
ElementType.PARAMETER:可作用在方法參數上
ElementType.LOCAL_VARIABLE:可作用在局部變量上,例如方法中定義的變量

@Documented

這個註解跟文檔相關,它的作用是能夠將註解中的元素包含到Javadoc中去。

@Inherited

Inherited是繼承的意思,但並不是註解本身可被繼承,而是指一個父類SuperClass被該類註解修飾,那麼它的子類SubClass如果沒有任何註解修飾,就會繼承父類的這個註解。

@Repeatable

可重複的意思.
實例 :準備利用註解和觀察者模式 試下一個類似EventBus的事件總線處理機制。
EventBus 事件模型



具體實現
MyBus實現類

public class MyBus {
    private static volatile MyBus myBus;
    //保存帶註解的方法
    private Map<Object, List<SubscriberInfo>> cacheMap = new HashMap<>();
    private ExecutorService executorService;
    private Handler handler;

    private MyBus() {
        handler = new Handler(Looper.getMainLooper());
        executorService = Executors.newCachedThreadPool();
    }

    //單例初始化
    public static MyBus getInstance() {
        if (myBus == null) {
            synchronized (MyBus.class) {
                if (myBus == null) {
                    myBus = new MyBus();
                }
            }
        }
        return myBus;
    }

    /***
     * 註冊方法
     * @param obj
     */
    public void register(Object obj) {
        List<SubscriberInfo> list = cacheMap.get(obj);
        if (list == null) {
            list = findAnnotationMethod(obj);
            cacheMap.put(obj, list);
        }
    }

    /**
     * 尋找帶註解的方法
     *
     * @param obj
     * @return
     */
    private List<SubscriberInfo> findAnnotationMethod(Object obj) {
        List<SubscriberInfo> list = new ArrayList<>();
        Class<?> clazz = obj.getClass();
        //獲取所有的方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            Subscriber subscriber = method.getAnnotation(Subscriber.class);
            if (subscriber == null) continue;
            checkMethod(method);
            SubscriberInfo subscriberInfo = new SubscriberInfo(method.getParameterTypes()[0], subscriber.threadMode(), method, obj);
            list.add(subscriberInfo);
        }
        return list;
    }

    /**
     * 檢查方法是否符合規則
     *
     * @param method
     */
    private void checkMethod(Method method) {
        //方法返回必須爲void
        if (!TextUtils.equals("void", method.getGenericReturnType().toString())) {
            throw new RuntimeException("method must return void ");
        }
        //方法參數校驗
        Class<?>[] paramsTypes = method.getParameterTypes();
        if (paramsTypes.length != 1) {
            throw new RuntimeException("method must has one params");
        }
    }

    /**
     * 清除保存的註解
     *
     * @param getter
     */
    public void unRegister(Object getter) {
        if (cacheMap.containsKey(getter)) {
            cacheMap.remove(getter);
        }
    }

    /**
     * 發送信息
     *
     * @param send
     */
    public static void post(Object send) {
        Set<Object> set = myBus.cacheMap.keySet();
        for (final Object obj : set) {
            List<SubscriberInfo> subscriberInfos = myBus.cacheMap.get(obj);
            if (subscriberInfos != null) {
                for (final SubscriberInfo subscriberInfo : subscriberInfos) {
                    //判斷這個類是否爲SubscriberInfo的子類
                    if (subscriberInfo.type.isAssignableFrom(send.getClass())) {
                        execute(subscriberInfo, send);
                    }
                }
            }
        }
    }
    //線程調度
    private static void execute(final SubscriberInfo subscriberInfo, final Object send) {
        switch (subscriberInfo.threadMode) {
            case MAIN:
                if (Looper.getMainLooper() == Looper.myLooper()) {
                    invoke(subscriberInfo, send);
                } else {
                    myBus.handler.post(new Runnable() {
                        @Override
                        public void run() {
                            invoke(subscriberInfo, send);
                        }
                    });
                }
                break;
            case POSTING:
                invoke(subscriberInfo, send);
                break;
            case BACKGROUND:
                if (Looper.getMainLooper() == Looper.myLooper()) {
                    myBus.executorService.execute(new Runnable() {
                        @Override
                        public void run() {
                            invoke(subscriberInfo, send);
                        }
                    });
                } else {
                    invoke(subscriberInfo, send);
                }
                break;
            case ASYNC:
                myBus.executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        invoke(subscriberInfo, send);
                    }
                });
                break;
        }

    }

    /**
     * 執行註解方法
     *
     * @param info   方法封裝對象
     * @param setter 消息對象的封裝
     */
    private static void invoke(SubscriberInfo info, Object setter) {
        try {
            info.method.setAccessible(true);
            info.method.invoke(info.object, setter);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

自定義註解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Subscriber {
    /**
     * 在哪個線程執行註解方法
     */
    ThreadMode threadMode() default ThreadMode.POSTING;
}

枚舉線程

public enum ThreadMode {
    POSTING,//發送者的線程
    MAIN,
    BACKGROUND,
    ASYNC//異步線程
}

訂閱實體類

/**
 * 訂閱對象
 */
public class SubscriberInfo {
    public Object object;
    //消息類型
    public Class<?> type;
    // 回調線程
    public ThreadMode threadMode;
    //回調方法
    public Method method;


    public SubscriberInfo(Class<?> type, ThreadMode threadMode, Method method,Object object) {
        this.type = type;
        this.threadMode = threadMode;
        this.method = method;
        this.object = object;
    }
}

這樣就完成了
最終demo github:https://github.com/525642022/MyBus

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