共計 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 小編將為大家輸出更多高質量的實用文章!