@@ -109,7 +109,7 @@ BusUtils.unregister(xxx);
109
109
线程切换使用的是 ThreadUtils 中的线程池,它具有安全的 Cached 线程池,以及 MAIN, IO, CPU, CACHED, SINGLE 线程池,默认不设置的话就是在提交的线程 POSTING,使用的话就是在 ` @BusUtils.Bus ` 注解中设置 ` threadMode = BusUtils.ThreadMode.xx ` 即可。
110
110
111
111
112
- ### 规范
112
+ ## 规范
113
113
114
114
要想工具用得舒服,规范肯定要遵守的,所谓无规矩不成方圆,不然五花八门的问题肯定一堆堆,这里推荐如下规范:
115
115
@@ -125,6 +125,7 @@ BusUtils.unregister(xxx);
125
125
## 性能测试
126
126
127
127
首先,把两者的事件定义好,因为比较的是事件达到的快慢,所以内部都是空实现即可,具体代码如下所示:
128
+
128
129
``` java
129
130
@Subscribe
130
131
public void eventBusFun(String param) {
@@ -136,6 +137,7 @@ public void busUtilsFun(String param) {
136
137
```
137
138
138
139
` BusUtils ` 在编译时会根据 ` @BusUtils.Bus ` 注解生成一份记录 tag 和 方法签名的映射表,因为是在编译时完成的,这里我们通过反射来完成。
140
+
139
141
``` java
140
142
@Before
141
143
public void setUp() throws Exception {
@@ -153,6 +155,7 @@ public void setUp() throws Exception {
153
155
* 注销 10000 个订阅者,共执行 10 次取平均值
154
156
155
157
测试机器如下所示:
158
+
156
159
```
157
160
macOS: 2.2GHz Intel Core i7 16GB
158
161
一加6: Android 9 8GB
@@ -161,6 +164,7 @@ macOS: 2.2GHz Intel Core i7 16GB
161
164
在 Android 上,我们加入 ` EventBus ` 的注解处理器来提升 ` EventBus ` 效率,让其在最优情况下和 ` BusUtils ` 比较。
162
165
163
166
接下来,我们把测试的模板代码写好,方便后续可以直接把两者比较的代码往回调中塞入即可,具体代码如下所示:
167
+
164
168
``` java
165
169
/**
166
170
* @param name 传入的测试函数名
@@ -202,9 +206,11 @@ public interface CompareCallback {
202
206
void restState ();
203
207
}
204
208
```
209
+
205
210
下面就让我们来一一对比测试。
206
211
207
212
### 注册 10000 个订阅者,共执行 10 次取平均值
213
+
208
214
``` java
209
215
/**
210
216
* 注册 10000 个订阅者,共执行 10 次取平均值
@@ -251,6 +257,7 @@ public void compareRegister10000Times() {
251
257
```
252
258
253
259
### 向 1 个订阅者发送 * 1000000 次,共执行 10 次取平均值
260
+
254
261
``` java
255
262
/**
256
263
* 向 1 个订阅者发送 * 1000000 次,共执行 10 次取平均值
@@ -298,6 +305,7 @@ private void comparePostTemplate(String name, int subscribeNum, int postTimes) {
298
305
```
299
306
300
307
### 向 100 个订阅者发送 * 10000 次,共执行 10 次取平均值
308
+
301
309
``` java
302
310
/**
303
311
* 向 100 个订阅者发送 * 100000 次,共执行 10 次取平均值
@@ -368,17 +376,17 @@ public void compareUnregister10000Times() {
368
376
// BusUtilsCostTime: 199
369
377
```
370
378
371
- ### 结论
379
+ ## 结论
372
380
373
381
为了方便观察,我们生成一份图表来比较两者之间的性能:
374
382
375
383
![ BusUtilsVsEventBusChart] ( https://github.com/Blankj/AndroidUtilCode/blob/master/art/busutil_vs_eventbus.png )
376
384
377
385
图表中分别对四个函数在 MacOS 和 OnePlus6 中的表现进行统计,每个函数中从左向右分别是 「MacOS 的 BusUtils」、「MacOS 的 EventBus」、「OnePlus6 的 BusUtils」、「OnePlus6 的 EventBus」,可以发现,BusUtils 在注册和注销上基本比 ` EventBus ` 要快上好几倍,BusUtils 在向少量订阅者发送多次事件比 ` EventBus ` 也快上好多,在向多个订阅者发送多次事件也比 ` EventBus ` 快上些许。
378
386
379
- 基于以上说的这么多,如果你项目中事件总线用得比较频繁,那么可以试着用我的 ` BusUtils ` 来替代 ` EventBus ` 来提升性能,或者在新的项目中,你也可以使用性能更好的 ` BusUtils ` 。
387
+ 基于以上说的这么多,如果你项目中事件总线用得比较频繁,那么可以试着用我的 ` BusUtils ` 来替代 ` EventBus ` 来提升性能,或者在新的项目中,你也可以直接使用性能更好的 ` BusUtils ` 。
380
388
381
- 下面我来总结下 ` BusUtils ` 的优点:
389
+ 下面来总结下 ` BusUtils ` 的优点:
382
390
383
391
* ` BusUtils ` 是通过事件 ` Tag ` 来确定唯一事件的,所以接收函数支持无参或者一个参数,而 ` EventBus ` 只能通过 MessageEvent 来确定具体的接收者,只能接收一个参数,即便仅仅是通知,也需要定义一个 MessageEvent,所以,` BusUtils ` 传参更灵活。
384
392
* ` BusUtils ` 在应用到项目中后,编译后便会在 application 中生成 ` __bus__.json ` 事件列表,如上生成的事件列表如下所示:
@@ -412,14 +420,14 @@ public void compareUnregister10000Times() {
412
420
所以,`BusUtils` 比 `EventBus` 更友好。
413
421
414
422
* `BusUtils` 比 `EventBus` 代码少得太多,`BusUtils` 的源码只有区区 300 行,而 `EventBus` 3000 行肯定是不止的哈。
415
- * `BusUtils` 比 `EventBus` 性能更好,下面我会来对其进行性能对比 。
423
+ * `BusUtils` 比 `EventBus` 性能更好。
416
424
417
425
418
426
## 原理
419
427
420
- ### bus 插件的作用
428
+ ### bus 插件原理分析
421
429
422
- 可以参考下[源码传送门 ](https://github.com/Blankj/AndroidUtilCode/tree/master/plugin/bus-gradle-plugin),插件通过 Gradle 的 transform 来完成对 `BusUtils.init()` 做注入,下面来一步步分析:
430
+ bus 插件的源码在这里:[bus 插件源码传送门 ](https://github.com/Blankj/AndroidUtilCode/tree/master/plugin/bus-gradle-plugin),该插件通过 Gradle 的 transform 来完成对 `BusUtils.init()` 做注入,下面来一步步分析:
423
431
424
432
不明白 transform 的可以先去了解下,简单来说 transform 就是专门用来做字节码插入操作的,最常见的就是 AOP(面向切面编程),这部分我就不科普了,有兴趣的可以自己搜索了解。
425
433
@@ -506,7 +514,7 @@ public MethodVisitor visitMethod(int access, String funName, String desc, String
506
514
}
507
515
```
508
516
509
- 然后往 ` BusUtils.init() ` 插入扫描出来的内容,比如上面提到的 ` oneParamFun ` 这个函数,那么其插入的代码如下所示 :
517
+ 然后往 ` BusUtils.init() ` 插入扫描出来的内容,比如上面提到的 ` oneParamFun ` 这个函数,那么其最终插入的代码如下所示 :
510
518
511
519
``` java
512
520
@@ -515,7 +523,58 @@ private void init() {
515
523
}
516
524
```
517
525
518
- 来看下 ` registerBus ` 的实现:
526
+ 其 ASM 插入的代码如下所示:
527
+
528
+ ``` java
529
+ @Override
530
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String [] exceptions) {
531
+ if (! " init" . equals(name)) {
532
+ return super . visitMethod(access, name, descriptor, signature, exceptions);
533
+ }
534
+ // 往 init() 函数中写入
535
+ if (cv == null ) return null ;
536
+ MethodVisitor mv = cv. visitMethod(access, name, descriptor, signature, exceptions);
537
+ mv = new AdviceAdapter (Opcodes . ASM5 , mv, access, name, descriptor) {
538
+ @Override
539
+ public AnnotationVisitor visitAnnotation (String desc , boolean visible ) {
540
+ return super . visitAnnotation(desc, visible);
541
+ }
542
+ @Override
543
+ protected void onMethodEnter () {
544
+ super . onMethodEnter();
545
+ }
546
+ @Override
547
+ protected void onMethodExit (int opcode ) {
548
+ super . onMethodExit(opcode);
549
+ for (Map . Entry<String , List<BusInfo > > busEntry : mBusMap. entrySet()) {
550
+ List<BusInfo > infoList = busEntry. getValue();
551
+ if (infoList. size() != 1 ) continue ;
552
+ BusInfo busInfo = infoList. get(0 );
553
+ if (! busInfo. isParamSizeNoMoreThanOne) continue ;
554
+ mv. visitVarInsn(ALOAD , 0 );
555
+ mv. visitLdcInsn(busEntry. getKey());
556
+ mv. visitLdcInsn(busInfo. className);
557
+ mv. visitLdcInsn(busInfo. funName);
558
+ if (busInfo. paramsInfo. size() == 1 ) {
559
+ mv. visitLdcInsn(busInfo. paramsInfo. get(0 ). className);
560
+ mv. visitLdcInsn(busInfo. paramsInfo. get(0 ). name);
561
+ } else {
562
+ mv. visitLdcInsn(" " );
563
+ mv. visitLdcInsn(" " );
564
+ }
565
+ mv. visitInsn(busInfo. sticky ? ICONST_1 : ICONST_0 );
566
+ mv. visitLdcInsn(busInfo. threadMode);
567
+ 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 );
568
+ }
569
+ }
570
+ };
571
+ return mv;
572
+ }
573
+ ```
574
+
575
+ ### BusUtils 原理分析
576
+
577
+ 接下来看下 ` BusUtils.registerBus ` 的实现:
519
578
520
579
``` java
521
580
private void registerBus(String tag,
0 commit comments