定義:定義對象之間的一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於他的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