之前我們介紹了如何在 Native (Android 項(xiàng)目) 中啟動(dòng) Flutter,展示 Flutter 頁面。但是在開發(fā)過程中,很多時(shí)候并不是簡(jiǎn)單的展示一個(gè)頁面即可,還會(huì)涉及到各種交互,比如傳遞一些消息。
本篇文章就簡(jiǎn)單介紹一下 Flutter 與原生 Native 的三種交互方式:
1.BasicMessageChannel
2.MethodChannel
3.EventChannel
BasicMessageChannel
雖然說是三種交互方式,但是其實(shí)本質(zhì)都是一種,這個(gè)我們后面會(huì)解釋。
先來看看 BasicMessageChannel。它可以實(shí)現(xiàn)雙方交互,發(fā)送一些簡(jiǎn)單消息,消息類型 Object,但是并不是所有 Object 都可以,基礎(chǔ)類型及基礎(chǔ)類型的數(shù)組、list、map 是可以的。這個(gè)可以參考 BasicMessageChannel 的源碼:
可以看到進(jìn)行了 encode,這個(gè) codec 一般是 StandardMessageCodec,它的 encodeMessage 函數(shù)源碼:public void send( T message, final Reply callback) {
messenger.send(
name,
codec.encodeMessage(message),
callback == null ? null : new IncomingReplyHandler(callback));
}
public ByteBuffer encodeMessage(Object message) {
if (message == null) {
return null;
}
final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();
writeValue(stream, message);
final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
buffer.put(stream.buffer(), 0, stream.size());
return buffer;
}
這里 writeValue 的源碼:
protected void writeValue(ByteArrayOutputStream stream, Object value) {
if (value == null || value.equals(null)) {
stream.write(NULL);
} else if (value == Boolean.TRUE) {
stream.write(TRUE);
} else if (value == Boolean.FALSE) {
stream.write(FALSE);
} else if (value instanceof Number) {
if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
stream.write(INT);
writeInt(stream, ((Number) value).intValue());
} else if (value instanceof Long) {
stream.write(LONG);
writeLong(stream, (long) value);
} else if (value instanceof Float || value instanceof Double) {
stream.write(DOUBLE);
writeAlignment(stream, 8);
writeDouble(stream, ((Number) value).doubleValue());
} else if (value instanceof BigInteger) {
stream.write(BIGINT);
writeBytes(stream, ((BigInteger) value).toString(16).getBytes(UTF8));
} else {
throw new IllegalArgumentException("Unsupported Number type: " + value.getClass());
}
} else if (value instanceof String) {
stream.write(STRING);
writeBytes(stream, ((String) value).getBytes(UTF8));
} else if (value instanceof byte[]) {
stream.write(BYTE_ARRAY);
writeBytes(stream, (byte[]) value);
} else if (value instanceof int[]) {
stream.write(INT_ARRAY);
final int[] array = (int[]) value;
writeSize(stream, array.length);
writeAlignment(stream, 4);
for (final int n : array) {
writeInt(stream, n);
}
} else if (value instanceof long[]) {
stream.write(LONG_ARRAY);
final long[] array = (long[]) value;
writeSize(stream, array.length);
writeAlignment(stream, 8);
for (final long n : array) {
writeLong(stream, n);
}
} else if (value instanceof double[]) {
stream.write(DOUBLE_ARRAY);
final double[] array = (double[]) value;
writeSize(stream, array.length);
writeAlignment(stream, 8);
for (final double d : array) {
writeDouble(stream, d);
}
} else if (value instanceof List) {
stream.write(LIST);
final List list = (List) value;
writeSize(stream, list.size());
for (final Object o : list) {
writeValue(stream, o);
}
} else if (value instanceof Map) {
stream.write(MAP);
final Map map = (Map) value;
writeSize(stream, map.size());
for (final Entry entry : map.entrySet()) {
writeValue(stream, entry.getKey());
writeValue(stream, entry.getValue());
}
} else {
throw new IllegalArgumentException("Unsupported value: " + value);
}
}
下面看一下如何來使用它,以 Android 端為例。
Android 端(1) 不使用 engine cache 預(yù)熱
如果不使用 engine cache,那么在 FlutterActivity 的繼承類中重寫 configureFlutterEngine:
classMainActivity:FlutterActivity(){
varchannel:BasicMessageChannel?= null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
var channel = BasicMessageChannel(flutterEngine.dartExecutor.binaryMessenger,"test" ,StringCodec.INSTANCE)
channel.setMessageHandler { message, reply ->
Log.e("recieve",message)
}
}
}
注意這里第二個(gè)參數(shù) "test" 是這通道 (channel) 的名稱,兩邊名稱一致才能進(jìn)行通信。
第三個(gè)參數(shù)是消息的編解碼器,這里我們因?yàn)槭呛?jiǎn)單的示例,消息是字符串 String,所以用 StringCodec。
StringCodec 是 MessageCodec 接口的實(shí)現(xiàn),除了它還有 BinaryCodec,JsonMessageCodec,StandardMessageCodec。另外我們還可以自己實(shí)現(xiàn) MessageCodec,實(shí)現(xiàn)它的兩個(gè)函數(shù)即可,它的源碼如下:
public interface MessageCodec<T> {
/**
* Encodes the specified message into binary.
*
* @param message the T message, possibly null.
* @return a ByteBuffer containing the encoding between position 0 and the current position, or
* null, if message is null.
*/
ByteBuffer encodeMessage(@Nullable T message);
/**
* Decodes the specified message from binary.
*
* @param message the {@link ByteBuffer} message, possibly null.
* @return a T value representation of the bytes between the given buffer's current position and
* its limit, or null, if message is null.
*/
T decodeMessage(@Nullable ByteBuffer message);
}
最后,MessageHandler 用于接受從 Flutter 傳遞過來的消息。這里簡(jiǎn)單的將消息打印出來。
當(dāng)需要向 Flutter 發(fā)送消息時(shí),執(zhí)行以下代碼即可:
channel?.send("androidcall")
(2)用 使 engine cache 預(yù)熱
一般情況我們?cè)?Application 中添加 cache,如下:class App : Application() {
companion object{
...
lateinit var flutterEngine2 : FlutterEngine
}
override fun onCreate() {
super.onCreate()
...
flutterEngine2 = FlutterEngine(this)
flutterEngine2.navigationChannel.setInitialRoute("second")
flutterEngine2.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put("second", flutterEngine2)
}
}
這里我們?yōu)?second 這個(gè) Flutter 頁面創(chuàng)建 engine 并加入 cache 進(jìn)行預(yù)熱。
如果我們想使用這個(gè) engine 發(fā)送消息,那么可以直接創(chuàng)建 BasicMessageChannel
var channel = BasicMessageChannel<String>(App.flutterEngine2.dartExecutor.binaryMessenger,"test" ,StandardMessageCodec.INSTANCE as MessageCodec<String>)
channel.setMessageHandler { message, reply ->
Log.e("recieve", message)
}
后續(xù)與上面就一樣了。
Flutter 端
步驟基本一樣,先創(chuàng)建
staticconstmessageChannel=constBasicMessageChannel("test",StringCodec());
這里通道名稱保持與 native 一致。
設(shè)置回調(diào):
messageChannel.setMessageHandler((message) async
{
print(message)
}
);
發(fā)送消息:
messageChannel.send("flutter call");
這樣就實(shí)現(xiàn)了 Native 和 Flutter 的雙向消息交互。
MethodChannel
用于雙方函數(shù)的調(diào)用,使用方法與 BasicMessageChannel 相似,其實(shí)本質(zhì)上是一樣的。我們先來看看如何使用它。
Android 端
與 BasicMessageChannel 一樣預(yù)熱和不預(yù)熱可以有兩種不同的處理,但是其實(shí)最終都是獲取到 FlutterEngine 對(duì)象,所以就不贅述了,直接使用即可。代碼如下:
//創(chuàng)建
varchannel=MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"test")
//回調(diào),根據(jù)call執(zhí)行native函數(shù)
channel.setMethodCallHandler { call, result ->
when(call.method){
"flutterCall" -> {
//執(zhí)行我們自定義的對(duì)應(yīng)函數(shù)
flutterCall(call.arguments)
}
else -> {}
}
}
這里 FlutterCall 是響應(yīng) Flutter 發(fā)送過來的請(qǐng)求,我們定義一個(gè)對(duì)應(yīng)的函數(shù)來處理,如:
fun flutterCall(arguments : Object){
Log.e("flutterCall", "message:" + arguments.toString())
}
然后我們可以通過 invokeMethod 函數(shù)來執(zhí)行 Flutter 函數(shù),如:
//執(zhí)行flutter函數(shù)
channel.invokeMethod("androidCall", "android message")
Flutter 端
流程一樣,代碼如下:
//創(chuàng)建
static const methodChannel = const MethodChannel("test");
//回調(diào),根據(jù)call執(zhí)行flutter函數(shù)
methodChannel.setMethodCallHandler((call) async {
switch(call.method){
case "androidCall":
//執(zhí)行自定義的對(duì)應(yīng)函數(shù)
androidCall(call.arguments);
break;
}
});
//執(zhí)行native函數(shù)
methodChannel.invokeMethod("flutterCall", "flutter message");
源碼分析
在分析 BasicMessageChannel 時(shí)我們知道它的 send 函數(shù)其實(shí)是調(diào)用了 messenger.send(...),這個(gè) messenger 是 BinaryMessenger,就是構(gòu)造函數(shù)的第一個(gè)參數(shù)。MethodCannel 也是一樣,它的 invokeMethod 函數(shù)源碼如下:
public void invokeMethod(String method, Object arguments, Result callback) {
messenger.send(
name,
codec.encodeMethodCall(new MethodCall(method, arguments)),
callback == null ? null : new IncomingResultHandler(callback));
}
可以看到,最終還是調(diào)用了 BinaryMessenger 的 send 函數(shù)。只不過將 invokeMethod 的兩個(gè)參數(shù) (String 類型的函數(shù)名 method 和 Object 類型的參數(shù) arguments) 封裝到 MethodCall 中。
再來看回調(diào)的處理,上面 invokeMethod 函數(shù)中可以看到,用 IncomingResultHandler 將 callback 進(jìn)行了封裝,它的關(guān)鍵源碼如下:
private final class IncomingMethodCallHandler implements BinaryMessageHandler {
private final MethodCallHandler handler;
IncomingMethodCallHandler(MethodCallHandler handler) {
this.handler = handler;
}
public void onMessage(ByteBuffer message, final BinaryReply reply) {
final MethodCall call = codec.decodeMethodCall(message);
try {
handler.onMethodCall(
call,
new Result() {
...
});
} catch (RuntimeException e) {
...
}
}
...
}
可以看到在收到消息 onMessage 后先將消息解析成 MethodCall 在執(zhí)行 callback,這樣就可以直接獲取到函數(shù)名及參數(shù)了。
通過上面我們知道 MethodChannel 和 BasicMessageChannel 本質(zhì)是一樣的,只不過經(jīng)過了一層 MethodCall 的封裝,方便直接獲取函數(shù)名和參數(shù)。
EventChannel
EventChannel 與上面兩個(gè)都不太一樣,它是 Flutter 發(fā)起,native 處理并返回結(jié)果,F(xiàn)lutter 再處理結(jié)果。說它是單方向通道也不是很準(zhǔn)確,但是 native 無法主動(dòng)發(fā)起,所以更像是一個(gè) c/s 結(jié)構(gòu)。
先來看看如何使用。
Android 端
同樣需要 FlutterEngine 對(duì)象,代碼如下:
//創(chuàng)建
varchannel=EventChannel(flutterEngine.dartExecutor.binaryMessenger,"test")
//設(shè)置處理handler
channel.setStreamHandler(object : StreamHandler(), EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
//根據(jù)arguments處理
arguments?.let {
...
//將處理結(jié)果返回,可能成功也可能失敗
events?.success("android back")
//events?.error("errorcode","errormssage",null)
//如果不返回,即success和error都不執(zhí)行,則需要執(zhí)行endOfStream
//events?.endOfStream()
}
}
overridefunonCancel(arguments:Any?){
//執(zhí)行取消操作
}
})
上面提到 Native 無法主動(dòng)發(fā)起,所以就沒有類似上面 send 或 invokeMethod 函數(shù)。
Flutter 端
通過 receiveBroadcastStream 來發(fā)送 event 請(qǐng)求,并通過 linsten 來監(jiān)聽返回。
//創(chuàng)建
static const eventChannel = const EventChannel("test");
//發(fā)送arguments給native處理,并監(jiān)聽結(jié)果
eventChannel.receiveBroadcastStream(["flutter event"]).listen((event){
//返回成功結(jié)果,處理
print(event.toString());
}, onError: (event){
//返回錯(cuò)誤結(jié)果,處理
},onDone:(){
//執(zhí)行完成處理
});
源碼分析
我們來看一下 receiveBroadcastStream 的關(guān)鍵源碼:
Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
final MethodChannel methodChannel = MethodChannel(name, codec);
late StreamController<dynamic> controller;
controller = StreamController<dynamic>.broadcast(onListen: () async {
binaryMessenger.setMessageHandler(name, (ByteData? reply) async {
...
});
try {
await methodChannel.invokeMethod<void>('listen', arguments);
} catch (exception, stack) {
...
}
}, onCancel: () async {
binaryMessenger.setMessageHandler(name, null);
try {
await methodChannel.invokeMethod<void>('cancel', arguments);
} catch (exception, stack) {
...
}
});
return controller.stream;
}
可以看到 EventChannel 本質(zhì)上就是 MethodChannel,只不過執(zhí)行了幾個(gè)預(yù)先定義好的函數(shù),如 listen 和 cancel。這樣對(duì) MethodChannel 進(jìn)行再次封裝,可以更簡(jiǎn)單的進(jìn)行事件傳遞。
總結(jié)
上面我們展示了三種交互方式的使用,并解析了其內(nèi)部的聯(lián)系。其實(shí)可以看到三種方式最終其實(shí)都是使用了 BinaryMessenger 這一抽象類的默認(rèn)實(shí)現(xiàn) _DefaultBinaryMessenger。所以如果我們通過 BinaryMessenger 來實(shí)現(xiàn)一套自己特別的消息傳遞機(jī)制。
"開發(fā)者說·DTalk" 面向中國(guó)開發(fā)者們征集 Google 移動(dòng)應(yīng)用 (apps & games) 相關(guān)的產(chǎn)品/技術(shù)內(nèi)容。歡迎大家前來分享您對(duì)移動(dòng)應(yīng)用的行業(yè)洞察或見解、移動(dòng)開發(fā)過程中的心得或新發(fā)現(xiàn)、以及應(yīng)用出海的實(shí)戰(zhàn)經(jīng)驗(yàn)總結(jié)和相關(guān)產(chǎn)品的使用反饋等。我們由衷地希望可以給這些出眾的中國(guó)開發(fā)者們提供更好展現(xiàn)自己、充分發(fā)揮自己特長(zhǎng)的平臺(tái)。我們將通過大家的技術(shù)內(nèi)容著重選出優(yōu)秀案例進(jìn)行谷歌開發(fā)技術(shù)專家 (GDE) 的推薦。
原文標(biāo)題:Flutter 如何與 Native (Android) 進(jìn)行交互 | 開發(fā)者說·DTalk
文章出處:【微信公眾號(hào):谷歌開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
審核編輯:湯梓紅
-
Android
+關(guān)注
關(guān)注
12文章
3945瀏覽量
127931 -
源碼
+關(guān)注
關(guān)注
8文章
652瀏覽量
29452 -
交互
+關(guān)注
關(guān)注
1文章
69瀏覽量
14831
原文標(biāo)題:Flutter 如何與 Native (Android) 進(jìn)行交互 | 開發(fā)者說·DTalk
文章出處:【微信號(hào):Google_Developers,微信公眾號(hào):谷歌開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論