https://github.com/eleme/dna
dna, dart native access. A lightweight dart to native super channel plugin, You can use it to invoke any native code directly in contextual and chained dart code.
https://github.com/eleme/dna
dart flutter flutter-plugin java objcective-c
Last synced: 13 days ago
JSON representation
dna, dart native access. A lightweight dart to native super channel plugin, You can use it to invoke any native code directly in contextual and chained dart code.
- Host: GitHub
- URL: https://github.com/eleme/dna
- Owner: eleme
- License: other
- Created: 2019-12-24T06:15:56.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2020-06-11T08:14:37.000Z (about 5 years ago)
- Last Synced: 2024-04-18T15:53:54.205Z (about 1 year ago)
- Topics: dart, flutter, flutter-plugin, java, objcective-c
- Language: Dart
- Homepage:
- Size: 347 KB
- Stars: 364
- Watchers: 12
- Forks: 30
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# dna
### [δΈζζζ‘£π](./README_CN.md)
### [ηΈε ³ζη« ](https://juejin.im/post/5e5f1d41518825495b29a05b)
dart native access. A lightweight dart to native super channel plugin, You can use it to invoke any native code directly in dart code.Supported Platform(Language):
- iOS(Objective-C)
- Android(Java)The primary scenario:
- Implement some simple channels directly in dart code;
- Native code that are calling using dna can also be hot-reloaded.## Add dependency
1. Add folllowing code to the *pubspec.yaml* file in your flutter project:```
dependencies:
dna:
git:[email protected]:Assuner-Lee/dna.git
```> Reference: [https://flutter.dev/docs/development/packages-and-plugins/using-packages](https://flutter.dev/docs/development/packages-and-plugins/using-packages)
2. import header file in dart code:
```
import 'package:dna/dna.dart';
```
3. add gradle dependency in Android project:```
implementation 'me.ele:dna-annotations:1.2.0'
annotationProcessor 'me.ele:dna-compiler:1.2.0'
```
4. add following conconfiguration in Android project's proguard-rules```
-keep class **.Dna_Class_Proxy { *; }
-keep class me.ele.dna_compiler.** { *; }
-keep class me.ele.dna.** { *; }
```## Usage
### Main class
- `NativeContext`: You can use it to describe *Native code* by *Dart code*, then call `context.execute()` to execute the final *Native code* on associated platform and get the returned value.
- `NativeObject`: Used to identify the *native variable*. The caller `NativeObject ` can call the `invoke` method to pass in the *method name* and the *parameter array args list* in the context of the `NativeContext` to get the return value `NativeObject` object.
The API of `NativeContext` is consistent. Now we will make a detailed introduction for call *ObjC* using `ObjCContext`, Then call *Java* using `JAVAContext`.
### Call ObjC using Dart
`ObjCContext` is the final executor on iOS platform.
#### Context call supported
##### Returned value as callerObjC code
```
NSString *versionString = [[UIDevice currentDevice] systemVersion];
// Return versionString using fluter channel
```
Dart code```
ObjCContext context = ObjCContext();
NativeObject UIDevice = context.classFromString('UIDevice');
NativeObject device = UIDevice.invoke(method: 'currentDevice');
NativeObject version = device.invoke(method: 'systemVersion');context.returnVar = version; // Can be omitted, See:Quick use of instantiated objects in JSON supported
// Get native execution results directly
var versionString = await context.execute();
```##### Returned value as parameters
ObjC code
```
NSString *versionString = [[UIDevice currentDevice] systemVersion];
NSString *platform = @"iOS-";
versionString = [platform stringByAppendingString: versionString];// Return versionString using fluter channel
```
Dart code```
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; // Can be omitted, See:Quick use of instantiated objects in JSON supported
// Get native execution results directly
var versionString = await context.execute();
```#### Chaining calls supported
ObjC code
```
NSString *versionString = [[UIDevice currentDevice] systemVersion];
versionString = [@"iOS-" stringByAppendingString: versionString];// Return versionString using fluter channel
```Dart code
```
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; // Can be omitted, See:Quick use of instantiated objects in JSON supported
// Get native execution results directly
var versionString = await context.execute();
```> **Something about the final returned value of the `context`**
> `context.returnVar` is the marker of the final returned value of `context`.
> 1. When setting `context.returnVar`, you can get the *Native* variable corresponding to the `NativeObject`;
> 2. Without setting `context.returnVar`, execute to the last `invoke`, if there is a return value, it will be the final returned value of `context`; if not, it will return a `null` value.> ```
> ObjCContext context = ObjCContext();
> context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');
>
> // Get native execution results directly
> var versionString = await context.execute();
> ```#### Quick use of instantiated objects in JSON supported
Sometimes, we need to directly instantiate an object with `JSON`.
ObjC code
```
ClassA *objectA = [ClassA new];
objectA.a = 1;
objectA.b = @"sss";
```Dart code
One way
```
ObjCContext context = ObjCContext();
NativeObject objectA = context.classFromString('ClassA').invoke(method: 'new');
objectA.invoke(method: 'setA:', args: [1]);
objectA.invoke(method: 'setB:', args: ['sss']);
```The other way
```
ObjCContext context = ObjCContext();
NativeObject objectA = context.newNativeObjectFromJSON({'a':1,'b':'sss'}, 'ClassA');
```### Call Java using Dart
`JAVAContext` is the final executor on Android
platform, it has all the fetures that `ObjCContext` have.
- Context call supported;
- Chaining calls supported;
- Quick use of instantiated objects in JSON supported.In addition, it additionally supports the instantiation of an object from the constructor.
#### The instantiation of an object from the constructor supported
Java code
```
String platform = new String("android");
```Dart code
```
NativeObject version = context
.newJavaObjectFromConstructor('java.lang.String', ["android "])```
### Fast organization of dual platform code
We provide you with a quick way to initialize and execute context:
```
static Future 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);
}
```So you can write the native call of two platforms quickly:
```
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; // Can be omitted
}, (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; // Can be omitted
});
```## Principle introduction
dna **does not involve the transformation from a dart object to a native object**, it also **does not care about the life cycle of the native object**, but **focuses on describing the `context` of native method calls**, When `context.execute()` called, a native method is called through `channel`, and the call stack is passed in the form of `JSON` for native dynamic parsing and calling.
for example, Let's take a look at the previous Dart code:
```
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; // Can be omitted, See: Quick use of instantiated objects in JSON supported
// Get native execution results directly
var versionString = await context.execute();
```What the `execute()` method of `NativeContext` actually called is the following method:
```
static Future executeNativeContext(NativeContext context) async {
return await _channel.invokeMethod('executeNativeContext', context.toJSON());
}
```In the native executed method corresponding to the `executeNativeContext` method, the received 'JSON' is as follows:
```
{
"_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"
}
}]
}
```Then we maintain an `objectsInContextMap` on the native side, its key is `objectId`, and the value is native object.
`_invocationNodes` is the call context of the method, let's take a look at one of them.
Here we will dynamically call `[UIDevice currentDevice]`, and return the object to `objectsInContextMap` with `_objectId_KNWtiPuM` stored in `returnVar` as the key.
```
{
"returnVar": {
"_objectId": "_objectId_KNWtiPuM"
},
"object": {
"_objectId": "_objectId_qyfACNGb",
"clsName": "UIDevice"
},
"method": "currentDevice"
},
```Here, the object `_objectId_KNWtiPuM` is the returned value of the previous method. Take it out from the `objectsInContextMap`, continue the dynamic call, and store the new returned value with the `_objectId` of the `returnVar` as the key.
```
{
"returnVar": {
"_objectId": "_objectId_haPktBlL"
},
"object": {
"_objectId": "_objectId_KNWtiPuM" // Will find the real object in objectsInContextMap
},
"method": "systemVersion"
}
```dna supports automatic package loading and unpacking when the method has parameters, such as `int<->NSNumber`, If the parameter is not one of the 15 basic types specified by `channel` but `NativeObject`, we will find the object from `objectsInContextMap` and put it into the actual parameter list.
```
{
"object": {
"_objectId": "_objectId_UiCMaHAN"
},
"method": "stringByAppendingString:",
"args": [{
"_objectId": "_objectId_haPktBlL" // Will find the real object in objectsInContextMap
}],
"returnVar": {
"_objectId": "_objectId_WyWRIsLl"
}
```If final `returnVar` is set, The object corresponding to the `returnVar objectId` will be found from the `objectsInContextMap` and called back as the return value of the `channel `, if not, take the return value of the last `invocation`(if any).
## Author
- [email protected]
- [email protected]
- [email protected]## Change log
| version | note |
| ------ | ------ |
| 0.1.0 | alpha version |## License
dna is available under the MIT license. See the LICENSE file for more info.
## Other Tips
- Code warehouse will be migrated to **eleme** in the near future;
- You are welcome to star, issue and PR.