Flutter如何与Native(Android)进行交互
Posted BennuCTech
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter如何与Native(Android)进行交互相关的知识,希望对你有一定的参考价值。
前言
上一篇文章《Flutter混合开发:android中如何启动Flutter》中我们介绍了如何在Native(Android项目)中启动Flutter,展示Flutter页面。但是在开发过程中,很多时候并不是简单的展示一个页面即可,还会涉及到各种交互,比如传递一些消息。
本篇文章就简单介绍一下Flutter与原生Native的三种交互方式:
BasicMessageChannel、MethodChannel和EventChannel。
BasicMessageChannel
虽然说是三种交互方式,但是其实本质都是一种,这个我们后面会解释。
先来看看BasicMessageChannel。它可以实现双方交互,发送一些简单消息,消息类型Object,但是并不是所有Object都可以,基础类型及基础类型的数组、list、map是可以的。这个可以参考BasicMessageChannel
的源码:
public void send(@Nullable T message, @Nullable final Reply<T> callback)
messenger.send(
name,
codec.encodeMessage(message),
callback == null ? null : new IncomingReplyHandler(callback));
可以看到进行了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? = null
override 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.
*/
@Nullable
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.
*/
@Nullable
T 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函数源码如下:
@UiThread
public 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
@UiThread
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
在执行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: ()
//执行完成处理
);
源码分析
我们来看一下receiveBroadcastStream
的关键源码:
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: (Flutter 如何与 Native (Android) 进行交互 | 开发者说·DTalk
Flutter 如何与 Native (Android) 进行交互 | 开发者说·DTalk
Flutter混合开发:Android中如何启动Flutter
Flutter混合开发:Android中如何启动Flutter
FlutterFlutter 混合开发 ( Flutter 与 Native 通信 | Android 端实现 EventChannel 通信 )
FlutterFlutter 混合开发 ( Flutter 与 Native 通信 | Android 端实现 MethodChannel 通信 )