{"id":28619641,"url":"https://github.com/eleme/dna","last_synced_at":"2025-10-16T23:34:33.716Z","repository":{"id":48790283,"uuid":"229882552","full_name":"eleme/dna","owner":"eleme","description":"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.","archived":false,"fork":false,"pushed_at":"2020-06-11T08:14:37.000Z","size":355,"stargazers_count":364,"open_issues_count":2,"forks_count":30,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-04-18T15:53:54.205Z","etag":null,"topics":["dart","flutter","flutter-plugin","java","objcective-c"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eleme.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-24T06:15:56.000Z","updated_at":"2024-04-10T05:46:53.000Z","dependencies_parsed_at":"2022-08-28T20:52:22.707Z","dependency_job_id":null,"html_url":"https://github.com/eleme/dna","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/eleme/dna","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleme%2Fdna","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleme%2Fdna/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleme%2Fdna/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleme%2Fdna/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eleme","download_url":"https://codeload.github.com/eleme/dna/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eleme%2Fdna/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259400464,"owners_count":22851816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","flutter","flutter-plugin","java","objcective-c"],"created_at":"2025-06-12T04:40:51.719Z","updated_at":"2025-10-16T23:34:28.669Z","avatar_url":"https://github.com/eleme.png","language":"Dart","readme":"# dna\n### [中文文档👉](./README_CN.md)\n### [相关文章](https://juejin.im/post/5e5f1d41518825495b29a05b)\ndart native access. A lightweight dart to native super channel plugin, You can use it to invoke any native code directly in dart code.\n\nSupported Platform(Language):\n\n- iOS(Objective-C)\n- Android(Java)\n\nThe primary scenario:\n\n- Implement some simple channels directly in dart code;\n- Native code that are calling using dna can also be hot-reloaded.\n\n\n## Add dependency\n1. Add folllowing code to the *pubspec.yaml* file in your flutter project:\n\n\t```\n\tdependencies:\n \tdna:\n    \tgit:git@github.com:Assuner-Lee/dna.git\n\t```\n\n\t\u003e Reference: [https://flutter.dev/docs/development/packages-and-plugins/using-packages](https://flutter.dev/docs/development/packages-and-plugins/using-packages)\n\n2. import header file in dart code:\n\n\t```\n\timport 'package:dna/dna.dart';\n\t```\n3. add gradle dependency in Android project:\n\n\t```\n\timplementation 'me.ele:dna-annotations:1.2.0'\n    annotationProcessor 'me.ele:dna-compiler:1.2.0'\n\t```\n4. add following conconfiguration in Android project's proguard-rules\n\n    ```\n\t-keep class **.Dna_Class_Proxy { *; }\n    -keep class me.ele.dna_compiler.**  { *; }\n    -keep class me.ele.dna.**  { *; }\n\t```\n\n## Usage\n\n### Main class\n\n- `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.\n\n- `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.\n\n\nThe API of `NativeContext` is consistent. Now we will make a detailed introduction for call *ObjC* using `ObjCContext`, Then call *Java* using `JAVAContext`.\n\n### Call ObjC using Dart\n\n`ObjCContext` is the final executor on iOS platform.\n\n#### Context call supported\n##### Returned value as caller\n\nObjC code\n\n```\nNSString *versionString = [[UIDevice currentDevice] systemVersion];\n// Return versionString using fluter channel\n``` \nDart code\n\n```\nObjCContext context = ObjCContext();\nNativeObject UIDevice = context.classFromString('UIDevice');\nNativeObject device = UIDevice.invoke(method: 'currentDevice');\nNativeObject version = device.invoke(method: 'systemVersion');\n\ncontext.returnVar = version; // Can be omitted, See:Quick use of instantiated objects in JSON supported\n\n// Get native execution results directly\nvar versionString = await context.execute(); \n```\n\n##### Returned value as parameters\n\nObjC code\n\n```\nNSString *versionString = [[UIDevice currentDevice] systemVersion];\nNSString *platform = @\"iOS-\";\nversionString = [platform stringByAppendingString: versionString];\n\n// Return versionString using fluter channel\n``` \nDart code\n\n```\nObjCContext context = ObjCContext();\nNativeClass UIDevice = context.classFromString('UIDevice');\nNativeObject device = UIDevice.invoke(method: 'currentDevice');\nNativeObject version = device.invoke(method: 'systemVersion');\nNativeObject platform = context.classFromString(\"NSString\").invoke(method: 'stringWithString:', args: ['iOS-']);\nversion = platform.invoke(method: 'stringByAppendingString:', args: [version]);\n\ncontext.returnVar = version; // Can be omitted, See:Quick use of instantiated objects in JSON supported\n\n// Get native execution results directly\nvar versionString = await context.execute(); \n```\n\n#### Chaining calls supported\n\nObjC code\n\n```\nNSString *versionString = [[UIDevice currentDevice] systemVersion];\nversionString = [@\"iOS-\" stringByAppendingString: versionString];\n\n// Return versionString using fluter channel\n```\n\nDart code\n\n```\nObjCContext context = ObjCContext();\nNativeObject version = context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');\nversion = context.classFromString(\"NSString\").invoke(method: 'stringWithString:', args: ['iOS-']).invoke(method: 'stringByAppendingString:', args: [version]);\n\ncontext.returnVar = version; // Can be omitted, See:Quick use of instantiated objects in JSON supported\n\n\n// Get native execution results directly\nvar versionString = await context.execute(); \n```\n\n\n\u003e **Something about the final returned value of the `context`**\n\n\u003e `context.returnVar` is the marker of the final returned value of `context`.\n\n\u003e 1. When setting `context.returnVar`, you can get the *Native* variable corresponding to the `NativeObject`;\n\u003e 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.\n\n\u003e ```\n\u003e ObjCContext context = ObjCContext();\n\u003e context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');\n\u003e \n\u003e // Get native execution results directly\n\u003e var versionString = await context.execute(); \n\u003e ```\n\n#### Quick use of instantiated objects in JSON supported\n\nSometimes, we need to directly instantiate an object with `JSON`.\n\nObjC code\n\n```\nClassA *objectA = [ClassA new]; \nobjectA.a = 1;\nobjectA.b = @\"sss\";\n``` \n\nDart code\n\nOne way\n\n```\nObjCContext context = ObjCContext();\nNativeObject objectA = context.classFromString('ClassA').invoke(method: 'new');\nobjectA.invoke(method: 'setA:', args: [1]);\nobjectA.invoke(method: 'setB:', args: ['sss']);\n```\n\nThe other way\n\n```\nObjCContext context = ObjCContext();\nNativeObject objectA = context.newNativeObjectFromJSON({'a':1,'b':'sss'}, 'ClassA');\n```\n\n### Call Java using Dart\n\n`JAVAContext` is the final executor on Android\n platform, it has all the fetures that `ObjCContext` have.\n \n- Context call supported;\n- Chaining calls supported;\n- Quick use of instantiated objects in JSON supported.\n\nIn addition, it additionally supports the instantiation of an object from the constructor.\n\n#### The instantiation of an object from the constructor supported\n\nJava code\n\n```\nString platform = new String(\"android\");\n``` \n\nDart code\n\n```\nNativeObject version = context\n            .newJavaObjectFromConstructor('java.lang.String', [\"android \"])\n\n```\n\n### Fast organization of dual platform code\n\nWe provide you with a quick way to initialize and execute context:\n\n```\nstatic Future\u003cObject\u003e traversingNative(ObjCContextBuilder(ObjCContext objcContext), JAVAContextBuilder(JAVAContext javaContext)) async {\n    NativeContext nativeContext;\n    if (Platform.isIOS) {\n      nativeContext = ObjCContext();\n      ObjCContextBuilder(nativeContext);\n    } else if (Platform.isAndroid) {\n      nativeContext = JAVAContext();\n      JAVAContextBuilder(nativeContext);\n    }\n    return executeNativeContext(nativeContext);\n}\n```\n\nSo you can write the native call of two platforms quickly:\n\n```\nplatformVersion = await Dna.traversingNative((ObjCContext context) {\n    NativeObject version = context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');\n    version = context.classFromString(\"NSString\").invoke(method: 'stringWithString:', args: ['iOS-']).invoke(method: 'stringByAppendingString:', args: [version]);\n    \n    context.returnVar = version; // Can be omitted\n}, (JAVAContext context) {\n    NativeObject versionId = context.newJavaObjectFromConstructor('com.example.dna_example.DnaTest', null).invoke(method: 'getDnaVersion').invoke(method: 'getVersion');\n    NativeObject version = context.newJavaObjectFromConstructor('java.lang.String', [\"android \"]).invoke(method: \"concat\", args: [versionId]);\n    \n    context.returnVar = version; // Can be omitted\n});\n```\n\n## Principle introduction\n\ndna **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.\n\nfor example,\tLet's take a look at the previous Dart code:\n\n```\nObjCContext context = ObjCContext();\nNativeObject version = context.classFromString('UIDevice').invoke(method: 'currentDevice').invoke(method: 'systemVersion');\nversion = context.classFromString(\"NSString\").invoke(method: 'stringWithString:', args: ['iOS-']).invoke(method: 'stringByAppendingString:', args: [version]);\n\ncontext.returnVar = version; // Can be omitted, See: Quick use of instantiated objects in JSON supported\n\n// Get native execution results directly\nvar versionString = await context.execute(); \n```\n\nWhat the `execute()` method of `NativeContext` actually called is the following method:\n\n```\nstatic Future\u003cObject\u003e executeNativeContext(NativeContext context) async {\n    return await _channel.invokeMethod('executeNativeContext', context.toJSON());\n}\n```\n\nIn the native executed method corresponding to the `executeNativeContext` method, the received 'JSON' is as follows:\n\n```\n{\n\t\"_objectJSONWrappers\": [],\n\t\"returnVar\": {\n\t\t\"_objectId\": \"_objectId_WyWRIsLl\"\n\t},\n\t\"_invocationNodes\": [{\n\t\t\"returnVar\": {\n\t\t\t\"_objectId\": \"_objectId_KNWtiPuM\"\n\t\t},\n\t\t\"object\": {\n\t\t\t\"_objectId\": \"_objectId_qyfACNGb\",\n\t\t\t\"clsName\": \"UIDevice\"\n\t\t},\n\t\t\"method\": \"currentDevice\"\n\t}, {\n\t\t\"returnVar\": {\n\t\t\t\"_objectId\": \"_objectId_haPktBlL\"\n\t\t},\n\t\t\"object\": {\n\t\t\t\"_objectId\": \"_objectId_KNWtiPuM\"\n\t\t},\n\t\t\"method\": \"systemVersion\"\n\t}, {\n\t\t\"object\": {\n\t\t\t\"_objectId\": \"_objectId_UAUcgnOD\",\n\t\t\t\"clsName\": \"NSString\"\n\t\t},\n\t\t\"method\": \"stringWithString:\",\n\t\t\"args\": [\"iOS-\"],\n\t\t\"returnVar\": {\n\t\t\t\"_objectId\": \"_objectId_UiCMaHAN\"\n\t\t}\n\t}, {\n\t\t\"object\": {\n\t\t\t\"_objectId\": \"_objectId_UiCMaHAN\"\n\t\t},\n\t\t\"method\": \"stringByAppendingString:\",\n\t\t\"args\": [{\n\t\t\t\"_objectId\": \"_objectId_haPktBlL\"\n\t\t}],\n\t\t\"returnVar\": {\n\t\t\t\"_objectId\": \"_objectId_WyWRIsLl\"\n\t\t}\n\t}]\n}\n```\n\nThen we maintain an `objectsInContextMap` on the native side, its key is `objectId`, and the value is native object.\n\n`_invocationNodes` is the call context of the method, let's take a look at one of them.\n\nHere we will dynamically call `[UIDevice currentDevice]`, and return the object to `objectsInContextMap` with `_objectId_KNWtiPuM` stored in `returnVar` as the key.\n\n```\n{\n\t\"returnVar\": {\n\t\t\"_objectId\": \"_objectId_KNWtiPuM\"\n\t},\n\t\"object\": {\n\t\t\"_objectId\": \"_objectId_qyfACNGb\",\n\t\t\"clsName\": \"UIDevice\"\n\t},\n\t\"method\": \"currentDevice\"\n },\n```\n\nHere, 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.\n\n```\n{\n\t\"returnVar\": {\n\t\t\"_objectId\": \"_objectId_haPktBlL\"\n\t},\n\t\"object\": {\n\t\t\"_objectId\": \"_objectId_KNWtiPuM\" // Will find the real object in objectsInContextMap\n\t},\n\t\"method\": \"systemVersion\"\n}\n```\n\ndna supports automatic package loading and unpacking when the method has parameters, such as `int\u003c-\u003eNSNumber`, 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.\n\n```\n{\n\t\"object\": {\n\t\t\"_objectId\": \"_objectId_UiCMaHAN\"\n\t},\n\t\"method\": \"stringByAppendingString:\",\n\t\"args\": [{\n\t\t\"_objectId\": \"_objectId_haPktBlL\" // Will find the real object in objectsInContextMap\n\t}],\n\t\"returnVar\": {\n\t\t\"_objectId\": \"_objectId_WyWRIsLl\"\n}\n```\n\nIf 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).\n\n## Author\n\n- yongguang.lyg@alibaba-inc.com\n- zhengguang.zzg@alibaba-inc.com\n- zyd178591@alibaba-inc.com\n\n## Change log\n| version | note |\n| ------ | ------ | \n| 0.1.0 | alpha version | \n\n## License\n\ndna is available under the MIT license. See the LICENSE file for more info.\n\n## Other Tips\n\n- Code warehouse will be migrated to **eleme** in the near future;\n- You are welcome to star, issue and PR.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feleme%2Fdna","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feleme%2Fdna","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feleme%2Fdna/lists"}