Flutter(11)— Platform Channel通信(下)
向Android发送数据
前面一篇已经介绍了Android主动向Flutter发送数据请求的流程,这一篇主页看看Flutter向Android发送数据的流程。有了前面的基础,反过来基本区别不大。
先看一下Flutter端的Demo, 创建了一个MethodChannel
,然后调用了invokeMethod
方法, 唯一的区别在于Flutter端的方法调用没有回调。但是有返回值,Android端MethodChannel 实际是包装了一下返回结果。
const platform = const MethodChannel('com.test.native/logger');
platform.invokeMethod("d", {"tag": "MyApp", "log": "build"});
类图
先看一下Dart中和消息相关的类图,整体结构和前面Java的差不多。 只是BinaryMessenger的Callback中缺少了一个Reply参数, 这是因为Dart中send是有返回值的 (应该得益于async/await机制)。DefaultBinaryMessenger扮演了Java中DartMessenger的角色。
Dart发送数据
//创建MethodChannel
const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), this.binaryMessenger = defaultBinaryMessenger ])
: assert(name != null),
assert(binaryMessenger != null),
assert(codec != null);
//发送数据
Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async {
assert(method != null);
final ByteData result = await binaryMessenger.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null) {
throw MissingPluginException('No implementation found for method $method on channel $name');
}
final T typedResult = codec.decodeEnvelope(result);
return typedResult;
}
前面已经介绍过了,Dart使用的是defaultBinaryMessenger来发送数据,最终调用的他的send方法。 注意这里用了await。
@override
Future<ByteData> send(String channel, ByteData message) {
final MessageHandler handler = _mockHandlers[channel];
if (handler != null)
return handler(message);
return _sendPlatformMessage(channel, message);
}
Dart这边包含了一个mockHandler用来测试,正常是null,所以不用管它。最终调用了内部方法:
Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
final Completer<ByteData> completer = Completer<ByteData>();
// ui.window is accessed directly instead of using ServicesBinding.instance.window
// because this method might be invoked before any binding is initialized.
// This issue was reported in #27541. It is not ideal to statically access
// ui.window because the Window may be dependency injected elsewhere with
// a different instance. However, static access at this location seems to be
// the least bad option.
ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
try {
completer.complete(reply);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message response callback'),
));
}
});
return completer.future;
}
这里使用了Dart中的window进行消息发送。他实际调用了C++层Window.cc的方法。 因为Dart这边没有Reply,所以也没有replayid。
Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
Dart_Handle data_handle) {
UIDartState* dart_state = UIDartState::Current();
//把Dart层的callback包装成C++层的PlatformMessageResponseDart
fml::RefPtr<PlatformMessageResponse> response;
if (!Dart_IsNull(callback)) {
response = fml::MakeRefCounted<PlatformMessageResponseDart>(
tonic::DartPersistentValue(dart_state, callback),
dart_state->GetTaskRunners().GetUITaskRunner());
}
// 处理Dart发来的消息,把name,data,response转换为PlatformMessage
if (Dart_IsNull(data_handle)) {
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(name, response));
} else {
tonic::DartByteData data(data_handle);
const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(
name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
response));
}
return Dart_Null();
}
这里调用HandlePlatformMessage的顺序是 : RuntimeController(WindowClient) –> Engine(RuntimeDelegate) –> Shell(Engine::Delegate) –> PlatformViewAndroid
void PlatformViewAndroid::HandlePlatformMessage(
fml::RefPtr<flutter::PlatformMessage> message) {
JNIEnv* env = fml::jni::AttachCurrentThread();
fml::jni::ScopedJavaLocalRef<jobject> view = java_object_.get(env);
if (view.is_null())
return;
// 因为Java层有Repaly,所有这里构建一个response_Id,并且把id和response对应关系存放到了pending_responses_
int response_id = 0;
if (auto response = message->response()) {
response_id = next_response_id_++;
pending_responses_[response_id] = response;
}
// 从PlatformMessage中取出channel 和 data,转换为java的数据类型, 然后
auto java_channel = fml::jni::StringToJavaString(env, message->channel());
if (message->hasData()) {
fml::jni::ScopedJavaLocalRef<jbyteArray> message_array(
env, env->NewByteArray(message->data().size()));
env->SetByteArrayRegion(
message_array.obj(), 0, message->data().size(),
reinterpret_cast<const jbyte*>(message->data().data()));
message = nullptr;
// This call can re-enter in InvokePlatformMessageXxxResponseCallback.
FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
message_array.obj(), response_id);
} else {
message = nullptr;
// This call can re-enter in InvokePlatformMessageXxxResponseCallback.
FlutterViewHandlePlatformMessage(env, view.obj(), java_channel.obj(),
nullptr, response_id);
}
}
这里和上一篇从Dart返回数据给Java是一样的
static jmethodID g_handle_platform_message_method = nullptr;
void FlutterViewHandlePlatformMessage(JNIEnv* env,
jobject obj,
jstring channel,
jobject message,
jint responseId) {
env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message,
responseId);
FML_CHECK(CheckException(env));
}
// 指向的Java方法
g_handle_platform_message_method =
env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
"(Ljava/lang/String;[BI)V");
这里很熟了,回到FluterJNI, 熟悉的java代码,熟悉的platformMessageHandler
private void handlePlatformMessage(@NonNull final String channel, byte[] message, final int replyId) {
if (platformMessageHandler != null) {
platformMessageHandler.handleMessageFromDart(channel, message, replyId);
}
// TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391)
}
最终回到了DartMessenger
@Override
public void handleMessageFromDart(
@NonNull final String channel,
@Nullable byte[] message,
final int replyId
) {
Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
// 根据chennel name找到对应的handler
BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
if (handler != null) {
try {
Log.v(TAG, "Deferring to registered handler to process message.");
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.");
flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
}
}
Android处理消息
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
// flutter -> Android
new MethodChannel(getFlutterView(), "com.test.native/logger").setMethodCallHandler((call, result) -> {
if (call.method.equals("d")) {
Logger.d(call.argument("tag"), call.argument("log"));
result.success(null);
} else if (call.method.equals("e")) {
Logger.d(call.argument("tag"), call.argument("log"));
result.success(null);
} else {
result.notImplemented();
}
});
}
这个是Android的Demo,接收Flutter发来的消息。和上一篇一样,看看是如何把2端的channel关联起来的。
// MethodChannel
public void setMethodCallHandler(final @Nullable MethodCallHandler handler) {
messenger.setMessageHandler(name,
handler == null ? null : new IncomingMethodCallHandler(handler));
}
// DartMessenage
@Override
public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) {
if (handler == null) {
Log.v(TAG, "Removing handler for channel '" + channel + "'");
messageHandlers.remove(channel);
} else {
Log.v(TAG, "Setting handler for channel '" + channel + "'");
messageHandlers.put(channel, handler);
}
}
DartMessenage 把创建的handler和channel 保存在了Map中。 所以当收到消息后,通过BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel);
就能找到对应的handler进行处理。
依赖关系
Android向Flutter发送数据时,是在ServiceBinding初始化时建立的关系,但是没有找到ServiceBinding初始化的实际,只是推测是framework加载时。 而从Flutter向Android发数据时,是从c++层调用FlutterJni的方法, 而FlutterJNI在FlutterView创建时就有了。
而Dart的功能是基于FlutterView的,所以任意时间Flutter给Android发送消息,都是可以收到的,反过来就不一定,因为可能Dart那边channel还没有注册好。
Android返回结果
找到Channle对应的BinaryMessageHandler后,就交给我们自己的代码去处理了。
final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
handler.onMessage(buffer, new Reply(flutterJNI, replyId));
在Dart端使用async/await实现了异步操作,而Java端不支持只能使用回调了,所以这里创建了一个Reply对象。这个对象也是实现在DartMessenger中。
private static class Reply implements BinaryMessenger.BinaryReply {
@NonNull
private final FlutterJNI flutterJNI;
private final int replyId;
private final AtomicBoolean done = new AtomicBoolean(false);
Reply(@NonNull FlutterJNI flutterJNI, int replyId) {
this.flutterJNI = flutterJNI;
this.replyId = replyId;
}
@Override
public void reply(@Nullable ByteBuffer reply) {
if (done.getAndSet(true)) {
throw new IllegalStateException("Reply already submitted");
}
if (reply == null) {
flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
} else {
flutterJNI.invokePlatformMessageResponseCallback(replyId, reply, reply.position());
}
}
}
这里如果自定义的Channle处理完成后调用了reply接口,就会把replyId和data返回到Flutter那边。
void PlatformViewAndroid::InvokePlatformMessageResponseCallback(
JNIEnv* env,
jint response_id,
jobject java_response_data,
jint java_response_position) {
if (!response_id)
return;
auto it = pending_responses_.find(response_id);
if (it == pending_responses_.end())
return;
uint8_t* response_data =
static_cast<uint8_t*>(env->GetDirectBufferAddress(java_response_data));
std::vector<uint8_t> response = std::vector<uint8_t>(
response_data, response_data + java_response_position);
auto message_response = std::move(it->second);
pending_responses_.erase(it);
message_response->Complete(
std::make_unique<fml::DataMapping>(std::move(response)));
}
这里根据response_id
从pending_responses_
中找到PlatformMessageResponseDart
对象, 把数据转换为Dart数据,调用Dart层的PlatformMessageResponseCallback
对象
void PlatformMessageResponseDart::Complete(std::unique_ptr<fml::Mapping> data) {
if (callback_.is_empty())
return;
FML_DCHECK(!is_complete_);
is_complete_ = true;
ui_task_runner_->PostTask(fml::MakeCopyable(
[callback = std::move(callback_), data = std::move(data)]() mutable {
std::shared_ptr<tonic::DartState> dart_state =
callback.dart_state().lock();
if (!dart_state)
return;
tonic::DartState::Scope scope(dart_state);
Dart_Handle byte_buffer = WrapByteData(std::move(data));
tonic::DartInvoke(callback.Release(), {byte_buffer});
}));
}
这里的callback就是 _DefaultBinaryMessenger
中发送时的第三个参数
ui.window.sendPlatformMessage(channel, message, (ByteData reply) {
try {
completer.complete(reply); //执行这里
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('during a platform message response callback'),
));
}
});
这里factory Completer() => new _AsyncCompleter<T>();
最终异步执行return completer.future
把结果返回给了调用方。
Platform Channle 通信流程图
Channel和编解码
Flutter中系统定义了3种Channel,使用在不同的场景。这里以Java层的为例。
这3个channel用途不同,所以发送数据时接收的参数也不同。但DartMessenger发送的ByteBuffer类型的数据。所以每个channle中有一个codec
,作用是同ByteBuffer进行数据转换。系统会默认给channel一个对应的codec,我们在创建的channel的时候也可以自己来指定。
MethodChannel
前面的例子都是使用Methodchannel,Java层和Dart层的定义基本相同。它主要作用就行进行跨语言的方法调用。传递的数据是要调用的方法名和参数。 所以它默认使用的时MethodCodec的子类StandardMethodCodec使用二进制传输, 还支持JSONMethodCodec使用Json字符串传输。
@Override
public ByteBuffer encodeMethodCall(MethodCall methodCall) {
final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();
messageCodec.writeValue(stream, methodCall.method);
messageCodec.writeValue(stream, methodCall.arguments);
final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
buffer.put(stream.buffer(), 0, stream.size());
return buffer;
}
@Override
public MethodCall decodeMethodCall(ByteBuffer methodCall) {
methodCall.order(ByteOrder.nativeOrder());
final Object method = messageCodec.readValue(methodCall);
final Object arguments = messageCodec.readValue(methodCall);
if (method instanceof String && !methodCall.hasRemaining()) {
return new MethodCall((String) method, arguments);
}
throw new IllegalArgumentException("Method call corrupted");
}
主要的工作就是处理MethodCall
对象和ByteBuffer
之间的相互转换。 Dart层的定义也一样。然后对结果进行包装和解析。
@override
ByteData encodeMethodCall(MethodCall call) {
final WriteBuffer buffer = WriteBuffer();
messageCodec.writeValue(buffer, call.method);
messageCodec.writeValue(buffer, call.arguments);
return buffer.done();
}
@override
MethodCall decodeMethodCall(ByteData methodCall) {
final ReadBuffer buffer = ReadBuffer(methodCall);
final dynamic method = messageCodec.readValue(buffer);
final dynamic arguments = messageCodec.readValue(buffer);
if (method is String && !buffer.hasRemaining)
return MethodCall(method, arguments);
else
throw const FormatException('Invalid method call');
}
在dart端,MethodChannel还提供了invokeListMethod
和invokeMapMethod
,可以一次调用多个Java方法。 同时还提供了一个子类OptionalMethodChannel
。 因为当channel没有注册或不存在时,默认会抛出MissingPluginException
异常,如果使用OptionalMethodChannel
, 就会返回null而不抛出异常。
EventChannel
从UML图上看到,它内部也是默认使用StandardMethodCodec
,但是和MethodChannle不同的是,它并没有定义一个发送的接口,只有一个setStreamHandler
用于接收Flutter发来的数据。EventChannel主要作用就是用于监听Java层的消息。
new EventChannel(getFlutterView(), "com.test.native/event").setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
}
@Override
public void onCancel(Object arguments) {
}
});
使用很简单,StreamHandler有两个回调方法,一个监听一个取消。在看下Dart端的用法,也只提供了receiveBroadcastStream
方法
const platform_event = const EventChannel('com.test.native/event');
void _onEnvent(Object obj){
}
void _onError(Object obj){
}
platform_event.receiveBroadcastStream().listen(_onEnvent, onError: _onError);
在Java层中EventMethod没有发送接口,看起来两边都是接收,那谁来发送呢?具体看一下Dart层receiveBroadcastStream的代码:
这里使用了Stream流异步处理方式,Dart | 什么是Stream。对于一个Stream来说,经过输入–>处理–>输出这样一个流程。其中StreamController
定义了处理的过程。 所以下面代码主要是构建了消息的处理过程,而上面的listen
是用来监听Stream处理结果的。
Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
final MethodChannel methodChannel = MethodChannel(name, codec); //创建了一个MethodChannle
创建_AsyncBroadcastStreamController
StreamController<dynamic> controller;
controller = StreamController<dynamic>.broadcast(onListen: () async {
// Stream开始工作时,设置channle消息监听
defaultBinaryMessenger.setMessageHandler(name, (ByteData reply) async {
if (reply == null) {
controller.close();
} else {
try {
controller.add(codec.decodeEnvelope(reply)); //注册一个监听
} on PlatformException catch (e) {
controller.addError(e);
}
}
return null;
});
try {
await methodChannel.invokeMethod<void>('listen', arguments); //调用java层 listen方法
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('while activating platform stream on channel $name'),
));
}
}, onCancel: () async {
//Stream停止工作时,取消channel消息监听
defaultBinaryMessenger.setMessageHandler(name, null);
try {
await methodChannel.invokeMethod<void>('cancel', arguments); //调用java层cancel方法
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: ErrorDescription('while de-activating platform stream on channel $name'),
));
}
});
return controller.stream; //_ControllerStream
}
上面的方法中创建了一个MethodChannel
,可以调用Java层的listen
和cancel
的方法(对应Java层StreamHandler的两个回调函数)。Dart端使用了Stream
来进行异步处理。
通过broadcast
创建了一个_AsyncBroadcastStreamController
对象,并且设置了onListen
和onCancel
事件的处理方法(注意这里onListen、onCancel是Stream处理的概念,和我们自己发送的数据无关)。我们调用Stream
的listen
开始监听时,会执行StreamController
中的onListen
方法。
// stream_impl.dart
StreamSubscription<T> listen(void onData(T data),
{Function onError, void onDone(), bool cancelOnError}) {
cancelOnError = identical(true, cancelOnError);
StreamSubscription<T> subscription =
_createSubscription(onData, onError, onDone, cancelOnError);
_onListen(subscription);
return subscription;
}
所以抛开Stream,EventChannel工作流程如下:
- Java层创建EventChannel并设置StreamHandler
- Dart层创建EventChannel,通过MethodChannle调用Java层的listen方法
- Java层收到listen方法调用,转换为StreamHandler的onListen方法
- Java层把Dart层需要监听的数据,通过EventChannel.EventSink不断的发送给Dart,使用codec编码
- Dart层收到数据后,通过codec解码,然后传递给listen中的onEvent方法
- Dart层在onEvent方法中处理返回的数据
从上面可以看到,EventChannel是用于Dart监听Java的事件,比如电量、网络连接状态等等。本质还是数据传输。 如果要从Java层获取Dart的某个状态,我们可以根据这个自己实现一个Channel。
BasicMessageChannel
前面2个Channel主要用于方法调用和状态监听,而BasicMessageChannel
主要用户数据传输。BasicMessageChannel没有指定默认的codec,而是需要自己指定MessageCodec
。
public interface MessageCodec<T> {
/**
* Encodes the specified message into binary.
*/
@Nullable
ByteBuffer encodeMessage(@Nullable T message);
/**
* Decodes the specified message from binary.
*/
@Nullable
T decodeMessage(@Nullable ByteBuffer message);
}
它下面4个codec实现都比较简单,这里就不用多介绍了。
系统Channel
前面介绍过,在FlutterView创建的时候,会创建系统使用的Channel。
// Create all platform channels
navigationChannel = new NavigationChannel(dartExecutor);
keyEventChannel = new KeyEventChannel(dartExecutor);
lifecycleChannel = new LifecycleChannel(dartExecutor);
localizationChannel = new LocalizationChannel(dartExecutor);
platformChannel = new PlatformChannel(dartExecutor);
systemChannel = new SystemChannel(dartExecutor);
settingsChannel = new SettingsChannel(dartExecutor);
这里看一下LifecycleChannel
是用来通知Flutter当前APP的生命周期的。在FlutterView中对应生命周期函数中会调用(来自于FlutterActivityDelegate)
public void onStart() {
lifecycleChannel.appIsInactive();
}
public void onPause() {
lifecycleChannel.appIsInactive();
}
public void onPostResume() {
for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
listener.onPostResume();
}
lifecycleChannel.appIsResumed();
}
public void onStop() {
lifecycleChannel.appIsPaused();
}
看一下它的实现
public class LifecycleChannel {
@NonNull
public final BasicMessageChannel<String> channel;
public LifecycleChannel(@NonNull DartExecutor dartExecutor) {
this.channel = new BasicMessageChannel<>(dartExecutor, "flutter/lifecycle", StringCodec.INSTANCE);
}
public void appIsInactive() {
Log.v(TAG, "Sending AppLifecycleState.inactive message.");
channel.send("AppLifecycleState.inactive");
}
public void appIsResumed() {
Log.v(TAG, "Sending AppLifecycleState.resumed message.");
channel.send("AppLifecycleState.resumed");
}
public void appIsPaused() {
Log.v(TAG, "Sending AppLifecycleState.paused message.");
channel.send("AppLifecycleState.paused");
}
}
创建了一个BasicMessageChannel
对象, 使用StringCodec
来进行编码,通知是单向的。在Flutter中我们前面看到过,当Engine收到消息后,会检查是否是对应的系统消息。
void Engine::DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message) {
if (message->channel() == kLifecycleChannel) {
if (HandleLifecyclePlatformMessage(message.get()))
return;
} else if (message->channel() == kLocalizationChannel) {
if (HandleLocalizationPlatformMessage(message.get()))
return;
} else if (message->channel() == kSettingsChannel) {
HandleSettingsPlatformMessage(message.get());
return;
}
if (runtime_controller_->IsRootIsolateRunning() &&
runtime_controller_->DispatchPlatformMessage(std::move(message))) {
return;
}
// If there's no runtime_, we may still need to set the initial route.
if (message->channel() == kNavigationChannel)
HandleNavigationPlatformMessage(std::move(message));
}
这个把消息交给了HandleLifecyclePlatformMessage
进行处理
bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) {
const auto& data = message->data();
std::string state(reinterpret_cast<const char*>(data.data()), data.size());
if (state == "AppLifecycleState.paused" ||
state == "AppLifecycleState.suspending") {
activity_running_ = false;
StopAnimator();
} else if (state == "AppLifecycleState.resumed" ||
state == "AppLifecycleState.inactive") {
activity_running_ = true;
StartAnimatorIfPossible();
}
这里内部处理很简单,根据状态来控制动画。系统消息的处理明显简单不少,注意这里并没有使用codec去解码,因为在C++层直接处理掉了。 而且也不像自定义的Channel,还要找对应的Handler。