背景
設計這個 BusUtils
其實是在做 ApiUtils 時順手做的,因為兩者實現方式基本一致,設計前我也沒想著要和 greenrobot 的 EventBus
一較高低,但設計完總需要一個對比,所以就拿業界最優秀的事件匯流排 EventBus
比較一下吧,然後就發現我這區區 300 行不到的 BusUtils
效能比 EventBus
要高出好多,當然,這一切的前提都是在 BusUtils
是切實可用並且有效的,它也是一款執行緒安全的事件匯流排,這些我都在單測中有做過實際測試的,不吹不擂,後面我們拿資料說話,有小夥伴不相信的話也可以通過下載我的原始碼來比較即可,單測地址:BusUtilsVsEventBusTest,Android 測試地址:BusCompareActivity,BusUtils
在 AucFrame 中的作用就是模組內傳值,其扮演的角色如下所示:
下面介紹其使用:
使用
配置
在專案根目錄的 build.gradle
中新增 bus
外掛:
buildscript {
dependencies {
...
classpath 'com.blankj:bus-gradle-plugin:2.2'
}
}
複製程式碼
然後在 application 模組中使用該外掛:
apply plugin: "com.blankj.bus"
複製程式碼
給你的專案新增 AndroidUtilCode 依賴:
api "com.blankj:utilcode:latest_version
複製程式碼
如果你單純只想引入 BusUtils
也是可以的,需要你自己拷貝一份這個類放到你工程裡,記得還要拷貝 ThreadUtils
哦,然後在 app 下的 build.gradle
中 配置 bus 的 DSL 域如下所示:
bus {
busUtilsClass "com.xxx.xxx.BusUtils"
}
android {
...
}
複製程式碼
可以猜測到預設的 busUtilsClass 為 com.blankj.utilcode.util.BusUtils
哈。
如果開啟混淆的話還需要配置你的 BusUtils
中註解方法的防混淆,如果直接用 AndroidUtilCode 的話是不需要你配置的,我已經幫你做完了,配置你自己的 BusUtils
防混淆應該如下所示:
-keepattributes *Annotation*
-keepclassmembers class * {
@com.xxx.xxx.BusUtils$Bus <methods>;
}
複製程式碼
當然,如果你專案是開啟混淆的話,全量引入 AndroidUtilCode 也是可以的,混淆會幫你去除未使用到的類和方法。
好了,外掛和依賴都配置完畢,下面介紹基本使用。
基本使用
public static final String TAG_NO_PARAM = "TagNoParam";
public static final String TAG_ONE_PARAM = "TagOneParam";
@BusUtils.Bus(tag = TAG_NO_PARAM)
public void noParamFun() {/* Do something */}
@BusUtils.Bus(tag = TAG_ONE_PARAM)
public void oneParamFun(String param) {/* Do something */}
@Override
public void onStart() {
super.onStart();
BusUtils.register(this);
}
@Override
public void onStop() {
super.onStop();
BusUtils.unregister(this);
}
BusUtils.post(TAG_NO_PARAM);// noParamFun() will receive
BusUtils.post(TAG_ONE_PARAM, "param");// oneParamFun() will receive
複製程式碼
使用過 EventBus
的肯定一下子就能看懂。
高階使用
粘性事件
支援粘性事件,也就是先傳送,然後在訂閱的時候接收到之前傳送的粘性事件,把其消費掉,使用方式和 EventBus
一致,就是在 @BusUtils.Bus
註解中設定 sticky = true
,具體例子如下所示:
public static final String TAG_NO_PARAM_STICKY = "TagNoParamSticky";
@BusUtils.Bus(tag = TAG_NO_PARAM_STICKY, sticky = true)
public void noParamStickyFun() {/* Do something */}
BusUtils.postSticky(TAG_NO_PARAM_STICKY);
BusUtils.register(xxx);// will invoke noParamStickyFun
BusUtils.removeSticky(TAG_NO_PARAM_STICKY);// When u needn't use the sticky, remove it
BusUtils.unregister(xxx);
複製程式碼
執行緒切換
執行緒切換使用的是 ThreadUtils 中的執行緒池,它具有安全的 Cached 執行緒池,以及 MAIN, IO, CPU, CACHED, SINGLE 執行緒池,預設不設定的話就是在提交的執行緒 POSTING,使用的話就是在 @BusUtils.Bus
註解中設定 threadMode = BusUtils.ThreadMode.xx
即可。
規範
要想工具用得舒服,規範肯定要遵守的,所謂無規矩不成方圓,不然五花八門的問題肯定一堆堆,這裡推薦如下規範:
- 持有事件的類和函式確保確保都是
public
的。 - 由於
BusUtils
是用於模組內呼叫,所以可以寫一個BusConfig
的類來儲存一個模組內所有 bus 的Tag
,方便查詢到使用方及呼叫方。 Tag
中最好還能帶有業務模組字尾名防止重複,是 sticky 型別的話也帶上 sticky,指定具體執行緒的話也帶上執行緒名,例如:update_avatar_sticky_main_info
這個Tag
,讓人直接望文生義。- 如果能結合 AucFrame 來使用,那就更規範不過了。
- 對
BusUtils
中事件傳輸的的bean
都需要keep
下來,否則開啟混淆後會找不到該實體物件而報錯。
使用已經介紹完畢,下面我們來和 EventBus
對比下效能。
效能測試
首先,把兩者的事件定義好,因為比較的是事件達到的快慢,所以內部都是空實現即可,具體程式碼如下所示:
@Subscribe
public void eventBusFun(String param) {
}
@BusUtils.Bus(tag = "busUtilsFun")
public void busUtilsFun(String param) {
}
複製程式碼
BusUtils
在編譯時會根據 @BusUtils.Bus
註解生成一份記錄 tag 和 方法簽名的對映表,因為是在編譯時完成的,這裡我們通過反射來完成。
@Before
public void setUp() throws Exception {
// 這一步是在 AOP 的時候注入的,這裡通過反射來注入 busUtilsFun 事件,效果是一樣的。
ReflectUtils getInstance = ReflectUtils.reflect(BusUtils.class).method("getInstance");
getInstance.method("registerBus", "busUtilsFun", BusUtilsVsEventBusTest.class.getName(), "busUtilsFun", String.class.getName(), "param", false, "POSTING");
}
複製程式碼
通過比較如下幾點的測試來完成對比:
- 註冊 10000 個訂閱者,共執行 10 次取平均值
- 向 1 個訂閱者傳送 * 1000000 次,共執行 10 次取平均值
- 向 100 個訂閱者傳送 * 100000 次,共執行 10 次取平均值
- 登出 10000 個訂閱者,共執行 10 次取平均值
測試機器如下所示:
macOS: 2.2GHz Intel Core i7 16GB
一加6: Android 9 8GB
複製程式碼
在 Android 上,我們加入 EventBus
的註解處理器來提升 EventBus
效率,讓其在最優情況下和 BusUtils
比較。
接下來,我們把測試的模板程式碼寫好,方便後續可以直接把兩者比較的程式碼往回撥中塞入即可,具體程式碼如下所示:
/**
* @param name 傳入的測試函式名
* @param sampleSize 樣本的數量
* @param times 每次執行的次數
* @param callback 比較的回撥函式
*/
private void compareWithEventBus(String name, int sampleSize, int times, CompareCallback callback) {
long[][] dur = new long[2][sampleSize];
for (int i = 0; i < sampleSize; i++) {
long cur = System.currentTimeMillis();
for (int j = 0; j < times; j++) {
callback.runEventBus();
}
dur[0][i] = System.currentTimeMillis() - cur;
cur = System.currentTimeMillis();
for (int j = 0; j < times; j++) {
callback.runBusUtils();
}
dur[1][i] = System.currentTimeMillis() - cur;
callback.restState();
}
long eventBusAverageTime = 0;
long busUtilsAverageTime = 0;
for (int i = 0; i < sampleSize; i++) {
eventBusAverageTime += dur[0][i];
busUtilsAverageTime += dur[1][i];
}
System.out.println(
name +
"\nEventBusCostTime: " + eventBusAverageTime / sampleSize +
"\nBusUtilsCostTime: " + busUtilsAverageTime / sampleSize
);
}
public interface CompareCallback {
void runEventBus();
void runBusUtils();
void restState();
}
複製程式碼
下面就讓我們來一一對比測試。
註冊 10000 個訂閱者,共執行 10 次取平均值
/**
* 註冊 10000 個訂閱者,共執行 10 次取平均值
*/
@Test
public void compareRegister10000Times() {
final List<BusUtilsVsEventBusTest> eventBusTests = new ArrayList<>();
final List<BusUtilsVsEventBusTest> busUtilsTests = new ArrayList<>();
compareWithEventBus("Register 10000 times.", 10, 10000, new CompareCallback() {
@Override
public void runEventBus() {
BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();
EventBus.getDefault().register(test);
eventBusTests.add(test);
}
@Override
public void runBusUtils() {
BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();
BusUtils.register(test);
busUtilsTests.add(test);
}
@Override
public void restState() {
for (BusUtilsVsEventBusTest test : eventBusTests) {
EventBus.getDefault().unregister(test);
}
eventBusTests.clear();
for (BusUtilsVsEventBusTest test : busUtilsTests) {
BusUtils.unregister(test);
}
busUtilsTests.clear();
}
});
}
// MacOS Output:
// Register 10000 times.
// EventBusCostTime: 427
// BusUtilsCostTime: 41
// 一加6 Output:
// Register 10000 times.
// EventBusCostTime: 1268
// BusUtilsCostTime: 399
複製程式碼
向 1 個訂閱者傳送 * 1000000 次,共執行 10 次取平均值
/**
* 向 1 個訂閱者傳送 * 1000000 次,共執行 10 次取平均值
*/
@Test
public void comparePostTo1Subscriber1000000Times() {
comparePostTemplate("Post to 1 subscriber 1000000 times.", 1, 1000000);
}
// MacOS Output:
// Post to 1 subscriber 1000000 times.
// EventBusCostTime: 145
// BusUtilsCostTime: 33
// 一加6 Output:
// Post to 1 subscriber 1000000 times.
// EventBusCostTime: 1247
// BusUtilsCostTime: 696
private void comparePostTemplate(String name, int subscribeNum, int postTimes) {
final List<BusUtilsVsEventBusTest> tests = new ArrayList<>();
for (int i = 0; i < subscribeNum; i++) {
BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();
EventBus.getDefault().register(test);
BusUtils.register(test);
tests.add(test);
}
compareWithEventBus(name, 10, postTimes, new CompareCallback() {
@Override
public void runEventBus() {
EventBus.getDefault().post("EventBus");
}
@Override
public void runBusUtils() {
BusUtils.post("busUtilsFun", "BusUtils");
}
@Override
public void restState() {
}
});
for (BusUtilsVsEventBusTest test : tests) {
EventBus.getDefault().unregister(test);
BusUtils.unregister(test);
}
}
複製程式碼
向 100 個訂閱者傳送 * 100000 次,共執行 10 次取平均值
/**
* 向 100 個訂閱者傳送 * 100000 次,共執行 10 次取平均值
*/
@Test
public void comparePostTo100Subscribers10000Times() {
comparePostTemplate("Post to 100 subscribers 100000 times.", 100, 100000);
}
// MacOS Output:
// Post to 100 subscribers 100000 times.
// EventBusCostTime: 139
// BusUtilsCostTime: 79
// 一加6 Output:
// Post to 100 subscribers 100000 times.
// EventBusCostTime: 3092
// BusUtilsCostTime: 2900
複製程式碼
登出 10000 個訂閱者,共執行 10 次取平均值
/**
* 登出 10000 個訂閱者,共執行 10 次取平均值
*/
@Test
public void compareUnregister10000Times() {
final List<BusUtilsVsEventBusTest> tests = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();
EventBus.getDefault().register(test);
BusUtils.register(test);
tests.add(test);
}
compareWithEventBus("Unregister 10000 times.", 10, 1, new CompareCallback() {
@Override
public void runEventBus() {
for (BusUtilsVsEventBusTest test : tests) {
EventBus.getDefault().unregister(test);
}
}
@Override
public void runBusUtils() {
for (BusUtilsVsEventBusTest test : tests) {
BusUtils.unregister(test);
}
}
@Override
public void restState() {
for (BusUtilsVsEventBusTest test : tests) {
EventBus.getDefault().register(test);
BusUtils.register(test);
}
}
});
for (BusUtilsVsEventBusTest test : tests) {
EventBus.getDefault().unregister(test);
BusUtils.unregister(test);
}
}
// MacOS Output:
// Unregister 10000 times.
// EventBusCostTime: 231
// BusUtilsCostTime: 23
// 一加6 Output:
// Unregister 10000 times.
// EventBusCostTime: 800
// BusUtilsCostTime: 199
複製程式碼
結論
為了方便觀察,我們生成一份圖表來比較兩者之間的效能:
圖表中分別對四個函式在 MacOS 和 OnePlus6 中的表現進行統計,每個函式中從左向右分別是 「MacOS 的 BusUtils」、「MacOS 的 EventBus」、「OnePlus6 的 BusUtils」、「OnePlus6 的 EventBus」,可以發現,BusUtils 在註冊和登出上基本比 EventBus
要快上好幾倍,BusUtils 在向少量訂閱者傳送多次事件比 EventBus
也快上好多,在向多個訂閱者傳送多次事件也比 EventBus
快上些許。
基於以上說的這麼多,如果你專案中事件匯流排用得比較頻繁,那麼可以試著用我的 BusUtils
來替代 EventBus
來提升效能,或者在新的專案中,你也可以直接使用效能更好的 BusUtils
。
下面來總結下 BusUtils
的優點:
BusUtils
是通過事件Tag
來確定唯一事件的,所以接收函式支援無參或者一個引數,而EventBus
只能通過 MessageEvent 來確定具體的接收者,只能接收一個引數,即便僅僅是通知,也需要定義一個 MessageEvent,所以,BusUtils
傳參更靈活。BusUtils
在應用到專案中後,編譯後便會在 application 中生成__bus__.json
事件列表,如上生成的事件列表如下所示:
{
"BusUtilsClass": "com.blankj.utilcode.util.BusUtils",
"rightBus": {
"noParamFun": "{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#noParamFun(), threadMode: POSTING }",
"oneParamFun": "{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#oneParamFun(java.lang.String param), threadMode: POSTING }"
},
"wrongBus": {}
}
複製程式碼
修改 oneParamFun
為兩個引數的話,為了確保專案不會因為 BusUtils
在執行時崩潰,api
外掛會使其在編譯時就不過,此時 __bus__.json
檔案如下所示,提示你引數個數不對:
{
"BusUtilsClass": "com.blankj.utilcode.util.BusUtils",
"rightBus": {
"noParamFun": "{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#noParamFun(), threadMode: POSTING }",
},
"wrongBus": {
"oneParamFun": "{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#oneParamFun(java.lang.String param, java.lang.String param1), threadMode: POSTING, paramSize: 2 }"
}
複製程式碼
同理,如果兩個 bus 的 (2.1 版本已支援 Tag 一對多及事件優先順序)Tag
相同了,也會編譯不過,提示你專案中存在 Tag
相同的 bus。
所以,BusUtils
比 EventBus
更友好。
BusUtils
比EventBus
程式碼少得太多,BusUtils
的原始碼只有區區 300 行,而EventBus
3000 行肯定是不止的哈。BusUtils
比EventBus
效能更好。
原理
bus 外掛原理分析
bus 外掛的原始碼在這裡:bus 外掛原始碼傳送門,該外掛通過 Gradle 的 transform 來完成對 BusUtils.init()
做注入,下面來一步步分析:
不明白 transform 的可以先去了解下,簡單來說 transform 就是專門用來做位元組碼插入操作的,最常見的就是 AOP(面向切面程式設計),這部分我就不科普了,有興趣的可以自己搜尋瞭解。
說到位元組碼操作,那就又有知識點了,想要上手快速簡單的可以使用 javassist
,不過,我選擇了更強大快速的 ASM
,這裡我就不詳細介紹了,有興趣的可以自己去學習,ASM
其實也很簡單的,在 ASM Bytecode Outline 這個外掛幫助下寫得還是很快的。
通過 ASM 掃描出所有帶有 @BusUtils.Bus
註解的函式,讀取並儲存註解的值和函式的引數資訊,相關程式碼如下所示:
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
className = name.replace("/", ".");
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String funName, String desc, String signature, String[] exceptions) {
if (cv == null) return null;
MethodVisitor mv = cv.visitMethod(access, funName, desc, signature, exceptions);
busInfo = null;
mv = new AdviceAdapter(Opcodes.ASM5, mv, access, funName, desc) {
@Override
public AnnotationVisitor visitAnnotation(String desc1, boolean visible) {
final AnnotationVisitor av = super.visitAnnotation(desc1, visible);
if (("L" + mBusUtilsClass + "$Bus;").equals(desc1)) {
busInfo = new BusInfo(className, funName);
funParamDesc = desc.substring(1, desc.indexOf(")"));
return new AnnotationVisitor(Opcodes.ASM5, av) {
@Override
public void visit(String name, Object value) {// 可獲取註解的值
super.visit(name, value);
if ("tag".equals(name)) {
tag = (String) value;
} else if ("sticky".equals(name) && (Boolean) value) {
busInfo.sticky = true;
}
}
@Override
public void visitEnum(String name, String desc, String value) {
super.visitEnum(name, desc, value);
if ("threadMode".equals(name)) {
busInfo.threadMode = value;
}
}
};
}
return av;
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
super.visitLocalVariable(name, desc, signature, start, end, index);// 獲取方法引數資訊
if (busInfo != null && !funParamDesc.equals("")) {
if ("this".equals(name)) {
return;
}
funParamDesc = funParamDesc.substring(desc.length());// 每次去除引數直到為 "",那麼之後的就不是引數了
busInfo.paramsInfo.add(new BusInfo.ParamsInfo(Type.getType(desc).getClassName(), name));
if (busInfo.isParamSizeNoMoreThanOne && busInfo.paramsInfo.size() > 1) {
busInfo.isParamSizeNoMoreThanOne = false;
}
}
}
@Override
public void visitEnd() {
super.visitEnd();
if (busInfo != null) {
List<BusInfo> infoList = mBusMap.get(tag);
if (infoList == null) {
infoList = new ArrayList<>();
mBusMap.put(tag, infoList);
} else if (infoList.size() == 0) {
mBusMap.put(tag, infoList);
} else if (infoList.size() == 1) {
BusInfo info0 = infoList.get(0);
info0.isTagRepeat = true;
busInfo.isTagRepeat = true;
} else {
busInfo.isTagRepeat = true;
}
infoList.add(busInfo);
}
}
};
return mv;
}
複製程式碼
然後往 BusUtils.init()
插入掃描出來的內容,比如上面提到的 oneParamFun
這個函式,那麼其最終插入的程式碼如下所示:
private void init() {
this.registerBus("TagOneParam", "com.blankj.bus.BusTest", "oneParamFun", "java.lang.String", "param", false, "POSTING");
}
複製程式碼
其 ASM 插入的程式碼如下所示:
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if (!"init".equals(name)) {
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
// 往 init() 函式中寫入
if (cv == null) return null;
MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, descriptor) {
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return super.visitAnnotation(desc, visible);
}
@Override
protected void onMethodEnter() {
super.onMethodEnter();
}
@Override
protected void onMethodExit(int opcode) {
super.onMethodExit(opcode);
for (Map.Entry<String, List<BusInfo>> busEntry : mBusMap.entrySet()) {
List<BusInfo> infoList = busEntry.getValue();
if (infoList.size() != 1) continue;
BusInfo busInfo = infoList.get(0);
if (!busInfo.isParamSizeNoMoreThanOne) continue;
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(busEntry.getKey());
mv.visitLdcInsn(busInfo.className);
mv.visitLdcInsn(busInfo.funName);
if (busInfo.paramsInfo.size() == 1) {
mv.visitLdcInsn(busInfo.paramsInfo.get(0).className);
mv.visitLdcInsn(busInfo.paramsInfo.get(0).name);
} else {
mv.visitLdcInsn("");
mv.visitLdcInsn("");
}
mv.visitInsn(busInfo.sticky ? ICONST_1 : ICONST_0);
mv.visitLdcInsn(busInfo.threadMode);
mv.visitMethodInsn(INVOKESPECIAL, mBusUtilsClass, "registerBus", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V", false);
}
}
};
return mv;
}
複製程式碼
BusUtils 原理分析
接下來看下 BusUtils.registerBus
的實現:
private void registerBus(String tag,
String className, String funName, String paramType, String paramName,
boolean sticky, String threadMode) {
mTag_BusInfoMap.put(tag, new BusInfo(className, funName, paramType, paramName, sticky, threadMode));
}
複製程式碼
很簡單,就是往 mTag_BusInfoMap
中插入了 key 為 tag
,value 為 BusInfo
的一個例項,這樣便把一個事件保留了下來。
接下來就是使用了,一開始我們都是先 register,原始碼如下所示:
public static void register(final Object bus) {
getInstance().registerInner(bus);
}
private void registerInner(final Object bus) {
if (bus == null) return;
String className = bus.getClass().getName();
synchronized (mClassName_BusesMap) {
Set<Object> buses = mClassName_BusesMap.get(className);
if (buses == null) {
buses = new CopyOnWriteArraySet<>();
mClassName_BusesMap.put(className, buses);
}
buses.add(bus);
}
processSticky(bus);
}
複製程式碼
我們獲取 bus 的類名,然後對 mClassName_BusesMap
加鎖來把它插入到 mClassName_BusesMap
的 value 的集合中,可以看到我們用了執行緒安全的 CopyOnWriteArraySet
集合,然後還需要處理下之前是否訂閱過粘性事件 processSticky
,到這裡 register 便結束了。
然後就是 post
來傳送事件了,原始碼如下:
public static void post(final String tag) {
post(tag, NULL);
}
public static void post(final String tag, final Object arg) {
getInstance().postInner(tag, arg);
}
private void postInner(final String tag, final Object arg) {
postInner(tag, arg, false);
}
private void postInner(final String tag, final Object arg, final boolean sticky) {
BusInfo busInfo = mTag_BusInfoMap.get(tag);
if (busInfo == null) {
Log.e(TAG, "The bus of tag <" + tag + "> is not exists.");
return;
}
if (busInfo.method == null) {
Method method = getMethodByBusInfo(busInfo);
if (method == null) {
return;
}
busInfo.method = method;
}
invokeMethod(tag, arg, busInfo, sticky);
}
private Method getMethodByBusInfo(BusInfo busInfo) {
try {
if ("".equals(busInfo.paramType)) {
return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName);
} else {
return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName, Class.forName(busInfo.paramType));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
private void invokeMethod(final String tag, final Object arg, final BusInfo busInfo, final boolean sticky) {
Runnable runnable = new Runnable() {
@Override
public void run() {
realInvokeMethod(tag, arg, busInfo, sticky);
}
};
switch (busInfo.threadMode) {
case "MAIN":
Utils.runOnUiThread(runnable);
return;
case "IO":
ThreadUtils.getIoPool().execute(runnable);
return;
case "CPU":
ThreadUtils.getCpuPool().execute(runnable);
return;
case "CACHED":
ThreadUtils.getCachedPool().execute(runnable);
return;
case "SINGLE":
ThreadUtils.getSinglePool().execute(runnable);
return;
default:
runnable.run();
}
}
private void realInvokeMethod(final String tag, Object arg, BusInfo busInfo, boolean sticky) {
Set<Object> buses = mClassName_BusesMap.get(busInfo.className);
if (buses == null || buses.size() == 0) {
if (!sticky) {
Log.e(TAG, "The bus of tag <" + tag + "> was not registered before.");
return;
} else {
return;
}
}
try {
if (arg == NULL) {
for (Object bus : buses) {
busInfo.method.invoke(bus);
}
} else {
for (Object bus : buses) {
busInfo.method.invoke(bus, arg);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
複製程式碼
可以看到程式碼還是比較多的,不過別急,我們一步步來還是很簡單的,首先在我們之前注入的 mTag_BusInfoMap
中查詢是否有該 tag
的 BusInfo
,沒有的話就輸出錯誤日誌直接返回。
然後我們根據獲取到的 BusInfo
來找到 method
例項,BusInfo
第一次會把 method
儲存在例項中,之後呼叫的話直接從例項中取出 method
即可。
接著我們從 BusInfo
中取出執行緒資訊,最後線上程中執行 method
的反射,大體就是這樣,具體細節的話還是需要自己分析原始碼。
最後就是 unregister
了:
public static void unregister(final Object bus) {
getInstance().unregisterInner(bus);
}
private void unregisterInner(final Object bus) {
if (bus == null) return;
String className = bus.getClass().getName();
synchronized (mClassName_BusesMap) {
Set<Object> buses = mClassName_BusesMap.get(className);
if (buses == null || !buses.contains(bus)) {
Log.e(TAG, "The bus of <" + bus + "> was not registered before.");
return;
}
buses.remove(bus);
}
}
複製程式碼
unregister
和 register
相反,就是從 mClassName_BusesMap
的 value 集合中移除,同樣需要對 mClassName_BusesMap
加鎖哦。
Change Log
打個小廣告
歡迎加入我的知識星球「基你太美」,我會在星球中分享 AucFrame 框架、大廠面經、AndroidUtilCode 更詳盡的說明...一切我所瞭解的知識,你可以通過支付進入我的星球「基你太美」進行體驗,加入後優先觀看星球中精華的部分,如果覺得星球的內容對自身沒有收益,你可以自行申請退款退出星球,也沒必要加我好友;如果你已確定要留在我的星球,可以通過掃描如下二維碼(備註:基你太美+你的星球暱稱)加我個人微信,方便我後續拉你進群(PS:進得越早價格越便宜)。