你们好!今天给你们安利一个自认为比较重磅的Flutter开源项目。java
Flutter的产品定义是一个高性能的跨平台的移动UI框架,可以用一套代码同时构建出Android/iOS/Web/MacOS应用。做为一套UI框架,它不具有一些系统的接口,天然仍是避免不了跟原生打交道。因而乎,它提出了名为platform channel
的东西,用于flutter和原生灵活的交换数据。如下为了描述方便,用Android代指原生。git
燃鹅,燃鹅,燃鹅,它只支持一些基础的数据类型和数据结构的传输,例如bool/int/long/byte/char/String/byte[]/List/Map等。github
所以,当你想传输复杂点的数据,你只能包装成Map,相似这样:shell
await _channel.invokeMethod('initUser', {'name': 'Oscar', 'age': 16, 'gender': 'MALE', 'country': 'China'});
而后再在Android层hard code,解析出不一样的key对应的不一样数据。若是你是一个纯fluter项目,且之后也没有和原生打交道的打算,或者只是须要进行简单的交互,那这种作法也无可厚非。而当你的项目已经有很大的一部分原生代码或者你须要使用第三方不支持flutter的lib库的时候,就意味着你须要编写大量向上面那样的模板代码。可见效率低下,且可维护性差。这时,你会想,能传输对象就行了!json
而当你想传输对象时:数据结构
抱歉,没门,只能给你一个尴尬又不是礼貌的危笑。固然,也不是不能够,咱们能够在原生上层把对象序列化成json对象,而后在flutter层再把json转成flutter的对象,一样效率不好。框架
学过Android的应该都知道AIDL(Android Interface Defination Language),即Android接口定义语言。Android中有一种高级的跨进程通讯方式——Binder,可是想要使用Binder须要了解一些Binder的机制和API,须要编写大量的模板代码。Android为了解决这个问题,尝试把使用Binder的方法作的小白一点。因而定义了AIDL,告诉开发者,你的接口文件必须按照我规定的来写,你要跨进程传输的对象必须实现Parcelable接口。而后,Android给你生成了一个Service.Stub类,偷偷的在背后把对象的序列化、反序列化的工做都给作了。开发者使用这个Stub类就能轻松上手Binder这种高级的跨进程通信方法。(😋😋😋我编的,差很少啦)ide
FIDL(Flutter Interface Defination Language)即Flutter接口定义语言,它的使命和AIDL很相似,悄悄把对象的序列化、反序列化、自动生成代码这种“脏活累活”给作了。开发者在原生代码中看到的类,能经过@FIDL注解标记,自动在Dart侧生成和原生代码中同样的类。FIDL是一面镜子,把各类原平生台的类影射到Dart中,把Dart中的类影射到各个原平生台。oop
一、首先是Java类:性能
public class User { String name; int age; String country; Gender gender; } enum Gender { MALE, FEMALE }
二、定义FIDL接口
@FIDL public interface IUserService { void initUser(User user); }
三、执行几个命令
四、Android侧在合适的地方打开IUserServiceStub通道(IUserServiceStub是IUserService的实现类,自动生成的)
FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() { @Override void initUser(User user){ System.out.println(user.name + " is " + user.age + "years old!"); } }
五、Flutter侧使用IUserService 通道
// 绑定通道(IUserService类是自动生产的哦) await Fidl.bindChannel(IUserService.CHANNEL_NAME, _channelConnection); // 使用User类(`User类`以及它使用的`Gender枚举`是自动生成的哦) User user = User(); user.name = 'Oscar'; user.age = 18; user.gender = Gender.MALE; user.country = 'China'; // 调用通道方法 await IUserService.initUser(user);
编译,运行,你将能在Logcat中看到Oscar is 18 years old!。
这一部分是对少啰嗦,先看东西
部分的补充解释,观众姥爷们能够自行跳过。
上面的例子中的Map,通常来讲,在Java中会对应一个类:
public class User { String name; int age; String country; Gender gender; } enum Gender { MALE, FEMALE }
若是想让flutter传输这个对象而不用在flutter层手动去编写User这个类,以及编写fromJson/toJson方法,你能够这样作:
一、定义一个接口,添加注解@FIDL。这个注解将告知annotationProcessor生成一些接口和类的描述文件。
@FIDL public interface IUserService { void initUser(User user); }
二、Android Studio点击sync,或者执行:
./gradlew assembleDebug
而后就会产生一堆json文件,以下:
这些json文件就是FIDL和类的描述文件。没错,也会同时生成User引用的Gender类的描述文件。
同时,还会生成IUserService的实现IUserServiceStub。即:
三、进入到你的flutter项目,在lib目录下建立fidl目录,把上面的json文件拷贝到这个目录,而后执行:
flutter packages pub run fidl_model
而后就能在fidl目录下自动生成相关的dart类:
即:
四、使用
a. Android侧在合适的地方打开IUserServiceStub通道
FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() { @Override void initUser(User user){} }
b. Flutter侧绑定IUserService通道
await Fidl.bindChannel(IUserService.CHANNEL_NAME, _channelConnection);
c、Flutter调用通道方法
await IUserService.initUser(User());
d、Flutter能够在合适的时候接触绑定
await Fidl.unbindChannel(IUserService.CHANNEL_NAME, _channelConnection);
e、Android侧能够在合适的时候关闭通道
FidlChannel.closeChannel(userServiceStub);
一、多个参数的FIDL接口
void init(String name, Integer age, Gender gender, Conversation conversation);
二、带返回值的FIDL接口
UserInfo getUserInfo();
三、支持泛型类的生成
public class User<T> { T country; } public class AUser<String>{}
FIDL接口:
void initUser(AUser user);
将能在dart侧生成AUser和User类,且能保持继承关系。
四、传递枚举
void initEnum0(EmptyEnum e); String initEnum1(MessageStatus status);
五、传递集合、Map
void initList0(List<String> ids); void initList1(Collection<String> ids); void initList7(Stack<String> ids); void initList10(BlockingQueue ids);
六、传递复杂对象。继承、抽象、泛型、枚举和混合类,来一个打一个。
如今,FIDL项目只实现了从Dart侧调用Android侧的方法。还有如下工做要作:
搞定了对象传输,这些问题,都是小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的截图:
一、首页,点击按钮调用Android侧方法,开启聊天服务
二、聊天页面
三、发一条消息给Lucy并获取和Lucy的聊天记录
四、调用Android侧方法发送N条消息给Wilson并获取聊天记录
上次作开源项目已是3年前了,那是一个Android原生刷新控件,TwinklingRefreshLayout,github 3.7k stars。后来因为工做的缘由,成天跟Android Framework、C/C++打交道,精力也都是放到了公司的业务上,也没有时间和精力维护下去。
那么今天我想发布的这个Flutter开源项目,是想经过社区的力量,和你们一块儿把项目维护下去。我在GayHub上创建了一个组织,https://github.com/flutterFIDL。稍晚一点时间,我会把项目开源出来,一两天内,代码会放在这里,https://github.com/flutterFIDL/FIDL。你们记得投币、点赞、收藏,一键3连(你们若是以为这个项目能很好解决跨平台通讯问题,给个star能够嘛😶)。阿不,我须要一个团队跟我一块儿发展这个项目,但愿你熟悉Flutter开发,了解Android和Java开发,热爱开源,熟悉Flutter+iOS / Flutter + Web其中的一种,并有相关项目经历,加我vx: w354850839。
这样一个库,香吗?告诉我,有多香。😉
欢迎留言评论,告诉我你的Flutter和原生通讯的使用场景,以及遇到的痛点和问题~