Flutter的AIDL怎么定义

65次阅读
没有评论

共计 5137 个字符,预计需要花费 13 分钟才能阅读完成。

本篇内容介绍了“Flutter 的 AIDL 怎么定义”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让丸趣 TV 小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

Flutter 的产品定义是一个高性能的跨平台的移动 UI 框架,能够用一套代码同时构建出 Android/iOS/Web/MacOS 应用。作为一套 UI 框架,它不具备一些系统的接口,自然还是避免不了跟原生打交道。于是乎,它提出了名为 platform  channel 的东西,用于 flutter 和原生灵活的交换数据。以下为了描述方便,用 Android 代指原生。

燃鹅,燃鹅,燃鹅,它只支持一些基础的数据类型和数据结构的传输,例如 bool/int/long/byte/char/String/byte[]/List/Map 等。

因此,当你想传输复杂点的数据,你只能包装成 Map,类似这样:

await _channel.invokeMethod(initUser , { name :  Oscar ,  age : 16,  gender :  MALE ,  country :  China

然后再在 Android 层 hard  code,解析出不同的 key 对应的不同数据。如果你是一个纯 fluter 项目,且以后也没有和原生打交道的打算,或者只是需要进行简单的交互,那这种做法也无可厚非。而当你的项目已经有很大的一部分原生代码或者你需要使用第三方不支持 flutter 的 lib 库的时候,就意味着你需要编写大量向上面那样的模板代码。可见效率低下,且可维护性差。这时,你会想,能传输对象就好了!

而当你想传输对象时:

抱歉,没门,只能给你一个尴尬又不是礼貌的危笑。当然,也不是不可以,我们可以在原生上层把对象序列化成 json 对象,然后在 flutter 层再把 json 转成 flutter 的对象,同样效率很差。

FIDL 是什么

学过 Android 的应该都知道 AIDL(Android Interface Defination  Language),即 Android 接口定义语言。Android 中有一种高级的跨进程通信方式 mdash; mdash;Binder,但是想要使用 Binder 需要了解一些 Binder 的机制和 API,需要编写大量的模板代码。Android 为了解决这个问题,尝试把使用 Binder 的方法做的小白一点。于是定义了 AIDL,告诉开发者,你的接口文件必须按照我规定的来写,你要跨进程传输的对象必须实现 Parcelable 接口。然后,Android 给你生成了一个 Service.Stub 类,偷偷的在背后把对象的序列化、反序列化的工作都给做了。开发者使用这个 Stub 类就能轻松上手 Binder 这种高级的跨进程通讯方法。

FIDL(Flutter Interface Defination  Language) 即 Flutter 接口定义语言,它的使命和 AIDL 很类似,悄悄把对象的序列化、反序列化、自动生成代码这种“脏活累活”给做了。开发者在原生代码中看到的类,能通过 @FIDL 注解标记,自动在 Dart 侧生成和原生代码中一样的类。FIDL 是一面镜子,把各种原生平台的类影射到 Dart 中,把 Dart 中的类影射到各个原生平台。

少啰嗦,先看东西

首先是 Java 类:

public class User { String name; int age; String country; Gender gender; } enum Gender { MALE, FEMALE }

Android 侧

1、定义 FIDL 接口

@FIDL public interface IUserService { void initUser(User user); }

2、执行命令./gradlew assembleDebug,生成 IUserServiceStub 类和 fidl.json 文件

3、打开通道,向 Flutter 公开方法

FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() { @Override void initUser(User user){ System.out.println(user.name +   is   + user.age +  years old!  } }

Flutter 侧

1、拷贝 fidl.json 文件到 fidl 目录,执行命令 flutter packages pub run fidl_model,生成 Dart 接口类

2、绑定 Android 侧的 IUserServiceStub 通道

await Fidl.bindChannel(IUserService.CHANNEL_NAME, _channelConnection);

3、调用公开方法

await IUserService.initUser(User());

编译,运行,你将能在 Logcat 中看到 Oscar is 18 years old!。

FIDL 使用详解

这一部分是对少啰嗦,先看东西部分的补充解释,观众姥爷们可以自行跳过。

上面的例子中的 Map,一般来说,在 Java 中会对应一个类:

public class User { String name; int age; String country; Gender gender; } enum Gender { MALE, FEMALE }

如果想让 flutter 传输这个对象而不用在 flutter 层手动去编写 User 这个类,以及编写 fromJson/toJson 方法,你可以这样做:

Android 侧

1、定义一个接口,添加注解 @FIDL。这个注解将告知 annotationProcessor 生成一些接口和类的描述文件。

@FIDL public interface IUserService { void initUser(User user); }

接口方法的限制如下:

由于 dart 不支持方法重载,所以接口中不能出现同名方法

参数只支持实体类,不支持回调

由于 JSON 解码的限制,Java 需要有无参构造函数

2、Android Studio 点击 sync,或者执行:

./gradlew assembleDebug

然后就会产生一堆 json 文件,如下:

这些 json 文件就是 FIDL 和类的描述文件。没错,也会同时生成 User 引用的 Gender 类的描述文件。

同时,还会生成 IUserService 的实现 IUserServiceStub。即:

com.infiniteloop.fidl_example.IUserService.fidl.json

com.infiniteloop.fidl_example.User.json

com.infiniteloop.fidl_example.Gender.json

com.infiniteloop.fidl_example.IUserServiceStub.java

限制:只能生成有强引用关系的 FIDL 文件,被 FIDL 接口强引用的类的子类如果没有被 FIDL 接口强引用,则不会生成相应的描述文件。

3、在合适的地方打开通道,向 Flutter 公开方法

IUserServiceStub userService = new IUserServiceStub() { @Override void initUser(User user){ System.out.println(user.name +   is   + user.age +  years old!  } FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), userService);

4、如有需要,可以在合适的地方关闭通道

FidlChannel.closeChannel(userService);

关闭的消息将通知到 Flutter 侧。

Flutter 侧

1、进入到你的 flutter 项目,在 lib 目录下创建 fidl 目录,把上面的 json 文件拷贝到这个目录,然后执行:

flutter packages pub run fidl_model

然后就能在 fidl 目录下自动生成相关的 dart 类:

即:

User.dart

Gender.dart

IUserService.dart

2、绑定 Android 侧的 IUserServiceStub 通道

bool connected = await Fidl.bindChannel(IUserService.CHANNEL_NAME, _channelConnection);

_channelConnection 用于跟踪 IUserService 通道的连接状态,通道连接成功时,会回调它的 onConnected 方法; 通道连接断开时,会回调它的 onDisconnected 方法。

3、调用通道的公开方法

if (_channelConnection.connected) { await IUserService.initUser(User()); }

4、如果不再需要使用这个通道了,可以解除绑定

await Fidl.unbindChannel(IUserService.CHANNEL_NAME, _channelConnection);

当然,FIDL 的功能不止于此

1、多个参数的 FIDL 接口

void init(String name, Integer age, Gender gender, Conversation conversation);

2、带返回值的 FIDL 接口

UserInfo getUserInfo();

3、支持泛型类的生成

public class User T  { T country; } public class AUser String {}

FIDL 接口:

void initUser(AUser user);

将能在 dart 侧生成 AUser 和 User 类,且能保持继承关系。

4、传递枚举

void initEnum0(EmptyEnum e); String initEnum1(MessageStatus status);

5、传递集合、Map

void initList0(List String  ids); void initList1(Collection String  ids); void initList7(Stack String  ids); void initList10(BlockingQueue ids);

6、传递复杂对象。继承、抽象、泛型、枚举和混合类,来一个打一个。

现在,FIDL 项目只实现了从 Dart 侧调用 Android 侧的方法。还有以下工作要做:

Android 侧调用 Dart 侧的方法

其它平台和 Flutter 方法的互相调用

EventChannel,EventChannel 本质上是可以通过 MethodChannel 实现的,问题不大

搞定了对象传输,这些问题,都是小 case 啦。

对于对象的序列化和反序列化

为了能满足大佬们的定制化需求,我分别在 Java 侧和 Flutter 侧定义了序列化 / 反序列化的接口类。

Java: public interface ObjectCodec { List byte[]  encode(Object... objects);  T  T decode(byte[] input, TypeLiteral T  type); }
Dart: abstract class ObjectCodec { dynamic decode(Uint8List input); List Uint8List  encode(List objects); }

目前使用的是 JsonObjectCodec,经过 JSON 的编解码,性能会稍差。后面还希望和小伙伴们一起努力,实现更高效的编解码。

项目进度

上述提到的功能,只要是从 Flutter 侧调用 Java 侧的方法相关的,大部分都已经实现了。

我做了一个 Demo,模拟了一个在 Android 侧依赖了 IM(即时通讯)SDK,需要在 Flutter 侧聊天、获取消息、发消息的场景。以下是 Demo 的截图:

1、首页,点击按钮调用 Android 侧方法,开启聊天服务

2、聊天页面

3、发一条消息给 Lucy 并获取和 Lucy 的聊天记录

4、调用 Android 侧方法发送 N 条消息给 Wilson 并获取聊天记录

“Flutter 的 AIDL 怎么定义”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注丸趣 TV 网站,丸趣 TV 小编将为大家输出更多高质量的实用文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-15发表,共计5137字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)