StandardMethodCodec
public class StandardMessageCodec implements MessageCodec<Object> {
public static final StandardMessageCodec INSTANCE = new StandardMessageCodec();
// 根据数据类型,先向stream中写入类型标志值,及上述提到的14个常量值,然后将具体的
// value值转成byte继续写入到stream
protected void writeValue(ByteArrayOutputStream stream, Object value) {
if (value == 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) { // 1.写入类型标志值
stream.write(INT);
// value转为byte,继续写入到stream中
writeInt(stream, ((Number) value).intValue());
}
.......
}else if (value instanceof String) {
stream.write(STRING);
writeBytes(stream, ((String) value).getBytes(UTF8));
}
.......
}
// writeValue()方法反向过程,原理一致
protected final Object readValue(ByteBuffer buffer) {
.......
}
}
在StandardMessageCodec中最重要的两个方法是writeValue()
和readValue()
.前者用于将value值写入到字节输出流ByteArrayOutputStream中,后者从字节缓冲数组中读取.在Android返回电量的过程中,假设电量值为100,该值转换成二进制数据流程为:首先向字节流stream中写入表示int类型的标志值3,再将100转为4个byte,继续写入到字节流stream中.当Dart中接受到该二进制数据后,先读取第一个byte值,根据此值得知后面需要读取一个int类型的数据,随后读取后面4个byte,并将其转为dart类型中int类型.
Handler
Flutter中定义了一套Handler用于处理经过Codec解码后消息.在使用Platform Channel时,需要为其设置对应的Handler,实际上就是为其注册一个对应BinaryMessageHandler,二进制数据会被BinaryMessageHanler进行处理,首先使用Codec进行解码操作,然后再分发给具体Handler进行处理.与三种Platform Channel相对应,Flutter中也定义了三种Handler:
- MessageHandler: 用于处理字符串或者半结构化消息,定义在BasicMessageChannel中.
public final class BasicMessageChannel<T> {
......
public interface MessageHandler<T> {
// onMessage()用于处理来自Flutter中的消息,
// 该接受两个参数:T类型的消息以及用于异步返回T类型的result
void onMessage(T message, Reply<T> reply);
}
......
}
- MethodCallHandler: 用于处理方法调用,定义在MethodChannel中.
- StreamHandler: 用于事件流通信,定义在EventChannel中.
MethodChannel调用原理
Dart -> Native
class MethodChannel {
// 构造方法,通常我们只需要指定该channel的name,
const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);
// name作为通道的唯一标志符,用于区分不同的通道调用
final String name;
// 用于方法调用过程的编码
final MethodCodec codec;
// 用于发起异步平台方法调用,需要指定方法名,以及可选方法参数
Future<dynamic> invokeMethod(String method, [dynamic arguments]) async {
assert(method != null);
// 将一次方法调用中需要的方法名和方法参数封装为MethodCall对象,然后使用MethodCodec对该
// 对象进行进行编码操作,最后通过BinaryMessages中的send方法发起调用
final dynamic result = await BinaryMessages.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null)
throw MissingPluginException('No implementation found for method $method on channel $name');
return codec.decodeEnvelope(result);
}
}
final MethodChannel _channel = new MethodChannel('flutter.io/player')
Channel名称作为MethodChannel的唯一标识符,用于区分不同的MethodChannel对象.
拿到MethodChannel对象后,通过调用其invokeMethod()
方法用于向平台发起一次调用.在invokeMethod()
方法中会将一次方法调中的方法名method和方法参数arguments封装为MethodCall对象,然后使用MethodCodec对其进行二进制编码,最后通过BinaryMessages.send()
发起平台方法调用请求.
// BinaryMessages类中提供了用于发送和接受平台插件的二进制消息.
class BinaryMessages {
......
static Future<ByteData> send(String channel, ByteData message) {
final _MessageHandler handler = _mockHandlers[channel];
// 在没有设置Mock Handler的情况下,继续调用_sendPlatformMessage()
if (handler != null)
return handler(message);
return _sendPlatformMessage(channel, message);
}
static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
final Completer<ByteData> completer = Completer<ByteData>();
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: 'during a platform message response callback',
));
}
});
return completer.future;
}
......
}
class Window{
......
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) {
final String error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null)
throw new Exception(error);
}
// 和Java类似,Dart中同样提供了Native方法用于调用底层C++/C代码的能力
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage';
.......
}
上述过程最终会调用到ui.Window._sendPlatformMessage()
方法,该方法是一个native方法,这与Java中JNI技术非常类似.
在调用该Native方法中,我们向native层发送了三个参数:
- name: String类型,代表Channel名称
- data: ByteData类型,代表之前封装的二进制数据
- callback: Function类型,用于结果回调
_sendPlatformMessage()
具体实现在 Window.cc 中:
void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"Window_defaultRouteName", DefaultRouteName, 1, true},
{"Window_scheduleFrame", ScheduleFrame, 1, true},
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
{"Window_reportUnhandledException", ReportUnhandledException, 2, true},
});
}
void _SendPlatformMessage(Dart_NativeArguments args) {
// 最终调用SendPlatformMessage函数
tonic::DartCallStatic(&SendPlatformMessage, args);
}
Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
const tonic::DartByteData& data) {
UIDartState* dart_state = UIDartState::Current();
// 1.只能在main iolate调用平台方法
if (!dart_state->window()) {
// Must release the TypedData buffer before allocating other Dart objects.
data.Release();
return tonic::ToDart(
"Platform messages can only be sent from the main isolate");
}
// 此处response的作用?
fml::RefPtr<PlatformMessageResponse> response;
if (!Dart_IsNull(callback)) {
response = fml::MakeRefCounted<PlatformMessageResponseDart>(
tonic::DartPersistentValue(dart_state, callback),
dart_state->GetTaskRunners().GetUITaskRunner());
}
// 2.核心方法调用
if (Dart_IsNull(data.dart_handle())) {
dart_state->window()->client()->HandlePlatformMessage(
fml::MakeRefCounted<PlatformMessage>(name, response));
} else {
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
class RuntimeController final : public WindowClient {
......
private:
RuntimeDelegate& client_;
.......
void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
......
}
// runtime_controller.cc
void RuntimeController::HandlePlatformMessage(
fml::RefPtr<PlatformMessage> message) {
client_.HandlePlatformMessage(std::move(message));
}
在运行过程中,不同的平台有运行机制不同,需要不同的处理策略,因此RuntimeController中相关的方法实现都被委托到了不同的平台实现类RuntimeDelegate
中,即上述代码中client_,定义如下:
class Engine final : public blink::RuntimeDelegate {
........
}
// engine.cc
void Engine::HandlePlatformMessage(
fml::RefPtr<blink::PlatformMessage> message) {
// kAssetChannel值为flutter/assets
if (message->channel() == kAssetChannel) {
HandleAssetPlatformMessage(std::move(message));
} else {
delegate_.OnEngineHandlePlatformMessage(std::move(message));
}
}
void Shell::OnEngineHandlePlatformMessage(
fml::RefPtr<blink::PlatformMessage> message) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
// kSkiaChannel值为flutter/skia
if (message->channel() == kSkiaChannel) {
HandleEngineSkiaMessage(std::move(message));
return;
}
// 其他情况下,向PlatformTaskRunner中添加Task
task_runners_.GetPlatformTaskRunner()->PostTask(
[view = platform_view_->GetWeakPtr(), message = std::move(message)]() {
if (view) {
view->HandlePlatformMessage(std::move(message));
}
});
}
Engine在处理message时,如果该message值等于kAssetChannel
,即flutter/assets,表示当前操作想要获取资源,因此会调用HandleAssetPlatformMessage()
来走获取资源的逻辑;否则调用delegate_.OnEngineHandlePlatformMessage()
方法.
OnEngineHandlePlatformMessage
在接收到消息后,首先判断要调用Channel是否是flutter/skia,如果是则调用HandleEngineSkiaMessage()
进行处理后返回,否则向PlatformTaskRunner添加一个Task,在该Task中会调用PlatformView的HandlePlatformMessage()
方法.根据运行平台不同PlatformView有不同的实现,对于Android平台而言,其具体实现是PlatformViewAndroid;对于IOS平台而言,其实现是PlatformViewIOS.
void PlatformViewAndroid::HandlePlatformMessage(
fml::RefPtr<blink::PlatformMessage> message) {
JNIEnv* env = fml::jni::AttachCurrentThread();
fml::jni::ScopedJavaLocalRef<jobject> view = java_object_.get(env);
if (view.is_null())
return;
// response_id在Flutter调用平台代码时,会传到平台代码中,后续平台代码需要回传数据时
// 需要用到它
int response_id = 0;
// 如果message中有response(response类型为PlatformMessageResponseDart),则需要对
// response_id进行自增
if (auto response = message->response()) {
response_id = next_response_id_++;
// pending_responses是一个Map结构
pending_responses_[response_id] = response;
}
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);
}
}
该方法在接受PlatformMessage类型的消息时,如果消息中有response,则对response_id自增,并以response_id为key,response为value存放在变量pending_responses_
中.
接着将消息中的channel和data数据转成Java可识别的数据,并连同response_id一同作为FlutterViewHandlePlatformMessage()
方法的参数,最终通过JNI调用的方式传递到Java层.
// platform_android_jni.cc
static jmethodID g_handle_platform_message_method = nullptr;
void FlutterViewHandlePlatformMessage(JNIEnv* env,
jobject obj,
jstring channel,
jobject message,
jint responseId) {
// g_handle_platform_message_method中指向Java层的方法
// 其在 RegisterApi 中被初始化
env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message,
responseId);
FML_CHECK(CheckException(env));
}
bool PlatformViewAndroid::Register(JNIEnv* env) {
......
g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
......
return RegisterApi(env);
}
bool RegisterApi(JNIEnv* env) {
......
g_handle_platform_message_method =
env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
"(Ljava/lang/String;[BI)V");
......
}
不难看出g_flutter_jni_class
指向FlutterJNI.java类,g_handle_platform_message_method
指向FlutterJN.javaI中的handlePlatformMessage()
方法.