Flutter之MethodChannel

前言

Flutter是Google使用Dart语言开发的一套移动应用开发框架。它不同于其他开发框架:

  1. 因为Flutter使用AOT预编译代码为机器码,所以它的运行效率更高。

  2. Flutter的UI控件并没有使用底层的原生控件,而是使用Skia渲染引擎绘制而成,因为不依赖底层控件,所以多端一致性非常好。

  3. Flutter的扩展性也非常强,开发者可以通过Plugin与Native进行通信。

简介

Flutter使用了一个灵活的系统,允许您调用特定平台的API,无论在Android上的Java或Kotlin代码中,还是iOS上的ObjectiveC或Swift代码中均可用。

  1. Flutter平台特定的API支持不依赖于代码生成,而是依赖于灵活的消息传递的方式:

  2. 应用的Flutter部分通过平台通道(platform channel)将消息发送到其应用程序的所在的宿主(iOS或Android)。

宿主监听的平台通道,并接收该消息。然后它会调用特定于该平台的API(使用原生编程语言) - 并将响应发送回客户端,即应用程序的Flutter部分。

框架概述: 平台通道

使用平台通道在客户端(Flutter UI)和宿主(平台)之间传递消息,如下图所示:https://i.imgur.com/ZfAk0ST.png)]

消息和响应是异步传递的,以确保用户界面保持响应(不会挂起)。

在客户端,MethodChannel (API)可以发送与方法调用相对应的消息。 在宿主平台上,MethodChannel 在Android((API) 和 FlutterMethodChannel iOS (API) 可以接收方法调用并返回结果。这些类允许您用很少的“脚手架”代码开发平台插件。

平台通道数据类型支持和解码器

标准平台通道使用标准消息编解码器,以支持简单的类似JSON值的高效二进制序列化,例如 booleans,numbers, Strings, byte buffers, List, Maps(请参阅StandardMessageCodec了解详细信息)。 当您发送和接收值时,这些值在消息中的序列化和反序列化会自动进行。
在这里插入图片描述

Flutter定义了三种不同类型的Channel,它们分别是

  1. BasicMessageChannel:用于传递字符串和半结构化的信息。
  2. MethodChannel:用于传递方法调用(method invocation)。
  3. EventChannel: 用于数据流(event streams)的通信。

BasicMessageChannel Androd端:
在这里插入图片描述

BasicMessageChannel Flutter端:

在这里插入图片描述

EventChannel Androd端:
在这里插入图片描述
EventChannel Flutter端:

在这里插入图片描述

三种Channel之间互相独立,各有用途,但它们在设计上却非常相近。每种Channel均有三个重要成员变量:

  1. name: String类型,代表Channel的名字,也是其唯一标识符。
  2. messager:BinaryMessenger类型,代表消息信使,是消息的发送与接收的工具。
  3. codec: MessageCodec类型或MethodCodec类型,代表消息的编解码器。

Channel name

​ 一个Flutter应用中可能存在多个Channel,每个Channel在创建时必须指定一个独一无二的name,Channel之间使用name来区分彼此。当有消息从Flutter端发送到Platform端时,会根据其传递过来的channel name找到该Channel对应的Handler(消息处理器)

BinaryMessenger是Platform端与Flutter端通信的工具,其通信使用的消息格式为二进制格式数据。当我们初始化一个Channel,并向该Channel注册处理消息的Handler时,实际上会生成一个与之对应的BinaryMessageHandler,并以channel name为key,注册到BinaryMessenger中。当Flutter端发送消息到BinaryMessenger时,BinaryMessenger会根据其入参channel找到对应的BinaryMessageHandler,并交由其处理。

Binarymessenger在Android端是一个接口,其具体实现为FlutterNativeView。

private final Map<String, BinaryMessageHandler> mMessageHandlers;
 ...
 public void setMessageHandler(String channel, BinaryMessageHandler handler) {
    if(handler == null) {
        this.mMessageHandlers.remove(channel);
    } else {
        this.mMessageHandlers.put(channel, handler);
    }

 }

Binarymessenger并不知道Channel的存在,它只和BinaryMessageHandler打交道。而Channel和BinaryMessageHandler则是一一对应的。由于Channel从BinaryMessageHandler接收到的消息是二进制格式数据,无法直接使用,故Channel会将该二进制消息通过Codec(消息编解码器)解码为能识别的消息并传递给Handler进行处理。

​ 当Handler处理完消息之后,会通过回调函数返回result,并将result通过编解码器编码为二进制格式数据,通过BinaryMessenger发送回Flutter端。

 private void handlePlatformMessage(final String channel, byte[] message, final int replyId) {
    this.assertAttached();
    BinaryMessageHandler handler = (BinaryMessageHandler)this.mMessageHandlers.get(channel);
	...
	if(reply == null) {
                            		FlutterNativeView.nativeInvokePlatformMessageEmptyResponseCallbac	(FlutterNativeView.this.mNativePlatformView, replyId);
    } else {
                            		FlutterNativeView.nativeInvokePlatformMessageResponseCallback	(FlutterNativeView.this.mNativePlatformView, replyId, reply, reply.position());
    }
	...
}

消息编解码器:Codec

消息编解码器Codec主要用于将二进制格式的数据转化为Handler能够识别的数据,Flutter定义了两种Codec:MessageCodec和MethodCodec。

MessageCodec

是一个接口,定义了两个方法:encodeMessage接收一个特定的数据类型T,并将其编码为二进制数据ByteBuffer,而decodeMessage则接收二进制数据ByteBuffer,将其解码为特定数据类型T。

MessageCodec有多种不同的实现:

  • BinaryCodec
  • StringCodec
  • JSONMessageCodec
  • StandardMessageCodec

MethodCodec

MethodCodec用于二进制数据与方法调用(MethodCall)和返回结果之间的编解码。MethodChannel和EventChannel所使用的编解码器均为MethodCodec。

与MessageCodec不同的是,MethodCodec用于MethodCall对象的编解码,一个MethodCall对象代表一次从Flutter端发起的方法调用。MethodCall有2个成员变量:String类型的method代表需要调用的方法名称,通用类型(Android中为Object,iOS中为id)的arguments代表需要调用的方法入参。

​ 由于处理的是方法调用,故相比于MessageCodec,MethodCodec多了对调用结果的处理。当方法调用成功时,使用encodeSuccessEnvelope将result编码为二进制数据,而当方法调用失败时,则使用encodeErrorEnvelope将error的code、message、detail编码为二进制数据

MethodCodec有两种实现:

  • JSONMethodCodec
  • StandardMethodCodec

消息处理器:Handler

当我们接收二进制格式消息并使用Codec将其解码为Handler能处理的消息后,就该Handler上场了。Flutter定义了三种类型的Handler,与Channel类型一一对应。我们向Channel注册一个Handler时,实际上就是向BinaryMessager注册一个与之对应的BinaryMessageHandler。当消息派分到BinaryMessageHandler后,Channel会通过Codec将消息解码,并传递给Handler处理。

  • MessageHandler

    MessageHandler用户处理字符串或者半结构化的消息,其onMessage方法接收一个T类型的消息,并异步返回一个相同类型result。MessageHandler的功能比较基础,使用场景较少,但是其配合BinaryCodec使用时,能够方便传递二进制数据消息。

  • MethodHandler

    MethodHandler用于处理方法的调用,其onMessage方法接收一个MethodCall类型消息,并根据MethodCall的成员变量method去调用对应的API,当处理完成后,根据方法调用成功或失败,返回对应的结果。

  • StreamHandler

    StreamHandler与前两者稍显不同,用于事件流的通信,最为常见的用途就是Platform端向Flutter端发送事件消息。当我们实现一个StreamHandler时,需要实现其onListen和onCancel方法。而在onListen方法的入参中,有一个EventSink(其在Android是一个对象)。我们持有EventSink后,即可通过EventSink向Flutter端发送事件消息。

理解消息编解码过程

Flutter官方文档表示,standard platform channels使用standard messsage codec对message和response进行序列化和反序列化,message与response可以是booleans, numbers, Strings, byte buffers,List, Maps等等,而序列化后得到的则是二进制格式的数据。

所以在上文提到的例子中,java.lang.Integer类型的返回值先是被序列化成了一段二进制格式的数据,然后该数据传递到传递到flutter侧后,被反序列化成了dart语言中的int类型的数据。

当message或response需要被编码为二进制数据时,会调用StandardMessageCodec的writeValue方法,该方法接收一个名为value的参数,并根据其类型,向二进制数据容器(ByteArrayOutputStream)写入该类型对应的type值,再将该数据转化为二进制表示,并写入二进制数据容器。

​ 而message或者response需要被解码时,使用的是StandardMessageCodec的readValue方法,该方法接收到二进制格式数据后,会先读取一个byte表示其type,再根据其type将二进制数据转化为对应的数据类型。

例如100,当这个值被转化为二进制数据时,会先向二进制数据容器写入int类型对应的type值:3,再写入由电量值100转化而得的4个byte。而当Flutter端接收到该二进制数据时,先读取第一个byte值,并根据其值得出该数据为int类型,接着,读取紧跟其后的4个byte,并将其转化为dart类型的int。

在这里插入图片描述

理解消息传递过程

下面通过实例来理解消息传递过程。

消息传递:从Flutter到Platform
交互图
在这里插入图片描述

Flutter端

我们在Flutter端使用MethodChannel的invokeMethod方法发起一次方法调用,invokeMethod方法会将其入参message和arguments封装成一个MethodCall对象,并使用MethodCodec将其编码为二进制格式数据,再通过BinaryMessages将消息发出

Future<String> getString(String key,String value) async {
String result;
try {
  	final Map<String, dynamic> params = <String, dynamic>{
    	'key': '$key',
  		};
  		params['value'] = value;
  		result = await _sChannel
      	.invokeMethod('getString', params);

	} on PlatformException catch (e) {
  print(e.toString());
	}
	return result;
}

调用methodchannel获取getString的值。下面依次来跟跟踪代码执行过程。invokeMethod方法执行如下:

 Future<dynamic> invokeMethod(String method, [dynamic arguments]) async {
	assert(method != null);
		final dynamic result = await BinaryMessages.send(
  		name,
  		codec.encodeMethodCall(new MethodCall(method, arguments)),
	);
	if (result == null)
  		throw new MissingPluginException('No implementation found for method $method on channel $name');
	return codec.decodeEnvelope(result);
}

最后会执行到:

 void sendPlatformMessage(String name,
                       ByteData data,
                       PlatformMessageResponseCallback callback) {
	final String error =
    _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
	if (error != null)
  	throw new Exception(error);
}
String _sendPlatformMessage(String name,
                          PlatformMessageResponseCallback callback,
                          ByteData data) native 'Window_sendPlatformMessage';

这里调了原生方法,再flutter engine源码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},
	});
}

_SendPlatformMessage函数对应:

void _SendPlatformMessage(Dart_NativeArguments args) {
	tonic::DartCallStatic(&SendPlatformMessage, args);
}

调用到的SendPlatformMessage:

Dart_Handle SendPlatformMessage(Dart_Handle window,
                            const std::string& name,
                            Dart_Handle callback,
                            const tonic::DartByteData& data) {
	UIDartState* dart_state = UIDartState::Current();

	if (!dart_state->window()) {
	// Must release the TypedData buffer before allocating other Dart objects.
	data.Release();
	return ToDart("Platform messages can only be sent from the main isolate");
	}

	fml::RefPtr<PlatformMessageResponse> response;
	if (!Dart_IsNull(callback)) {
		response = fml::MakeRefCounted<PlatformMessageResponseDart>(
    	tonic::DartPersistentValue(dart_state, callback),
   	 	dart_state->GetTaskRunners().GetUITaskRunner());
	}
	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();
}

仔细查看,关键部位来了 dart_state->window()->client()->HandlePlatformMessage(…);
再WindowClient中找到这个函数。

class WindowClient {
	public:
		virtual std::string DefaultRouteName() = 0;
		virtual void ScheduleFrame() = 0;
		virtual void Render(Scene* scene) = 0;
		virtual void UpdateSemantics(SemanticsUpdate* update) = 0;

		virtual void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) = 0;

		virtual FontCollection& GetFontCollection() = 0;
		virtual void UpdateIsolateDescription(const std::string isolate_name,
                                    int64_t isolate_port) = 0;

	protected:
	virtual ~WindowClient();
};

这是个纯虚函数,去找实现类RuntimeController,其中可以找到RuntimeDelegate& client_,再顺势去找engine.h继承了RuntimeDelegate。

 class Delegate {
	public:
		virtual void OnEngineUpdateSemantics(
    	blink::SemanticsNodeUpdates update,
    	blink::CustomAccessibilityActionUpdates actions) = 0;

 		virtual void OnEngineHandlePlatformMessage(
    		fml::RefPtr<blink::PlatformMessage> message) = 0;

		virtual void OnPreEngineRestart() = 0;

		virtual void UpdateIsolateDescription(const std::string isolate_name,
                                      int64_t isolate_port) = 0;
	};

其中OnEngineHandlePlatformMessage就是涉及到HandlePlatformMessage的调用,那么找到Delegate这个类调用的地方便能找到HandlePlatformMessage的调用。全局搜最终确定PlatformViewAndroid有调用到Delegate

PlatformViewAndroid(PlatformView::Delegate& delegate,
                  blink::TaskRunners task_runners,
                  fml::jni::JavaObjectWeakGlobalRef java_object);

最终在platform_view_android.cc中找到调用:

// |shell::PlatformView|
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;

int response_id = 0;
if (auto response = message->response()) {
	response_id = next_response_id_++;
	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);
}
}

再platform_view_android_jni.cc中找到:

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));
}

最终找到:

g_flutter_native_view_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
  env, env->FindClass("io/flutter/view/FlutterNativeView"));
	if (g_flutter_native_view_class->is_null()) {
	return false;
}

FlutterNativeView便是我们的java代码了。到此边可以告一段落。对应的便是:
在这里插入图片描述
后面的调用我们一路跟踪代码便能明白。

参考资料

https://www.jianshu.com/p/39575a90e820

https://www.colabug.com/3916347.html

https://www.jianshu.com/p/cb96d62f5042

https://flutter.io/platform-channels/

https://github.com/flutter/flutter

https://github.com/flutter/engine

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值