前言
新年的第一篇文章就从本次开始了,在朋友圈立下了flag,怎么能不兑现了。。。 本人是一个Flutter学习不到四个月的菜鸡,由于公司需求,在领导的要求下学习Flutter,用于优化人员结构(bushi)。废话不多说,直接进入主题
需求背景
既然都开始用Flutter开始项目了,那么绕不开的就必须有眼花缭乱的第三方库了,得益于Flutter社区的活跃,大部分的工具都能搜到,然而当第三方库无法满足的时候,自己写相应的插件就得提上日程了。 公司项目要求接入阿里云的人脸识别功能,由于自己搜索方式的错误,只好自己动手了。在网上搜了各式各样的的插件文章,对插件项目的创建了然于心。 创建插件项目的流程我就不细讲了,就是Project_type要注意选择Plugin,其他的跟普通Flutter项目差不了多少,一个插件项目的目录如图所示
android目录就是插件Android端功能的实现,ios目录就是IOS端的实现,example目录里是一个完整的Flutter项目,可用于编写插件调用示例,lib目录就是调用插件的主入口了,由于还没接触过IOS端的东西,暂时就不说了。
开始
如果直接点开android目录,去里面编写功能实现,那有点不现实
这红红火火的文字,将预示着所有人
红红火火
这时候就要选中android目录,右键,选择Flutter,点击Open Android module in Android Studio,然后就是喝茶时间到了,茶喝完了,项目也build完成了
第一个android就是写插件的module了。由于要接入阿里云的实人认证,就还得去官网找到对应的集成方式,可惜的是竟然没提供远程依赖的方式,下载下来的全是aar包
本着直接冲的原则,上来就是一套libs组合拳,然后在External Libraries成功看到了相关引入,开开心心的开始写插件咯,在module目录下新建个AlifacePlugin,然后引入Flutter插件相关的东西,时间紧迫,直接上图
在
onAttachedToEngine创建MethodChannel相关实例
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "aliface")
channel.setMethodCallHandler(this)
mContext = flutterPluginBinding.applicationContext
}
在onDetachedFromEngine中解除相关引用
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
至于onAttachedToActivity和onDetachedFromActivity也是同样的道理,后面这几个Activity相关的方法并不一定要用的,因为aar中的方法有需要持有Activity,因此才引入的,相关描述都在ActivityAware这个类中
然后就是onMethodCall,功能的实现就是在这个函数中处理了
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
// 初始化阿里实人认证库
"registerFace" -> {
ZIMFacade.install(mContext)
val params = hashMapOf<String, Any>()
params["code"] = true
params["msg"] = "初始化成功"
result.success(params)
}
"getMetaInfo" -> {
val metaInfo = ZIMFacade.getMetaInfos(mContext)
val params = hashMapOf<String, Any>().also {
it["metaInfo"] = metaInfo
}
result.success(params)
}
// 开始实人认证
"startVerifier" -> {
val certifyId = call.argument<String>("certifyId")
val extParams = hashMapOf<String, String>().also {
it[ZIMFacade.ZIM_EXT_PARAMS_KEY_SCREEN_ORIENTATION] =
ZIMFacade.ZIM_EXT_PARAMS_VAL_SCREEN_PORT
}
ZIMFacadeBuilder.create(mActivity)
.verify(certifyId, true, extParams, ZIMCallback { response ->
val params = hashMapOf<String, Any>().also {
it["code"] = response.code
it["msg"] = if (response.msg == null) "" else response.msg
}
result.success(params)
return@ZIMCallback true
})
}
}
}
其中call.method是要表明需要实现的名称,Flutter插件的调用需要通过配置的名称来这里获取相关的实现,例如我这里的registerFace,就是实现实人认证的初始化功能。至于怎么调用这里的,后面会讲到,基本上就是,你需要实现哪些功能,就需要在这里实现相关的功能。
当我们需要从Flutter传递参数过来时,就需要用到call.argument,熟悉原生的佬们都是清楚这是用来干啥的,如上文代码中所示的startVerifier中,实人认证需要接收一些参数,所以就需要用到call.argument<String>("certifyId")来读取传递过来的参数,接收参数,编写实现基本就是这样,但往往还需要回调,可以说大部分的业务都需要知道你调用的结果如何,因此就需要用到Result了,包含三个方法result.success(@Nullable Object result)、result.error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails),result.notImplemented(),这里我就没想得太多,不管原生返回的成功还是失败,我都是用的result.success()来处理的,我在这里是将结果用Map来传递,因为Flutter项目中json数据是Map格式的,这样便于处理结果,当然如果你需要的仅仅是传递一个标记来表明成功失败,那就简简单单的传递值就够了,插件部分对于原生实现的地方基本就成功了。
调用实现
原生部分的实现已经完成,接下来就是如何访问原生的方法了,还记得前面提到的call.method吗,接下来就会用到了,回到Flutter Plugin项目,在lib目录下新建一个类aliface.dart
class Aliface {
static const MethodChannel _channel = MethodChannel('aliface');
}
很明显,我们需要使用MethodChannel来访问实现,MethodChannel('aliface')访问的是谁,刚才没有提到
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "aliface")
channel.setMethodCallHandler(this)
mContext = flutterPluginBinding.applicationContext
}
就是在这个实现里啦MethodChannel(flutterPluginBinding.binaryMessenger, "aliface"),
那么怎么访问之前创建的实现呢,这里就要用到channel.invokeMethod,
// 初始化人脸识别库
static Future<InitSdkData> get registerFace async {
final result = await _channel.invokeMethod('registerFace');
final bool code = result['code'];
final String msg = result['msg'];
return InitSdkData(code: code, msg: msg);
}
通过调用channel.invokeMethod来读取之前的result.success(),记得别写错了key值哦,需要跟实现类那边的名称一一对应,InitSdkData()是什么,当然是用来存放结果的,是不是忘了什么,如果需要传递参数给原生那边要怎么办呢,各位读者肯定想到了,都用invokeMethod了,那会有相应的参数了,通过源码不难发现
@optionalTypeArgs
Future<T?> invokeMethod<T>(String method, [ dynamic arguments ]) {
return _invokeMethod<T>(method, missingOk: false, arguments: arguments);
}
该方法中第二个参数就是用于传递参数给原生部分的,原生部分通过call.argument来接收,注意餐宿类型要处理好。插件的调用,实现基本就是如此了,别忘了使用
export 'model/verifier_data.dart';
export 'model/init_sdk_data.dart';
export 'model/verifier_result.dart';
export 'package:aliface/aliface.dart';
不然会发现调用插件类的时候竟然找不到相关引用。接下来就可以在example中验证插件是否正常工作了,这个就不在这里展示了。
结尾
事情就这样结束了?当然不是,还记得前面提到的依赖是本地的aar包吗,这就是我痛苦的时候了
当我欢天喜地的把插件项目传到自己的远程仓库,在另一个项目中引入才发现,问题大发了,竟然用不了,编译不通过,日志的大概意思就是不支持引入本地的aar,因为不是安全的,容易遭到篡改,日志原文我就不发了,因为没存。。。。。。
下一篇我就会告诉你如何处理本地aar包带来的问题以及如何处理aar包来正确引入使用
以前没写过文章,可能很多地方写得不是很合理,有任何错误或建议,欢迎评论区留言, 俺们下次见^_^