-
Notifications
You must be signed in to change notification settings - Fork 30
/
README_CN.md
366 lines (282 loc) · 10.9 KB
/
README_CN.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# dna
### [README👉](./README.md)
一个flutter plugin. 轻量级的Dart到Native的超级通道, 可直接在dart代码中调用原生代码,目前支持安卓 JAVA 和 iOS ObjC. 主要用途:
* 可以把channel中的原生代码写在dart代码中,
* 让原生代码也支持热加载.
# 开始
1.这里建议使用 `git` 依赖, 在flutter工程 pubspec.yaml 添加如下:
```
dependencies:
dna:
git:[email protected]:Assuner-Lee/dna.git
```
> [参考文档: https://flutter.dev/docs/development/packages-and-plugins/using-packages](https://flutter.dev/docs/development/packages-and-plugins/using-packages)
2.在dart代码中引入头文件
```
import 'package:dna/dna.dart';
```
3.在android项目中引入依赖
```
implementation 'me.ele:dna-annotations:1.2.0'
annotationProcessor 'me.ele:dna-compiler:1.2.0'
```
使用模块化开发时,可以在子模块中单独引入dna-annotations包,从而获得注解方法的能力
4.在android项目中加入反混淆配置
```
-keep class **.Dna_Class_Proxy { *; }
-keep class me.ele.dna_compiler.** { *; }
-keep class me.ele.dna.** { *; }
```
# 使用介绍
`dna` 在`Dart代码`中:
* 定义了 `NativeContext 类` ,以执行 `Dart 代码` 的方式,描述 `Native 代码` 调用上下文(调用栈);最后调用 `context.execute()` 执行对应平台的 `Native 代码` 并返回结果。
* 定义了 `NativeObject 类` ,用于标识 `Native 变量`. `调用者 NativeObject 对象` 可借助 `所在NativeContext上下文` 调用 `invoke方法` 传入 `方法名 method` 和 `参数数组 args list` ,得到 `返回值NativeObject对象` 。
`NativeContext 子类` 的API是一致的. 下面先详细介绍通过 `ObjCContext` 调用 `ObjC` ,再区别介绍 `JAVAContext` 调用 `JAVA`.
## Dart 调用 ObjC
`ObjCContext` 仅在iOS平台会实际执行.
### 1. 支持上下文调用
##### (1) 返回值作为调用者
ObjC代码
```
NSString *versionString = [[UIDevice currentDevice] systemVersion];
// 通过channel返回versionString
```
Dart 代码
```
ObjCContext context = ObjCContext();
NativeObject UIDevice = context.classFromString('UIDevice');
NativeObject device = UIDevice.invoke(method: 'currentDevice');
NativeObject version = device.invoke(method: 'systemVersion');
context.returnVar = version; // 可省略设定最终返回值, 参考3
// 直接获得原生执行结果
var versionString = await context.execute();
```
##### (2) 返回值作为参数
ObjC代码
```
NSString *versionString = [[UIDevice currentDevice] systemVersion];
NSString *platform = @"iOS-";
versionString = [platform stringByAppendingString: versionString];
// 通过channel返回versionString
```
Dart 代码
```
ObjCContext context = ObjCContext();
NativeClass UIDevice = context.classFromString('UIDevice');
NativeObject device = UIDevice.invoke(method: 'currentDevice');
NativeObject version = device.invoke(method: 'systemVersion');
NativeObject platform = context.classFromString("NSString").invoke(method: 'stringWithString:', args: ['iOS-']);
version = platform.invoke(method: 'stringByAppendingString:', args: [version]);
context.returnVar = version; // 可省略设定最终返回值, 参考3
// 直接获得原生执行结果
var versionString = await context.execute();
```
### 2. 支持链式调用
ObjC代码
```
NSString *versionString = [[UIDevice currentDevice] systemVersion];
versionString = [@"iOS-" stringByAppendingString: versionString];
// 通过channel返回versionString
```
Dart 代码
```
ObjCContext context = ObjCContext();
NativeObject version = context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');
version = context.classFromString("NSString").invoke(method: 'stringWithString:', args: ['iOS-']).invoke(method: 'stringByAppendingString:', args: [version]);
context.returnVar = version; // 可省略设定最终返回值, 参考3
// 直接获得原生执行结果
var versionString = await context.execute();
```
### *关于Context的最终返回值
`context.returnVar` 是 `context` 最终执行完毕返回值的标记
1. 设定context.returnVar: 返回该NativeObject对应的Native变量
2. 不设定context.returnVar: 执行到最后一个invoke,如果有返回值,作为context的最终返回值; 无返回值则返回空值;
```
ObjCContext context = ObjCContext();
context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');
// 直接获得原生执行结果
var versionString = await context.execute();
```
### 3.支持快捷使用JSON中实例化对象
或许有些时候,我们需要用 `JSON` 直接实例化一个对象.
ObjC代码
```
ClassA *objectA = [ClassA new];
objectA.a = 1;
objectA.b = @"sss";
```
一般时候,这样写
Dart 代码
```
ObjCContext context = ObjCContext();
NativeObject objectA = context.classFromString('ClassA').invoke(method: 'new');
objectA.invoke(method: 'setA:', args: [1]);
objectA.invoke(method: 'setB:', args: ['sss']);
```
也可以从JSON中生成
```
ObjCContext context = ObjCContext();
NativeObject objectA = context.newNativeObjectFromJSON({'a':1,'b':'sss'}, 'ClassA');
```
## Dart 调用 JAVA
`JAVAContext` 仅在安卓系统中会被实际执行. `JAVAContext` 拥有上述 `ObjCContext` `Dart调ObjC` 的全部特性.
* 支持上下文调用
* 支持链式调用
* 支持用JSON中实例化对象
另外,额外支持了从构造器中实例化一个对象
### 4. 支持快捷使用构造器实例化对象
JAVA代码
```
String platform = new String("android");
```
Dart 代码
```
NativeObject version = context
.newJavaObjectFromConstructor('java.lang.String', ["android "])
```
## 快捷组织双端代码
提供了一个快捷的方法来 初始化和执行 context.
```
static Future<Object> traversingNative(ObjCContextBuilder(ObjCContext objcContext), JAVAContextBuilder(JAVAContext javaContext)) async {
NativeContext nativeContext;
if (Platform.isIOS) {
nativeContext = ObjCContext();
ObjCContextBuilder(nativeContext);
} else if (Platform.isAndroid) {
nativeContext = JAVAContext();
JAVAContextBuilder(nativeContext);
}
return executeNativeContext(nativeContext);
}
```
可以快速书写两端的原生调用
```
platformVersion = await Dna.traversingNative((ObjCContext context) {
NativeObject version = context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');
version = context.classFromString("NSString").invoke(method: 'stringWithString:', args: ['iOS-']).invoke(method: 'stringByAppendingString:', args: [version]);
context.returnVar = version; // 该句可省略
}, (JAVAContext context) {
NativeObject versionId = context.newJavaObjectFromConstructor('com.example.dna_example.DnaTest', null).invoke(method: 'getDnaVersion').invoke(method: 'getVersion');
NativeObject version = context.newJavaObjectFromConstructor('java.lang.String', ["android "]).invoke(method: "concat", args: [versionId]);
context.returnVar = version; // 该句可省略
});
```
# 原理简介
`dna` 并不涉及` dart对象到Native对象的转换` ,也不关心 `Native对象的生命周期`,而是着重与描述原生方法调用的上下文,在 `context execute` 时通过 `channel` 调用一次原生方法,把调用栈以 `JSON` 的形式传过去供原生动态解析调用。
如前文的中 dart 代码
```
ObjCContext context = ObjCContext();
NativeObject version = context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');
version = context.classFromString("NSString").invoke(method: 'stringWithString:', args: ['iOS-']).invoke(method: 'stringByAppendingString:', args: [version]);
context.returnVar = version; // 可省略设定最终返回值, 参考3
// 直接获得原生执行结果
var versionString = await context.execute();
```
`NativeContext的execute()` 方法,实际调用了
```
static Future<Object> executeNativeContext(NativeContext context) async {
return await _channel.invokeMethod('executeNativeContext', context.toJSON());
}
```
在 `原生的 executeNativeContext` 对应执行的方法中,接收到的 `JSON` 是这样的
```
{
"_objectJSONWrappers": [],
"returnVar": {
"_objectId": "_objectId_WyWRIsLl"
},
"_invocationNodes": [{
"returnVar": {
"_objectId": "_objectId_KNWtiPuM"
},
"object": {
"_objectId": "_objectId_qyfACNGb",
"clsName": "UIDevice"
},
"method": "currentDevice"
}, {
"returnVar": {
"_objectId": "_objectId_haPktBlL"
},
"object": {
"_objectId": "_objectId_KNWtiPuM"
},
"method": "systemVersion"
}, {
"object": {
"_objectId": "_objectId_UAUcgnOD",
"clsName": "NSString"
},
"method": "stringWithString:",
"args": ["iOS-"],
"returnVar": {
"_objectId": "_objectId_UiCMaHAN"
}
}, {
"object": {
"_objectId": "_objectId_UiCMaHAN"
},
"method": "stringByAppendingString:",
"args": [{
"_objectId": "_objectId_haPktBlL"
}],
"returnVar": {
"_objectId": "_objectId_WyWRIsLl"
}
}]
}
```
我们在 `Native` 维护了一个 `objectsInContextMap` , `以objectId` 为键,以 `Native对象` 为值。
`_invocationNodes` 便是方法的调用上下文, 看单个
这里会动态调用 `[UIDevice currentDevice]`, 返回对象以 `returnVar中存储的"_objectId_KNWtiPuM" ` 为键放到 `objectsInContextMap` 里
```
{
"returnVar": {
"_objectId": "_objectId_KNWtiPuM"
},
"object": {
"_objectId": "_objectId_qyfACNGb",
"clsName": "UIDevice"
},
"method": "currentDevice"
},
```
这里 `调用方法的对象的objectId` 是 `"_objectId_KNWtiPuM"` ,是上一个方法的返回值,从`objectsInContextMap` 中取出,继续动态调用,以 `returnVar的object_id为键` 存储新的返回值。
```
{
"returnVar": {
"_objectId": "_objectId_haPktBlL"
},
"object": {
"_objectId": "_objectId_KNWtiPuM" // 会在objectsInContextMap找到中真正的对象
},
"method": "systemVersion"
}
```
方法有参数时,支持自动装包和解包的,如 `int<->NSNumber..`, 如果参数是非 `channel` 规定的15种基本类型,是`NativeObject`, 我们会把对象从 `objectsInContextMap ` 中找出,放到实际的参数列表里
```
{
"object": {
"_objectId": "_objectId_UiCMaHAN"
},
"method": "stringByAppendingString:",
"args": [{
"_objectId": "_objectId_haPktBlL" // 会在objectsInContextMap找到中真正的对象
}],
"returnVar": {
"_objectId": "_objectId_WyWRIsLl"
}
```
...
如果设置了`最终的returnVar`, 将把该 `returnVar objectId` 对应的对象从 `objectsInContextMap` 中找出来,作为 `channel的返回值` 回调回去。如果没有设置,取最后一个 `invocation` 的返回值(如果有)。
## 作者
## 更新日志
| version | note |
| ------ | ------ |
| 0.1.0 | 能用 |
## License
dna is available under the MIT license. See the LICENSE file for more info.
## 其他
* 代码仓库近期会迁移到eleme下
* 欢迎试用,建议和提交代码