本文主要是介紹FlutterPlugin,涉及到原理和使用飞崖。
Flutter Plugin提供Android或者iOS的底層封裝烂叔,在Flutter層提供組件功能,使Flutter可以較方便的調(diào)取Native的模塊固歪。很多平臺(tái)相關(guān)性或者對(duì)于Flutter實(shí)現(xiàn)起來比較復(fù)雜的部分蒜鸡,都可以封裝成Plugin胯努。
那么flutter和native如何相互調(diào)用呢,原理如下
消息在client和host之間通過平臺(tái)通道(platform channels)來進(jìn)行的逢防,之間的通訊都是異步
的叶沛。
我們先介紹下MethodChannel的實(shí)現(xiàn)原理,使用方式以Android端為例:
//1忘朝、注冊(cè)通道
MethodChannel channel = new MethodChannel(getFlutterView(),"flutter/channel");
//2灰署、設(shè)置回調(diào),被call回調(diào)
channel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
//通知到flutter端
result.success(“返回值”);
}
});
//3局嘁、主動(dòng)call flutter的方法
channel.invokeMethod("callFlutter", "params", new MethodChannel.Result() {
@Override
public void success(Object result) {
//呼叫成功
}
@Override
public void error(String errorCode, String errorMessage, Object errorDetails) {
//呼叫出現(xiàn)異常
}
@Override
public void notImplemented() {
//flutter沒有對(duì)應(yīng)的實(shí)現(xiàn)方法
}
});
其實(shí)MethodChannel
中的構(gòu)造函數(shù)第一個(gè)參數(shù)就是BinaryMessenger
溉箕,這個(gè)BinaryMessenger
是在DartExecuter
中初始化的,而BinaryMessenger
的實(shí)現(xiàn)類中的send函數(shù)
最終是對(duì)DartMessenger
的一層包裝悦昵,所以我們看消息是如何發(fā)送出去的肴茄,直接看DartMessenger
的send函數(shù)
@Override
public void send(
@NonNull String channel,
@Nullable ByteBuffer message,
@Nullable BinaryMessenger.BinaryReply callback
) {
Log.v(TAG, "Sending message with callback over channel '" + channel + "'");
int replyId = 0;
if (callback != null) {
replyId = nextReplyId++;
pendingReplies.put(replyId, callback);
}
if (message == null) {
flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
} else {
flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId);
}
}
如果有方法回調(diào)callback,就會(huì)生成一個(gè)replyId,然后把方法回調(diào)和replyId加入到一個(gè)map但指,如果沒有就直接發(fā)送消息了寡痰。
如果消息體為空,就發(fā)送一個(gè)空消息棋凳,如果有消息內(nèi)容氓癌,就把消息發(fā)送過去。還會(huì)攜帶一個(gè)replyId贫橙,這個(gè)會(huì)在回調(diào)的時(shí)候有用。發(fā)送消息是通過flutterJni
分發(fā)方法反粥,最終調(diào)到了c++層面卢肃,通過c++的dart虛擬機(jī),把消息傳遞給了flutter才顿。
如何收到flutter端的消息呢
同樣的接受消息也是在DartExecutor
莫湘,通過onAttachToJNI
會(huì)建立通道關(guān)聯(lián),同樣的消息會(huì)經(jīng)過DartMessenger
郑气,如果是收到原生調(diào)用flutter以后的回復(fù)幅垮,會(huì)調(diào)用
@Override
public void handlePlatformMessageResponse(int replyId, @Nullable byte[] reply) {
Log.v(TAG, "Received message reply from Dart.");
BinaryMessenger.BinaryReply callback = pendingReplies.remove(replyId);
if (callback != null) {
try {
Log.v(TAG, "Invoking registered callback for reply from Dart.");
callback.reply(reply == null ? null : ByteBuffer.wrap(reply));
} catch (Exception ex) {
Log.e(TAG, "Uncaught exception in binary message reply handler", ex);
}
}
}
首先根據(jù)replyId找到剛才發(fā)送消息時(shí)保存的callback,找到了尾组,就把reply轉(zhuǎn)化為bytebuff調(diào)用出去忙芒。BinaryReply會(huì)將消息回調(diào)回去。
如果是從flutter主動(dòng)調(diào)動(dòng)的消息稍微負(fù)責(zé)一些讳侨,也是類似的原理呵萨,只不過多了一步根據(jù)通道名字找到對(duì)象的處理類。
//三個(gè)參數(shù)跨跨,通道名字潮峦,消息的字節(jié),從flutter端傳遞過來的replyId,
public void handleMessageFromDart(
@NonNull final String channel,
@Nullable byte[] message,
final int replyId
) {
Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
//根據(jù)通道名字找到對(duì)象的處理類"
BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
if (handler != null) {
try {
Log.v(TAG, "Deferring to registered handler to process message.");
//解碼數(shù)據(jù)并調(diào)用onMessage傳遞消息 忱嘹,注意一下這個(gè)Reply
final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
handler.onMessage(buffer, new Reply(flutterJNI, replyId));
} catch (Exception ex) {
Log.e(TAG, "Uncaught exception in binary message listener", ex);
//異常處理
flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
} else {
Log.v(TAG, "No registered handler for message. Responding to Dart with empty reply message.");
//未實(shí)現(xiàn)通道處理
flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
}
像Flutter官方提供的shared_preferences嘱腥,就是通過通道來實(shí)現(xiàn)的。
FlutterPlugin還有另外一個(gè)用法拘悦,就是直接在Flutter端由Native渲染齿兔。比如視頻播放插件video_player。
通常有兩種做法窄做,一種是PlatformView,另外一種是Texture(俗稱外接紋理)愧驱。其中PlatformView區(qū)分Android和iOS,在Android平上上叫做 AndroidView椭盏,而在iOS平臺(tái)组砚,叫UIKitView。
前面提到的MethodChannel
可以向flutter傳輸數(shù)據(jù)掏颊,但是如果將一個(gè)視頻或者圖片的數(shù)據(jù)傳到flutter會(huì)很繁重糟红,所以Flutter提供了一種基于Texture的數(shù)據(jù)共享機(jī)制,而以上兩種方式原理都是基于Texture。
Texture https://api.flutter.dev/flutter/widgets/Texture-class.html
下圖是視頻插件的實(shí)現(xiàn)原理圖