前言

大家好!今天给大家安利一个自认为比较重磅的Flutter开源项目。

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中有一种高级的跨进程通信方式——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接口

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

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

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

FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() {@Overridevoid 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() {@Overridevoid 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并获取聊天记录

文章不易,如果大家喜欢这篇文章,或者对你有帮助希望大家多多,**点赞,转发,关注 **哦。文章会持续更新的。绝对干货!!!

你是否还记得?那些年我们一起追过的(FIDL:Flutter界的AIDL)相关推荐

  1. 一个时代的印记:还记得那些年我们逃课去的网吧

    一一哥Sun 2020-03-06 11:52:16 对于大多数80后,90后来说,有一个地方应该是你青春回忆里很重要的一个所在,那就是网吧! 相信看我这篇文章的小伙伴们,可能90%以上都去过网吧,曾 ...

  2. 那些年的java游戏_那些年我们曾经玩过的游戏,你还记得几个

    标题:那些年我们曾经玩过的游戏,你还记得几个 随着时间长河的推进,我们已经长大了.你还记得我们那些年一起玩过的游戏么? 弹弓 一般用树枝做弓架,也可以用旱伞的伞骨做弹弓架.要买弹力很大的像皮筋,就和那 ...

  3. 那些年我们一起追过的缓存写法(二)

    感谢园子里的同学对上一篇的支持,很高兴楼主的一些经验及想法能够对大家有一些帮助. 上次主要讨论缓存读写这块各种代码实现,本篇就上次的问题继续来,看看那些年折腾过的各种缓存做法. 阅读目录: 缓存预热 ...

  4. 70、80后、90后,小时候开学的场景!你还记得吗?

    转眼又是一年开学季 角色转变,现在是目送孩子们去上学 还记得那些年, 我们开学的日子吗? 那时候的农村的学校 水泥地面很少 任何地方都适合杂草生长 一个暑假过完 教室前后.操场.... 全部被杂草占领 ...

  5. 星梦缘陈彦妃_还记得《星梦缘》的女主吗,她现在变成这样了

    如今<为了你我愿意热爱整个世界>.<扶摇>等偶像剧凭借男女主演的人气,每每有新看点出现都能上热搜,成为年轻一代谈论的话题.自始至终偶像剧都是一个很卖座的题材,但你还记得曾经经典 ...

  6. 你还记得当初为什么进入IT行业吗?

    2019独角兽企业重金招聘Python工程师标准>>> 说到这个问题,小编相信不少童鞋开始忆往昔峥嵘岁月,那个少年为了心中的改变世界的理想,进入了这个行业,但是呢,有一群人画风就不一 ...

  7. 如果你还记得我[转载]

    这是我在[飞雪连天]博客中看到的一篇文章,文章不错,借用一下. 最后一次见到方强是在他家搬离村庄的那天早上. 那天早上,我和母亲从菜地拔菜回来,还在路上,我就听见村子里的人跟母亲说,庆文嫂家要搬到市里 ...

  8. 哨兵机器人钢力士_还记得秒杀X战警的哨兵机器人吗?在漫威原著里,X战警更憋屈...

    还记得<X战警:逆转未来>中的哨兵机器人吗?他们出现在这部电影的开场部分,哨兵机器人对X战警造成了非常大的困扰. 在漫威漫画原著里呢,哨兵机器人的历史也颇为复杂,他们的程序设定就是&quo ...

  9. 还记得当年你是如何接触Python的吗?

    @图片自制byunsplash 阅读文本大概需要 3 分钟. 绘画:琪琪  |  作者:xiaoyu 要说目前什么语言最受欢迎,我想Python肯定是其中之一了吧.Python语言以其简单的操作.强大 ...

最新文章

  1. Gromacs 5.1.4 在CentOS7下GPU加速版的安装
  2. 医疗实体链接(标准化)论文解读 (附代码) A Lightweight Neural Model for Biomedical Entity Linking
  3. java 循环依赖_java – 如何在Gradle中解决循环依赖
  4. cxgrid列高度行宽度定义
  5. 深度学习(四十)——深度强化学习(3)Deep Q-learning Network(2), DQN进化史
  6. python项目部署nginx_【python】Django web项目部署(Nginx+uwsgi)
  7. Django(五)中间件
  8. 红橙Darren视频笔记 AOP简介
  9. python散点图密度颜色_Python实现彩色散点图绘制(利用色带对散点图进行颜色渲染)...
  10. LitePal的使用
  11. 软件开发人员的简历项目经验怎么写?
  12. php之RSA加密解密
  13. 导论计算机论文题目,计算机类论文题目
  14. MySQL插入emoji表情错误的3种解决方案,Incorrect string value: '\xF0\x9F\x98\x84'
  15. jquery H5 好用的编辑器umeditor
  16. 转自一个计算机毕业生的求职经验
  17. Geekon移动电源概念版
  18. 利用Python实现图片信息隐藏
  19. [网站搭建] 阿里云搭建个人网站及域名绑定
  20. iOS实现微信发送位置效果

热门文章

  1. 写作小课堂:如何写好工作邮件?
  2. Spark配置参数中英文对照
  3. c语言程序怎么记忆,c语言的记忆方法指导
  4. android性能排行,2018年11月Android手机性能排行 麒麟980碾压高通蝉联前三
  5. LeetCode Algorithm 225. 用队列实现栈
  6. Node.js的用户交互(使用readline模块)
  7. 腾讯云服务器4核8g支持多少人同时在线?并发用户数计算
  8. 谷歌收购手势识别公司Flutter
  9. catia零件隐藏显示
  10. @分词算法FMM(正向最大匹配)