• Stars
    star
    364
  • Rank 117,101 (Top 3 %)
  • Language
    Dart
  • License
    Other
  • Created almost 5 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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.

dna

中文文档👉

相关文章

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

  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 caller

ObjC 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<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);
}

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<Object> 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

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.

More Repositories

1

UETool

Show/edit any view's attributions on the screen.
Java
3,258
star
2

lancet

A lightweight and fast AOP framework for Android App and SDK developers
Java
2,094
star
3

morjs

基于小程序 DSL(微信、支付宝)的,可扩展的多端研发框架,支持一键将微信或支付宝小程序转换为微信、支付宝、百度、字节、QQ、快手、淘宝、钉钉等小程序 或 Web 应用。
TypeScript
1,655
star
4

Amigo

A hotfix library for Android platform, and not just this...
Java
1,365
star
5

Stinger

Stinger is a high-efficiency library with great compatibility, for aop in Objective-C, using libffi instead of Objective-C message forwarding. It is 20+ times faster than the Aspects, from message-sending to Aspect-oriented code ends.
Objective-C
992
star
6

corvus

A fast and lightweight Redis Cluster Proxy for Redis 3.0
C
791
star
7

Mess

a gradle plugin for minifying activities, services, receivers, providers and custom view
Groovy
682
star
8

Trojan

Trojan is an efficient mobile terminal lightweight log SDK
Java
390
star
9

bigkeeper

Efficiency improvement for iOS&Android modular development.
Ruby
212
star
10

Intimate

Intimate 提供了友好的 API 让 java 反射的使用更加简单平滑。 其最核心的价值在于 Intimate 将在编译期对 apk 内部代码的调用进行反射优化,完全免除反射的效率问题,使得反射调用就像普通调用一样快捷且无任何代价。
Java
202
star
11

duang

自动 CMS 生成工具
JavaScript
199
star
12

tedis

基于TiKV的兼容Redis协议的强一致性NoSQL数据库
Go
184
star
13

eleme.github.io

Eleme Developers Homepage
CSS
116
star
14

Sparrow

Vue
79
star
15

mobilists

mobile team blog
JavaScript
57
star
16

thrift-parser

To parse thrift file to a AST.
JavaScript
53
star
17

meepo

Event sourcing and broadcasting for database.
Python
52
star
18

ruskit

Redis cluster administration toolkit
Python
34
star
19

doctor

Metric based in-memory circuit breaker for python
Python
23
star
20

react-context-global-store

A simple global store based on React context
TypeScript
22
star
21

NVMImageMaker

API for chaining image drawing codes in Objc.
Objective-C
19
star
22

node-thrift-protocol

An implementation of thrift-protocol with node.
JavaScript
18
star
23

easyxml

php xml lib
PHP
17
star
24

SparrowSDK-iOS

Objective-C
16
star
25

NVMAspects

C
15
star
26

thrift-php

Mirror of apache thrift php lib
PHP
10
star
27

thrift-client

A nodejs thrift client
JavaScript
8
star
28

python-jianfan-mirror

mirror of https://code.google.com/p/python-jianfan/
Python
4
star
29

SparrowSDK-Android

4
star
30

thrift-tracker

Go
1
star
31

finite

F-init-E
JavaScript
1
star
32

python-geohash-mirror

mirror of https://code.google.com/p/python-geohash/
Python
1
star