转载

Fluttify输出Flutter插件工程详解

[TOC]

系列文章:

(一) Flutter插件开发必备 原生SDK->Dart接口生成引擎 Fluttify 介绍

(二) 如何利用Fluttify开发一个新的Flutter插件

(三) Fluttify输出Flutter插件工程详解

注:目前Fluttify本身并不对外开放,但是内测阶段可以免费为你生成插件,只要提供android端的jar/aar和ios端的framework/.h+.a,或者maven坐标和cocoapods名称即可,联系方法请看文末

工程结构

Fluttify的输出工程是标准的Flutter插件工程,其中输出的原生语言是java(android)和objc(ios)。

android端使用java是因为从字节码反编译到java的时候,如果字节码来自kotlin,那么会有一些特殊的标记,导致一些情况下(比如基础类型和对应包装类的混淆)需要多余的工作去适配,为了加强兼容性,所以后期选择了java作为生成的原生语言。

ios端选择objc也是类似的原因,objc的方法转为swift的方法时,方法名会自动转换,一些涉及到介词的方法名都会被转换为swift风格,这也导致了一些额外的工作去转换objc方法名到swift,所以最终选择了objc作为输出语言。

dart端结构

引用自上一篇文章:

Fluttify 的产物是一个标准的Flutter的插件工程,所以 lib 文件夹之上的结构都和普通插件一样。 lib 文件夹下会分成 androidios 文件夹,分别放置各平台SDK中的类(枚举/接口等)对应的Dart类(枚举/接口等)。 android / ios 文件夹下还会各自生成:

  • function.g.dart 文件:生成的所有顶层函数;
  • type_op.g.dart 文件:所有的 asis 方法,用来判断类型和造型;
  • ios/android.export.g.dart 文件:导出所有的ios/android类型;
  • platformview 文件夹:生成的所有 PlatformView

习惯上会在 lib 文件夹下再加一个 dart 文件夹,放置对各平台进行抽象的代码,并且最后对外 export 的时候,只 export 这个文件夹下的文件。

lib 文件夹结构概览: . ├── janalytics_fluttify.dart └── src ├── android │   ├── android.export.g.dart │   ├── cn ... android端对应的dart接口 │   └── type_op.g.dart ├── dart │   └── janalytics_service.dart └── ios ├── JANALYTICSBrowseEvent.g.dart ├── ...其他生成文件 ├── functions.g.dart ├── ios.export.g.dart └── type_op.g.dart

原生端结构

原生端生成的文件分成两种。

第一种是 PlatformViewFactory 类,负责 PlatformView 的创建,Fluttify会扫描到SDK内所有的View类并为其生成 PlatformViewFactory 类。第二种是主Plugin类,负责所有的MethodChannel的调用处理。

示例的android端的文件夹结构,ios端类似:

.
└── me
    └── yohom
        └── amap_map_fluttify
            ├── AmapMapFluttifyPlugin.java // 主Plugin
            ├── DownloadProgressViewFactory.java // 以下都是PlatformViewFactory
            ├── MapViewFactory.java
            ├── TextureMapViewFactory.java
            └── WearMapViewFactory.java
复制代码

语言元素的映射

java中的类一般都会有作为命名空间使用的包名,平时使用的时候都会先 import ,再使用简称来引用。Fluttify实现初期,生成的dart类也是直接使用java类的简称,但这很容易就会出现类名冲突,所以最终决定使用全类名来生成java对应的dart类。其规则为: java:

package com.test;
class A {}
复制代码

转换为dart:

class com_test_A {}
复制代码

在这点上objc就直接了很多,因为objc类本身就没有命名空间,类名就是它的全名,所以objc这边的类名不需要转换直接用到dart类名上即可,规则为:

@interface TestClassA
@end
复制代码

转换为:

class TestClassA {}
复制代码

接口

所谓 接口 在java和objc的语境下都是代表可以多重继承的类型。虽然dart也有隐式接口,但是objc的接口( protocol )可以有实现且子类可以不实现所有的方法,而dart一旦 implements 了一个隐式接口,就必须实现所有的方法,所以dart的隐式接口不能作为objc的protocol的等价角色。

万幸的是dart支持 mixinmixin 正好能够处理objc的protocol特性。

示例 java:

package com.test;

interface InterfaceA {}
class ClassA implements InterfaceA {}
复制代码

转换为dart:

class com_test_ClassA extends java_lang_Object with com_test_Interface {}
复制代码

objc:

@protocol TestInterfaceA
@end

@interface TestClassA
@end
复制代码

转换为dart:

class TestClassA extends NSObject with TestInterfaceA {}
复制代码

方法

java,objc以及dart的方法在概念上基本一致,除了objc端的一些指针类型和值类型的区分,其他的都差不多。这里给一个例子阐述一下: java:

package com.test;
class TestClassA {
  public String testMethod(int arg) { /* 方法内容 */ }
}
复制代码

转换为dart:

class com_test_TestClassA {
  String testMethod(int arg) { /* 调用原生代码 */ }
}
复制代码

objc:

@interface TestClassA
- (NSString*) testMethod: (NSInteger) arg;
@end
复制代码

转换为dart:

class TestClassA {
  String testMethod(int arg) { /* 调用原生代码 */}
}
复制代码

函数

java没有顶层函数,所以没有需要处理的。

objc的函数实际上就是c函数,而dart也支持顶层函数,且与objc的函数语义上没有太大的出入。

常量

目前支持转换java的类常量到dart的类常量。

回调

回调分为lambda和delegate,不过在Fluttify的生成代码中的角色差不多。

回调的实现主要通过双向的MethodChannel调用来实现,比如说java端有一个方法:

void setCallback(Callback callback) { /* 代码 */ }
复制代码

生成的dart代码会是这样的:

Future<void> setCallback(Callback callback) async {
  await MethodChannel('some channel').invokeMethod('some method');
  
  // 这里会接收到native端的调用
  MethodChannel('some channel callback').setMethodHandler((methodResult) {
    // 处理原生的回调
    callback.onXXX();
  });
}
复制代码

内存管理

dart端

Dart端的所有SDK类都会间接继承 foundation_fluttify 中定义的 Ref 类,这个类代表是一个引用类,内部含有一个 refId 字段,保存的是原生端对应对象的id。

目前这个id的实现使用的是对象的 hashCode 。android端所有的对象都会有 hashCode() 方法,而ios端只有继承 NSObject 的类才有 hash 字段,如果碰到有处理结构体的需要,则用 NSValue 包装结构体后再调用其 hash 字段。

当调用SDK类的方法时,会把 refId 传递给原生,然后原生从全局 HEAP 中获取到目标对象,然后再在目标对象上进行调用。

dart端还提供了一个 kNativeObjectPool 全局集合对象,这个集合对象保存了所有的原生对象的引用(即 refId ),在需要释放对象时,可以对这个集合进行操作。

原生端

foundation_fluttify 的原生端提供了一个 HEAP 全局集合,用来存放插件调用过程中产生的原生对象。当dart端开始一个方法调用时,原生端便会先从 HEAP 中获取到目标对象,再调用对应方法。

如果需要把释放一个对象需要把它从 HEAP 中删除,不然 HEAP 会一直强引用对象导致一直占用内存。从 HEAP 中删除后,后续的内存管理就交给系统来处理了。

结语

本文对Fluttify输出的插件工程的结构作了大致的介绍。这些其实也包含了很多我在实现Fluttify过程中遇到的困难,包括java/objc/dart这些语言在语法上的统一,如何实现回调等等,还有很多很多细节的问题,更有甚者还要给SDK作者的一些骚操作骚写法擦屁股。

最后还是推荐一波,如果有想要生成插件的老铁也可以联系我(382146139@qq.com),目前Fluttify还处于内测阶段,不会收取任何费用,有任何反馈都可以往 fluttify-feedback 提issue,欢迎各位的反馈。

原文  https://juejin.im/post/5e19306c6fb9a02ff67d3780
正文到此结束
Loading...