大家好!今天給大家安利一個(gè)自認(rèn)為比較重磅的Flutter開源項(xiàng)目酷麦。
Flutter的產(chǎn)品定義是一個(gè)高性能的跨平臺(tái)的移動(dòng)UI框架矿卑,能夠用一套代碼同時(shí)構(gòu)建出Android/iOS/Web/MacOS應(yīng)用。作為一套UI框架沃饶,它不具備一些系統(tǒng)的接口母廷,自然還是避免不了跟原生打交道轻黑。于是乎,它提出了名為platform channel
的東西琴昆,用于flutter和原生靈活的交換數(shù)據(jù)氓鄙。以下為了描述方便,用Android代指原生业舍。
燃鵝抖拦,燃鵝,燃鵝舷暮,它只支持一些基礎(chǔ)的數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu)的傳輸态罪,例如bool/int/long/byte/char/String/byte[]/List/Map等。
因此下面,當(dāng)你想傳輸復(fù)雜點(diǎn)的數(shù)據(jù)复颈,你只能包裝成Map,類似這樣:
await _channel.invokeMethod('initUser',
{'name': 'Oscar', 'age': 16, 'gender': 'MALE', 'country': 'China'});
然后再在Android層hard code沥割,解析出不同的key對(duì)應(yīng)的不同數(shù)據(jù)券膀。如果你是一個(gè)純fluter項(xiàng)目,且以后也沒(méi)有和原生打交道的打算驯遇,或者只是需要進(jìn)行簡(jiǎn)單的交互,那這種做法也無(wú)可厚非蓄髓。而當(dāng)你的項(xiàng)目已經(jīng)有很大的一部分原生代碼或者你需要使用第三方不支持flutter的lib庫(kù)的時(shí)候叉庐,就意味著你需要編寫大量向上面那樣的模板代碼』岷龋可見效率低下陡叠,且可維護(hù)性差。這時(shí)肢执,你會(huì)想枉阵,能傳輸對(duì)象就好了!
而當(dāng)你想傳輸對(duì)象時(shí):
抱歉预茄,沒(méi)門兴溜,只能給你一個(gè)尷尬又不是禮貌的危笑。當(dāng)然耻陕,也不是不可以拙徽,我們可以在原生上層把對(duì)象序列化成json對(duì)象,然后在flutter層再把json轉(zhuǎn)成flutter的對(duì)象诗宣,同樣效率很差膘怕。
FIDL是什么
學(xué)過(guò)Android的應(yīng)該都知道AIDL(Android Interface Defination Language),即Android接口定義語(yǔ)言召庞。Android中有一種高級(jí)的跨進(jìn)程通信方式——Binder岛心,但是想要使用Binder需要了解一些Binder的機(jī)制和API来破,需要編寫大量的模板代碼。Android為了解決這個(gè)問(wèn)題忘古,嘗試把使用Binder的方法做的小白一點(diǎn)徘禁。于是定義了AIDL,告訴開發(fā)者存皂,你的接口文件必須按照我規(guī)定的來(lái)寫晌坤,你要跨進(jìn)程傳輸?shù)膶?duì)象必須實(shí)現(xiàn)Parcelable接口。然后旦袋,Android給你生成了一個(gè)Service.Stub類骤菠,偷偷的在背后把對(duì)象的序列化、反序列化的工作都給做了疤孕。開發(fā)者使用這個(gè)Stub類就能輕松上手Binder這種高級(jí)的跨進(jìn)程通訊方法商乎。(??????我編的,差不多啦)
FIDL(Flutter Interface Defination Language)即Flutter接口定義語(yǔ)言祭阀,它的使命和AIDL很類似鹉戚,悄悄把對(duì)象的序列化、反序列化专控、自動(dòng)生成代碼這種“臟活累活”給做了抹凳。開發(fā)者在原生代碼中看到的類,能通過(guò)@FIDL注解標(biāo)記伦腐,自動(dòng)在Dart側(cè)生成和原生代碼中一樣的類赢底。FIDL是一面鏡子,把各種原生平臺(tái)的類影射到Dart中柏蘑,把Dart中的類影射到各個(gè)原生平臺(tái)幸冻。
少啰嗦,先看東西
1咳焚、首先是Java類:
public class User {
String name;
int age;
String country;
Gender gender;
}
enum Gender {
MALE, FEMALE
}
2洽损、定義FIDL接口
@FIDL
public interface IUserService {
void initUser(User user);
}
3、執(zhí)行幾個(gè)命令
4革半、Android側(cè)在合適的地方打開IUserServiceStub通道(IUserServiceStub是IUserService的實(shí)現(xiàn)類碑定,自動(dòng)生成的)
FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() {
@Override
void initUser(User user){
System.out.println(user.name + " is " + user.age + "years old!");
}
}
5、Flutter側(cè)使用IUserService 通道
// 綁定通道(IUserService類是自動(dòng)生產(chǎn)的哦)
await Fidl.bindChannel(IUserService.CHANNEL_NAME, _channelConnection);
// 使用User類(`User類`以及它使用的`Gender枚舉`是自動(dòng)生成的哦)
User user = User();
user.name = 'Oscar';
user.age = 18;
user.gender = Gender.MALE;
user.country = 'China';
// 調(diào)用通道方法
await IUserService.initUser(user);
編譯又官,運(yùn)行不傅,你將能在Logcat中看到Oscar is 18 years old!。
FIDL使用詳解
這一部分是對(duì)少啰嗦赏胚,先看東西
部分的補(bǔ)充解釋访娶,觀眾姥爺們可以自行跳過(guò)。
上面的例子中的Map觉阅,一般來(lái)說(shuō)崖疤,在Java中會(huì)對(duì)應(yīng)一個(gè)類:
public class User {
String name;
int age;
String country;
Gender gender;
}
enum Gender {
MALE, FEMALE
}
如果想讓flutter傳輸這個(gè)對(duì)象而不用在flutter層手動(dòng)去編寫User這個(gè)類秘车,以及編寫fromJson/toJson方法,你可以這樣做:
1劫哼、定義一個(gè)接口叮趴,添加注解@FIDL。這個(gè)注解將告知annotationProcessor生成一些接口和類的描述文件权烧。
@FIDL
public interface IUserService {
void initUser(User user);
}
2眯亦、Android Studio點(diǎn)擊sync,或者執(zhí)行:
./gradlew assembleDebug
然后就會(huì)產(chǎn)生一堆json文件般码,如下:
這些json文件就是FIDL和類的描述文件妻率。沒(méi)錯(cuò),也會(huì)同時(shí)生成User引用的Gender類的描述文件板祝。
同時(shí)宫静,還會(huì)生成IUserService的實(shí)現(xiàn)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
3券时、進(jìn)入到你的flutter項(xiàng)目孤里,在lib目錄下創(chuàng)建fidl目錄,把上面的json文件拷貝到這個(gè)目錄橘洞,然后執(zhí)行:
flutter packages pub run fidl_model
然后就能在fidl目錄下自動(dòng)生成相關(guān)的dart類:
即:
- User.dart
- Gender.dart
- IUserService.dart
4捌袜、使用
a. Android側(cè)在合適的地方打開IUserServiceStub通道
FidlChannel.openChannel(getFlutterEngine().getDartExecutor(), new IUserServiceStub() {
@Override
void initUser(User user){}
}
b. Flutter側(cè)綁定IUserService通道
await Fidl.bindChannel(IUserService.CHANNEL_NAME, _channelConnection);
c、Flutter調(diào)用通道方法
await IUserService.initUser(User());
d炸枣、Flutter可以在合適的時(shí)候接觸綁定
await Fidl.unbindChannel(IUserService.CHANNEL_NAME, _channelConnection);
e琢蛤、Android側(cè)可以在合適的時(shí)候關(guān)閉通道
FidlChannel.closeChannel(userServiceStub);
當(dāng)然,F(xiàn)IDL的功能不止于此
1抛虏、多個(gè)參數(shù)的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側(cè)生成AUser和User類迂猴,且能保持繼承關(guān)系。
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傻寂、傳遞復(fù)雜對(duì)象息尺。繼承、抽象疾掰、泛型搂誉、枚舉和混合類,來(lái)一個(gè)打一個(gè)静檬。
當(dāng)然炭懊,F(xiàn)IDL能做的不止于此
現(xiàn)在并级,F(xiàn)IDL項(xiàng)目只實(shí)現(xiàn)了從Dart側(cè)調(diào)用Android側(cè)的方法。還有以下工作要做:
- Android側(cè)調(diào)用Dart側(cè)的方法
- 其它平臺(tái)和Flutter方法的互相調(diào)用
- EventChannel侮腹,EventChannel本質(zhì)上是可以通過(guò)MethodChannel實(shí)現(xiàn)的嘲碧,問(wèn)題不大
搞定了對(duì)象傳輸,這些問(wèn)題父阻,都是小case啦愈涩。
對(duì)于對(duì)象的序列化和反序列化
為了能滿足大佬們的定制化需求,我分別在Java側(cè)和Flutter側(cè)定義了序列化/反序列化的接口類加矛。
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履婉,經(jīng)過(guò)JSON的編解碼,性能會(huì)稍差荒椭。后面還希望和小伙伴們一起努力谐鼎,實(shí)現(xiàn)更高效的編解碼。
項(xiàng)目進(jìn)度
上述提到的功能趣惠,只要是從Flutter側(cè)調(diào)用Java側(cè)的方法相關(guān)的狸棍,大部分都已經(jīng)實(shí)現(xiàn)了。
我做了一個(gè)Demo味悄,模擬了一個(gè)在Android側(cè)依賴了IM(即時(shí)通訊)SDK草戈,需要在Flutter側(cè)聊天、獲取消息侍瑟、發(fā)消息的場(chǎng)景唐片。以下是Demo的截圖:
1、首頁(yè)涨颜,點(diǎn)擊按鈕調(diào)用Android側(cè)方法费韭,開啟聊天服務(wù)
2、聊天頁(yè)面
3庭瑰、發(fā)一條消息給Lucy并獲取和Lucy的聊天記錄
4星持、調(diào)用Android側(cè)方法發(fā)送N條消息給Wilson并獲取聊天記錄
最后
上次做開源項(xiàng)目已經(jīng)是3年前了,那是一個(gè)Android原生刷新控件弹灭,TwinklingRefreshLayout督暂,github 3.7k stars。后來(lái)由于工作的原因穷吮,整天跟Android Framework逻翁、C/C++打交道,精力也都是放到了公司的業(yè)務(wù)上捡鱼,也沒(méi)有時(shí)間和精力維護(hù)下去八回。
那么今天我想發(fā)布的這個(gè)Flutter開源項(xiàng)目,是想通過(guò)社區(qū)的力量,和大家一起把項(xiàng)目維護(hù)下去辽社。我在GayHub上建立了一個(gè)組織伟墙,https://github.com/flutterFIDL。稍晚一點(diǎn)時(shí)間滴铅,我會(huì)把項(xiàng)目開源出來(lái)戳葵,一兩天內(nèi),代碼會(huì)放在這里汉匙,https://github.com/flutterFIDL/FIDL拱烁。大家記得投幣、點(diǎn)贊噩翠、收藏,一鍵3連(大家如果覺得這個(gè)項(xiàng)目能很好解決跨平臺(tái)通信問(wèn)題擅笔,給個(gè)star可以嘛??)猛们。阿不狞洋,我需要一個(gè)團(tuán)隊(duì)跟我一起發(fā)展這個(gè)項(xiàng)目吉懊,希望你熟悉Flutter開發(fā)浆竭,了解Android和Java開發(fā)缤沦,熱愛開源,熟悉Flutter+iOS / Flutter + Web其中的一種届巩,并有相關(guān)項(xiàng)目經(jīng)歷瘾英,加我vx: w354850839。
這樣一個(gè)庫(kù)煌集,香嗎卷拘?告訴我乍赫,有多香诈皿。??
歡迎留言評(píng)論截歉,告訴我你的Flutter和原生通信的使用場(chǎng)景凉逛,以及遇到的痛點(diǎn)和問(wèn)題~