Flutter如何与Native(Android)进行交互
目錄
前言
BasicMessageChannel
Android端-
(1)不使用engine?cache預熱
(2)使用engine cache預熱
Flutter端-
MethodChannel
Android端=
Flutter端=
源碼分析=
EventChannel
Android端+
Flutter端+
源碼分析+
總結
前言
上一篇文章Flutter混合開發:Android中如何啟動Flutter中我們介紹了如何在Native(Android項目)中啟動Flutter,展示Flutter頁面。但是在開發過程中,很多時候并不是簡單的展示一個頁面即可,還會涉及到各種交互,比如傳遞一些消息。
本篇文章就簡單介紹一下Flutter與原生Native的三種交互方式:
BasicMessageChannel、MethodChannel和EventChannel。
BasicMessageChannel
雖然說是三種交互方式,但是其實本質都是一種,這個我們后面會解釋。
先來看看BasicMessageChannel。它可以實現雙方交互,發送一些簡單消息,消息類型Object,但是并不是所有Object都可以,基礎類型及基礎類型的數組、list、map是可以的。這個可以參考BasicMessageChannel的源碼:
可以看到進行了encode,這個codec一般是StandardMessageCodec,它的encodeMessage函數源碼:
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預熱
如果不使用engine cache,那么在FlutterActivity的繼承類中重寫configureFlutterEngine:
class MainActivity : FlutterActivity() {var channel : BasicMessageChannel? = nulloverride fun configureFlutterEngine(flutterEngine: FlutterEngine) {super.configureFlutterEngine(flutterEngine)var channel = BasicMessageChannel<String>(flutterEngine.dartExecutor.binaryMessenger,"test" ,StringCodec.INSTANCE)channel.setMessageHandler { message, reply ->Log.e("recieve", message)}} }注意這里第二個參數"test"是這通道(channel)的名稱,兩邊名稱一致才能進行通信。
第三個參數是消息的編解碼器,這里我們因為是簡單的示例,消息是字符串String,所以用StringCodec。
StringCodec是MessageCodec接口的實現,除了它還有BinaryCodec,JsonMessageCodec,StandardMessageCodec。另外我們還可以自己實現MessageCodec,實現它的兩個函數即可,它的源碼如下:
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.*/@NullableByteBuffer 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.*/@NullableT decodeMessage(@Nullable ByteBuffer message); }最后,MessageHandler用于接受從Flutter傳遞過來的消息。這里簡單的將消息打印出來。
當需要向flutter發送消息時,執行:
channel?.send("android call")即可
(2)使用engine cache預熱
一般情況我們在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)} }這里我們為second這個flutter頁面創建engine并加入cache進行預熱。
如果我們想使用這個engine發送消息,那么可以直接創建BasicMessageChannel
var channel = BasicMessageChannel<String>(App.flutterEngine2.dartExecutor.binaryMessenger,"test" ,StandardMessageCodec.INSTANCE as MessageCodec<String>) channel.setMessageHandler { message, reply ->Log.e("recieve", message) }后續與上面就一樣了。
Flutter端-
步驟基本一樣,先創建
static const messageChannel = const BasicMessageChannel("test", StringCodec());這里通道名稱保持與native一致。
設置回調:
messageChannel.setMessageHandler((message) async{print(message)});發送消息:
messageChannel.send("flutter call");這樣就實現了Native和Flutter的雙向消息交互。
MethodChannel
用于雙方函數的調用,使用方法與BasicMessageChannel相似,其實本質上是一樣的。我們先來看看如何使用它。
Android端=
與BasicMessageChannel一樣預熱和不預熱可以有兩種不同的處理,但是其實最終都是獲取到FlutterEngine對象,所以就不贅述了,直接使用即可。代碼如下:
//創建var channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger,"test")//回調,根據call執行native函數channel.setMethodCallHandler { call, result ->when(call.method){"flutterCall" -> {//執行我們自定義的對應函數flutterCall(call.arguments)}else -> {}}}這里flutterCall是響應Flutter發送過來的請求,我們定義一個對應的函數來處理,如:
fun flutterCall(arguments : Object){Log.e("flutterCall", "message:" + arguments.toString())}然后我們可以通過invokeMethod函數來執行Flutter函數,如:
//執行flutter函數channel.invokeMethod("androidCall", "android message")Flutter端=
流程一樣,代碼如下:
//創建 static const methodChannel = const MethodChannel("test"); //回調,根據call執行flutter函數methodChannel.setMethodCallHandler((call) async {switch(call.method){case "androidCall"://執行自定義的對應函數androidCall(call.arguments);break;}}); //執行native函數 methodChannel.invokeMethod("flutterCall", "flutter message");源碼分析=
在分析BasicMessageChannel時我們知道它的send函數其實是調用了messenger.send(...),這個messenger是BinaryMessenger,就是構造函數的第一個參數。MethodCannel也是一樣,它的invokeMethod函數源碼如下:
@UiThreadpublic void invokeMethod(String method, @Nullable Object arguments, @Nullable Result callback) {messenger.send(name,codec.encodeMethodCall(new MethodCall(method, arguments)),callback == null ? null : new IncomingResultHandler(callback));}可以看到,最終還是調用了BinaryMessenger的send函數。只不過將invokeMethod的兩個參數(String類型的函數名method和Object類型的參數arguments)封裝到MethodCall中。
再來看回調的處理,上面invokeMethod函數中可以看到,用IncomingResultHandler將callback進行了封裝,它的關鍵源碼如下:
private final class IncomingMethodCallHandler implements BinaryMessageHandler {private final MethodCallHandler handler;IncomingMethodCallHandler(MethodCallHandler handler) {this.handler = handler;}@Override@UiThreadpublic void onMessage(ByteBuffer message, final BinaryReply reply) {final MethodCall call = codec.decodeMethodCall(message);try {handler.onMethodCall(call,new Result() {...});} catch (RuntimeException e) {...}}...}可以看到在收到消息onMessage后先將消息解析成MethodCall在執行callback,這樣就可以直接獲取到函數名及參數了。
通過上面我們知道MethodChannel和BasicMessageChannel本質是一樣的,只不過經過了一層MethodCall的封裝,方便直接獲取函數名和參數。
EventChannel
EventChannel與上面兩個都不太一樣,它是flutter發起,native處理并返回結果,flutter再處理結果。說它是單方向通道也不是很準確,但是native無法主動發起,所以更像是一個c/s結構。
先來看看如何使用。
Android端+
同樣需要FlutterEngine對象,代碼如下:
//創建 var channel = EventChannel(flutterEngine.dartExecutor.binaryMessenger,"test") //設置處理handler channel.setStreamHandler(object : StreamHandler(), EventChannel.StreamHandler {override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {//根據arguments處理arguments?.let {...//將處理結果返回,可能成功也可能失敗events?.success("android back")//events?.error("errorcode", "errormssage", null)//如果不返回,即success和error都不執行,則需要執行endOfStream//events?.endOfStream()}}override fun onCancel(arguments: Any?) {//執行取消操作} })上面提到Native無法主動發起,所以就沒有類似上面send或invokeMethod函數。
Flutter端+
通過receiveBroadcastStream來發送event請求,并通過linsten來監聽返回。
//創建 static const eventChannel = const EventChannel("test"); //發送arguments給native處理,并監聽結果 eventChannel.receiveBroadcastStream(["flutter event"]).listen((event) {//返回成功結果,處理print(event.toString()); }, onError: (event){//返回錯誤結果,處理 }, onDone: (){//執行完成處理 });源碼分析+
總結
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的Flutter如何与Native(Android)进行交互的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Flutter混合开发:Android中
- 下一篇: 如何发布Android Library到