背景:
Android組件之間通訊的方式有很多,廣播 、intent 、handler、接口等這些都可以實現(xiàn)。網(wǎng)上開源框架EventBus是一款針對Android優(yōu)化的發(fā)布/訂閱事件總線。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,線程之間傳遞消息.優(yōu)點是開銷小,代碼更優(yōu)雅。以及將發(fā)送者和接收者解耦。通過EventBus源碼自己實現(xiàn)線程之間消息傳遞。
1.介紹EventButs的使用方法:
一:注冊EventButs事件:
EventBus.getDefault().register(this);
二:監(jiān)聽EventButs事件:
@Subscribe(threadMode = ThreadMode.MAIN)
public void getEvent(EventBean bean){
Log.e("====>", "thread = " + Thread.currentThread().getName());
Log.e("======> ",bean.toString());
}
三:發(fā)送EventButs事件:
EventBus.getDefault().post(new EventBean("1111","22222"));
通過上面的代碼就可以實現(xiàn)事件消息在組件之間的傳遞,下面我將不改變開源框架的代碼格式,自己去實現(xiàn)EventButs:
第一步:定義注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) // 注解會在class字節(jié)碼文件中存在,jvm在加載時可以通過反射的方式獲取該注解的內(nèi)容
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.MAIN;
}
上面添加注解的枚舉類:定義的兩種類型一種指定主線程另一種為子線程
ublic enum ThreadMode {
MAIN,
BACKGROUND
}
第二步:注冊事件
public void register(Object obj){
// 先去數(shù)據(jù)源中獲取subcribleMethods,如果不存在,則去尋找方法并添加
List<SubscribleMethod> list = cacheMap.get(obj);
if(list == null){
list = findSubscribleMethods(obj);
cacheMap.put(obj,list);
}
}
上面的代碼主要為從緩存中獲取是否添加過監(jiān)聽,避免重復添加。
重點:
findSubscribleMethods 中主要將當前類以及父類中所有添加的注解一一遍歷出來,獲取注解類的回調(diào)方法、數(shù)據(jù)返回的線程模式、回調(diào)方法中的參數(shù)類 添加到map緩存 。具體代碼如下:
public List<SubscribleMethod> findSubscribleMethods(Object obj){
List<SubscribleMethod> list = new ArrayList<>();
Class<?> clazz = obj.getClass();
// 循環(huán)去查找父類是否存在subscrible注解方法
while (clazz != null){
// 判斷當前是否是系統(tǒng)類,如果是,就退出循環(huán)
String name = clazz.getName();
if(name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")){
break;
}
// 得到所有的方法,這個clazz在本項目中,暫時指代的是MainActivity
Method[] methods = clazz.getMethods();
for(Method method : methods){
// 通過注解找到我們需要注冊的方法
Subscribe subscribe = method.getAnnotation(Subscribe.class);
if(subscribe == null){
continue;
}
// 獲取方法中的參數(shù),并判斷
Class<?>[] types = method.getParameterTypes();
if(types.length != 1){
throw new RuntimeException("EventBus只能接受一個參數(shù)");
}
// 獲取線程模式
ThreadMode threadMode = subscribe.threadMode();
SubscribleMethod subscribleMethod = new SubscribleMethod(method,threadMode,types[0]);
list.add(subscribleMethod);
}
clazz = clazz.getSuperclass();
}
return list;
}
第三步:發(fā)送事件
發(fā)生事件為根據(jù)當前的發(fā)送的對象獲取緩存中的注解類,根據(jù)當前的線程調(diào)度通過反射去執(zhí)行注解類方法,實現(xiàn)數(shù)據(jù)的傳遞。
public void post(final Object type){
Set<Object> set = cacheMap.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()){
final Object obj = iterator.next();
List<SubscribleMethod> list = cacheMap.get(obj);
for(final SubscribleMethod subscribleMethod : list){
// 簡單的理解:兩個列對比一下,看看是否一致 (不嚴謹?shù)恼f法)
// a(subscribleMethod.getType())對象所對應的類信息,是b(type.getClass())對象所對應的類信息的父類或者父接口
if(subscribleMethod.getType().isAssignableFrom(type.getClass())){
switch (subscribleMethod.getThreadMode()){
// 不管你在post是在主線程 還是在子線程,我都在主線程接受
case MAIN:
// 主 - 主
if(Looper.myLooper() == Looper.getMainLooper()){
invoke(subscribleMethod,obj,type);
}else{
// 子 - 主
mHandler.post(new Runnable() {
@Override
public void run() {
invoke(subscribleMethod,obj,type);
}
});
}
break;
case BACKGROUND:
// 主 - 子
if(Looper.myLooper() == Looper.getMainLooper()){
mExecutorService.execute(new Runnable() {
@Override
public void run() {
invoke(subscribleMethod,obj,type);
}
});
}else{
// 子 - 子
invoke(subscribleMethod,obj,type);
}
break;
}
}
}
}
}
private void invoke(SubscribleMethod subscribleMethod, Object obj, Object type) {
Method method = subscribleMethod.getMethod();
try {
method.invoke(obj,type);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}

image.png
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}