Skip to content

Commit 7d8417b

Browse files
committed
see 07/18 log
1 parent 6d34d1d commit 7d8417b

File tree

3 files changed

+116
-17
lines changed

3 files changed

+116
-17
lines changed

art/communication.png

-378 Bytes
Loading

plugin/api-gradle-plugin/README.md

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,22 @@
66

77
组件化方案中各业务是相互隔离的,所以两个业务模块要通信的话,就需要通过路由或者接口下沉来完成,业界的方案都无法与 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 完美融合,所以我就只好自己动手来完成一个更方便、精简、完美的 `ApiUtils`,它功能类似 SPI,但比 SPI 更适合于 Android,而且功能更强大。
88

9-
在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 架构中,我们可以通过 ApiUtils 来自由调用各模块的 APIs,各业务通过对外提供的 `export` 模块来供其他业务方使用,各业务方只需实现自身的 `export` 中的 APIs 即可。其 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 结构如下图所示
9+
在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 架构中,我们可以通过 `ApiUtils` 来自由调用各模块的 `apis`,各业务通过对外提供的 `export` 模块来供其他业务方使用,自身只需要实现自身的 `export` 中的 `apis` 即可。其 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 的架构图如下所示
1010

1111

1212
![AucFrame](https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/auc_frame.png)
1313

1414
`ApiUtils` 扮演的角色如下所示:
1515

16-
![ApiUtilsPlayer](https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/communication.png)
16+
![ApiUtilsRole](https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/communication.png)
1717

18-
图中还有提到 **[BusUtils](https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/plugin/bus-gradle-plugin)**,这是一个比 EventBus 更高效的模块内通讯工具,想了解的可以点进去看看哈。当然,`ApiUtils` 不仅在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中可以使用,在正常项目中你也可以使用它来做业务隔离,下面来介绍其具体使用方式。
18+
图中还有提到 **[BusUtils](https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/bus-gradle-plugin)**,这是一个比 EventBus 更高效的模块内通讯工具,想了解的可以点进去看看哈。当然,`ApiUtils` 不仅在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中可以使用,在正常项目中你也可以使用它来做业务隔离,下面来介绍其具体使用方式。
1919

2020

2121
## 使用
2222

23+
### 配置
24+
2325
在项目根目录的 `build.gradle` 中添加 `api` 插件:
2426

2527
```groovy
@@ -59,7 +61,9 @@ android {
5961

6062
当然,如果你项目是开启混淆的话,全量引入 **[AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode)** 也是可以的,混淆会帮你去除未使用到的类和方法。
6163

62-
好了,插件和依赖都配置完毕,下面就让我们在项目中使用吧,我们举一个实际的例子,比如 `login` 模块中存在 `LoginActivity`,`main` 模块存在 `MainActivity`,这两个模块是平行的关系,两者互不依赖,现在我们通过 `ApiUtils` 让 `LoginActivity` 来启动 `MainActivity`,在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中每个业务模块下都有 `export` 模块,类似于你们自己项目中的底层公共模块,因为是 `login` 来调用 `main` 模块,所以是 `mian` 模块需要提供 `api` 来供 `login` 来调,所以我们在 `main` 的 `export` 中加入一个继承自 `ApiUtils.BaseApi` 的抽象类 `MainApi`,并添加启动 `MainActivity` 的抽象方法,我们把方法搞得更复杂点,带上自定义的参数和返回值,具体如下所示:
64+
### 例子
65+
66+
插件和依赖都配置完毕,下面就让我们在项目中使用吧,举一个实际的例子,比如 `login` 模块中存在 `LoginActivity`,`main` 模块存在 `MainActivity`,这两个模块是平行的关系,两者互不依赖,现在我们通过 `ApiUtils` 让 `LoginActivity` 来启动 `MainActivity`,在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中每个业务模块下都有 `export` 模块,类似于你们自己项目中的底层公共模块,因为是 `login` 来调用 `main` 模块,所以是 `mian` 模块需要提供 `api` 来供 `login` 来调,所以我们在 `main` 的 `export` 中加入一个继承自 `ApiUtils.BaseApi` 的抽象类 `MainApi`,并添加启动 `MainActivity` 的抽象方法,我们把方法搞得更复杂点,带上自定义的参数和返回值,具体如下所示:
6367

6468
```java
6569
public abstract class MainApi extends ApiUtils.BaseApi {
@@ -199,9 +203,9 @@ public abstract class MainApi extends ApiUtils.BaseApi {
199203

200204
## 原理
201205

202-
### api 插件的作用
206+
### api 插件原理分析
203207

204-
可以参考下[源码传送门](https://github.com/Blankj/AndroidUtilCode/tree/master/plugin/api-gradle-plugin),插件通过 Gradle 的 transform 来完成对 `ApiUtils.init()` 做注入,下面来一步步分析:
208+
api 插件的源码在这里:[api 插件源码传送门](https://github.com/Blankj/AndroidUtilCode/tree/master/plugin/api-gradle-plugin),该插件通过 Gradle 的 transform 来完成对 `ApiUtils.init()` 做注入,下面来一步步分析:
205209

206210
不明白 transform 的可以先去了解下,简单来说 transform 就是专门用来做字节码插入操作的,最常见的就是 AOP(面向切面编程),这部分我就不科普了,有兴趣的可以自己搜索了解。
207211

@@ -250,15 +254,51 @@ public void visitEnd() {
250254
}
251255
```
252256

253-
然后往 `ApiUtils.init()` 插入扫描出来的内容,比如上面举例的 `MainApi`,那么其插入的代码如下所示
257+
然后往 `ApiUtils.init()` 插入扫描出来的内容,比如上面举例的 `MainApi`,那么其最终插入的代码如下所示
254258

255259
```java
256260
private void init() {
257261
this.registerImpl(MainApiImpl.class);
258262
}
259263
```
260264

261-
我们来看下 `registerImpl` 的实现:
265+
其 ASM 插入的代码如下所示:
266+
267+
```java
268+
@Override
269+
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
270+
if (!"init".equals(name)) {
271+
return super.visitMethod(access, name, descriptor, signature, exceptions);
272+
}
273+
// 往 init() 函数中写入
274+
if (cv == null) return null;
275+
MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
276+
mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, descriptor) {
277+
@Override
278+
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
279+
return super.visitAnnotation(desc, visible);
280+
}
281+
@Override
282+
protected void onMethodEnter() {
283+
super.onMethodEnter();
284+
}
285+
@Override
286+
protected void onMethodExit(int opcode) {
287+
super.onMethodExit(opcode);
288+
for (Map.Entry<String, ApiInfo> apiImplEntry : mApiImplMap.entrySet()) {
289+
mv.visitVarInsn(Opcodes.ALOAD, 0);
290+
mv.visitLdcInsn(Type.getType("L" + apiImplEntry.getValue().implApiClass + ";"));
291+
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, mApiUtilsClass, "registerImpl", "(Ljava/lang/Class;)V", false);
292+
}
293+
}
294+
};
295+
return mv;
296+
}
297+
```
298+
299+
### ApiUtils 原理分析
300+
301+
接下来看下 `ApiUtils.registerImpl` 的实现:
262302

263303
```java
264304
private void registerImpl(Class implClass) {

plugin/bus-gradle-plugin/README.md

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ BusUtils.unregister(xxx);
109109
线程切换使用的是 ThreadUtils 中的线程池,它具有安全的 Cached 线程池,以及 MAIN, IO, CPU, CACHED, SINGLE 线程池,默认不设置的话就是在提交的线程 POSTING,使用的话就是在 `@BusUtils.Bus` 注解中设置 `threadMode = BusUtils.ThreadMode.xx` 即可。
110110

111111

112-
### 规范
112+
## 规范
113113

114114
要想工具用得舒服,规范肯定要遵守的,所谓无规矩不成方圆,不然五花八门的问题肯定一堆堆,这里推荐如下规范:
115115

@@ -125,6 +125,7 @@ BusUtils.unregister(xxx);
125125
## 性能测试
126126

127127
首先,把两者的事件定义好,因为比较的是事件达到的快慢,所以内部都是空实现即可,具体代码如下所示:
128+
128129
```java
129130
@Subscribe
130131
public void eventBusFun(String param) {
@@ -136,6 +137,7 @@ public void busUtilsFun(String param) {
136137
```
137138

138139
`BusUtils` 在编译时会根据 `@BusUtils.Bus` 注解生成一份记录 tag 和 方法签名的映射表,因为是在编译时完成的,这里我们通过反射来完成。
140+
139141
```java
140142
@Before
141143
public void setUp() throws Exception {
@@ -153,6 +155,7 @@ public void setUp() throws Exception {
153155
* 注销 10000 个订阅者,共执行 10 次取平均值
154156

155157
测试机器如下所示:
158+
156159
```
157160
macOS: 2.2GHz Intel Core i7 16GB
158161
一加6: Android 9 8GB
@@ -161,6 +164,7 @@ macOS: 2.2GHz Intel Core i7 16GB
161164
在 Android 上,我们加入 `EventBus` 的注解处理器来提升 `EventBus` 效率,让其在最优情况下和 `BusUtils` 比较。
162165

163166
接下来,我们把测试的模板代码写好,方便后续可以直接把两者比较的代码往回调中塞入即可,具体代码如下所示:
167+
164168
```java
165169
/**
166170
* @param name 传入的测试函数名
@@ -202,9 +206,11 @@ public interface CompareCallback {
202206
void restState();
203207
}
204208
```
209+
205210
下面就让我们来一一对比测试。
206211

207212
### 注册 10000 个订阅者,共执行 10 次取平均值
213+
208214
```java
209215
/**
210216
* 注册 10000 个订阅者,共执行 10 次取平均值
@@ -251,6 +257,7 @@ public void compareRegister10000Times() {
251257
```
252258

253259
### 向 1 个订阅者发送 * 1000000 次,共执行 10 次取平均值
260+
254261
``` java
255262
/**
256263
* 向 1 个订阅者发送 * 1000000 次,共执行 10 次取平均值
@@ -298,6 +305,7 @@ private void comparePostTemplate(String name, int subscribeNum, int postTimes) {
298305
```
299306

300307
### 向 100 个订阅者发送 * 10000 次,共执行 10 次取平均值
308+
301309
```java
302310
/**
303311
* 向 100 个订阅者发送 * 100000 次,共执行 10 次取平均值
@@ -368,17 +376,17 @@ public void compareUnregister10000Times() {
368376
// BusUtilsCostTime: 199
369377
```
370378

371-
### 结论
379+
## 结论
372380

373381
为了方便观察,我们生成一份图表来比较两者之间的性能:
374382

375383
![BusUtilsVsEventBusChart](https://github.com/Blankj/AndroidUtilCode/blob/master/art/busutil_vs_eventbus.png)
376384

377385
图表中分别对四个函数在 MacOS 和 OnePlus6 中的表现进行统计,每个函数中从左向右分别是 「MacOS 的 BusUtils」、「MacOS 的 EventBus」、「OnePlus6 的 BusUtils」、「OnePlus6 的 EventBus」,可以发现,BusUtils 在注册和注销上基本比 `EventBus` 要快上好几倍,BusUtils 在向少量订阅者发送多次事件比 `EventBus` 也快上好多,在向多个订阅者发送多次事件也比 `EventBus` 快上些许。
378386

379-
基于以上说的这么多,如果你项目中事件总线用得比较频繁,那么可以试着用我的 `BusUtils` 来替代 `EventBus` 来提升性能,或者在新的项目中,你也可以使用性能更好的 `BusUtils`。
387+
基于以上说的这么多,如果你项目中事件总线用得比较频繁,那么可以试着用我的 `BusUtils` 来替代 `EventBus` 来提升性能,或者在新的项目中,你也可以直接使用性能更好的 `BusUtils`。
380388

381-
下面我来总结下 `BusUtils` 的优点:
389+
下面来总结下 `BusUtils` 的优点:
382390

383391
* `BusUtils` 是通过事件 `Tag` 来确定唯一事件的,所以接收函数支持无参或者一个参数,而 `EventBus` 只能通过 MessageEvent 来确定具体的接收者,只能接收一个参数,即便仅仅是通知,也需要定义一个 MessageEvent,所以,`BusUtils` 传参更灵活。
384392
* `BusUtils` 在应用到项目中后,编译后便会在 application 中生成 `__bus__.json` 事件列表,如上生成的事件列表如下所示:
@@ -412,14 +420,14 @@ public void compareUnregister10000Times() {
412420
所以,`BusUtils` 比 `EventBus` 更友好。
413421

414422
* `BusUtils` 比 `EventBus` 代码少得太多,`BusUtils` 的源码只有区区 300 行,而 `EventBus` 3000 行肯定是不止的哈。
415-
* `BusUtils` 比 `EventBus` 性能更好,下面我会来对其进行性能对比
423+
* `BusUtils` 比 `EventBus` 性能更好。
416424

417425

418426
## 原理
419427

420-
### bus 插件的作用
428+
### bus 插件原理分析
421429

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()` 做注入,下面来一步步分析:
423431

424432
不明白 transform 的可以先去了解下,简单来说 transform 就是专门用来做字节码插入操作的,最常见的就是 AOP(面向切面编程),这部分我就不科普了,有兴趣的可以自己搜索了解。
425433

@@ -506,7 +514,7 @@ public MethodVisitor visitMethod(int access, String funName, String desc, String
506514
}
507515
```
508516

509-
然后往 `BusUtils.init()` 插入扫描出来的内容,比如上面提到的 `oneParamFun` 这个函数,那么其插入的代码如下所示
517+
然后往 `BusUtils.init()` 插入扫描出来的内容,比如上面提到的 `oneParamFun` 这个函数,那么其最终插入的代码如下所示
510518

511519
```java
512520

@@ -515,7 +523,58 @@ private void init() {
515523
}
516524
```
517525

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` 的实现:
519578

520579
```java
521580
private void registerBus(String tag,

0 commit comments

Comments
 (0)