当我们尝试理解dart是如何调用原生代码的时候,80%的人都能说出来是根据channel,但再问为什么channel就可以调到原生的代码了呢,能说上来的人就不足1%了,本文旨在刨根问题,有些内容我现在也不理解为什么是这样的。
当我们发生一次dart调用原生方法的时候,使用VSCode或者AndroidStudio调试dart代码都会有类似的dart层堆栈,这里基本上是dart层
所有的调用了, 我们看具体的代码
amap_core.dart
class AmapCore {
static Future<void> init(String iosKey) {
return platform(
android: (pool) async {
// 高德的android SDK没有提供一个公共的library, 所以无法在amap_core中提供一个公共
// 的初始化方法.
// 0.17.0开始的amap_map_fluttify提供了一个通过代码初始的方法AmapService.init(iosKey:androidKey:);
},
ios: (pool) async {
final service = await AMapServices.sharedServices();
await service.set_apiKey(iosKey);
await service.set_enableHTTPS(true);
},
);
}
}
AMapServices.g.dart
Future<void> set_enableHTTPS(bool enableHTTPS) async {
await MethodChannel('me.yohom/amap_core_fluttify').invokeMethod('AMapServices::set_enableHTTPS', {'refId': refId, "enableHTTPS": enableHTTPS});
}
platform_channel.dart
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;
}
bindding.dart
@override
Future<ByteData> send(String channel, ByteData message) {
final MessageHandler handler = _mockHandlers[channel];
if (handler != null)
return handler(message);
return _sendPlatformMessage(channel, message);
}
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;
}
来到关键点,platform_channel.dart 文件中: invokeMethod
方法,这是所有dart代码调用原生方法的入口了,invokeMethod
接受2个参数,method
和arguments
,在这里会调用binaryMessenger.send
发送消息,传递了2个变量name
和codec.encodeMethodCall(MethodCall(method, arguments))
,下面先来来具体解释binaryMessenger
和name
,
在platform_channel.dart中会有如下代码
class MethodChannel {
const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger binaryMessenger ])
: assert(name != null),
assert(codec != null),
_binaryMessenger = binaryMessenger;
/// The logical channel on which communication happens, not null.
final String name;
/// The message codec used by this channel, not null.
final MethodCodec codec;
BinaryMessenger get binaryMessenger => _binaryMessenger ?? defaultBinaryMessenger;
// 以下省略
}
name
作为MethodChannel
的属性在构造函数中被初始化,再来看调用的地方
await MethodChannel('me.yohom/amap_core_fluttify')
这里只传递了一个参数,所以其它两个参数都是取了默认值,binaryMessenger
是一个get方法,当_binaryMessenger
属性值为null的时候取的是defaultBinaryMessenger
, defaultBinaryMessenger
也是一个get方法,具体的代码在binding.dart中,用法和binaryMessenger
类似,最终binaryMessenger
是一个_DefaultBinaryMessenger
类的实例,这个可以在binding.dart的代码中找到最终的答案,这里针对binaryMessenger
就不做更多的介绍了。
下面来具体解释第二个参数:codec.encodeMethodCall(MethodCall(method, arguments))
这里首先用到了MethodChannel
构造的时候第二个参数codec
,默认值是StandardMethodCodec
,再看encodeMethodCall
方法的定义:
message_codec.dart
abstract class MethodCodec {
/// Encodes the specified [methodCall] into binary.
ByteData encodeMethodCall(MethodCall methodCall);
/// Decodes the specified [methodCall] from binary.
MethodCall decodeMethodCall(ByteData methodCall);
/// Decodes the specified result [envelope] from binary.
///
/// Throws [PlatformException], if [envelope] represents an error, otherwise
/// returns the enveloped result.
dynamic decodeEnvelope(ByteData envelope);
/// Encodes a successful [result] into a binary envelope.
ByteData encodeSuccessEnvelope(dynamic result);
/// Encodes an error result into a binary envelope.
///
/// The specified error [code], human-readable error [message], and error
/// [details] correspond to the fields of [PlatformException].
ByteData encodeErrorEnvelope({ @required String code, String message, dynamic details });
}
这里MethodCodec
类是一个抽象类,StandardMethodCodec
是MethodCodec
一个子类,负责具体的实现
message_codecs.dart
class StandardMethodCodec implements MethodCodec {
// The codec method calls, and result envelopes as outlined below. This format
// must match the Android and iOS counterparts.
//
// * Individual values are encoded using [StandardMessageCodec].
// * Method calls are encoded using the concatenation of the encoding
// of the method name String and the arguments value.
// * Reply envelopes are encoded using first a single byte to distinguish the
// success case (0) from the error case (1). Then follows:
// * In the success case, the encoding of the result value.
// * In the error case, the concatenation of the encoding of the error code
// string, the error message string, and the error details value.
/// Creates a [MethodCodec] using the Flutter standard binary encoding.
const StandardMethodCodec([this.messageCodec = const StandardMessageCodec()]);
/// The message codec that this method codec uses for encoding values.
final StandardMessageCodec messageCodec;
@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');
}
子类实现了抽象类中定义的接口encodeMethodCall
,这里用到WriteBuffer
类将传入的参数MethodCall
转换成ByteData
, 这里先不详细展开讲WriteBuffer
,我们再说下encodeMethodCall
的参数MethodCall
message_codec.dart
class MethodCall {
/// Creates a [MethodCall] representing the invocation of [method] with the
/// specified [arguments].
const MethodCall(this.method, [this.arguments])
: assert(method != null);
/// The name of the method to be called.
final String method;
/// The arguments for the method.
///
/// Must be a valid value for the [MethodCodec] used.
final dynamic arguments;
@override
String toString() => '$runtimeType($method, $arguments)';
}
这里实际是对方法进行了dart层面的封装,将其转换成对象,保存了方法名method
和arguments
,后面这2个参数会被取出来,通过messageCodec.writeValue(buffer, xxx)
方法写入到WriteBuffer
中。
至此,应该已经解释清楚了binaryMessenger
实例和name
参数
await binaryMessenger.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
上面这段代码的调用有2个参数了,下面来详细解释调用binaryMessenger.send
, 由于binaryMessenger
最终是_DefaultBinaryMessenger
, 所以实际调用的是_DefaultBinaryMessenger
的send
方法
binding.dart
@override
Future<ByteData> send(String channel, ByteData message) {
final MessageHandler handler = _mockHandlers[channel];
if (handler != null)
return handler(message);
return _sendPlatformMessage(channel, message);
}
这里在没有设置_mockHandlers
的时候,会执行_sendPlatformMessage
私有方法,关于_mockHandlers
后面再展开分析。
binding.dart
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;
}
上面的这段代码已经是调试的尽头了,最终调用ui.window.sendPlatformMessage
将参数传递给ui.window
的sendPlatformMessage
方法,并设置了reply
,但是,还远远没有结束,我们来看下window.dart的源码
window.dart
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) {
final String error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null)
throw Exception(error);
}
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage';
这里是dart代码的执行尽头,sendPlatformMessage
将传入的callback
进行包装
window.dart
/// Wraps the given [callback] in another callback that ensures that the
/// original callback is called in the zone it was registered in.
static PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback callback) {
if (callback == null)
return null;
// Store the zone in which the callback is being registered.
final Zone registrationZone = Zone.current;
return (ByteData data) {
registrationZone.runUnaryGuarded(callback, data);
};
}
官方已经细心的给了解释,是为了让callback
在执行的时候能够和它注册的时候在一个zone
,关于Zone
,由于这里不是本文要阐述的核心路径,所以不展开讲。sendPlatformMessage
->_sendPlatformMessage
结束了dart代码,转为Native层,这里所说的Native并不是Java、OC原生代码,而是Flutter引擎自带的Native层代码,由c++编写,下面的代码也都是开源的,但我们跟代码已经无法跟到了,需要手动下载源码或在sourcegraph进行源码的阅读。这里用到了native这个关键字
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native 'Window_sendPlatformMessage'
意思是,这个接口是由native方法Window_sendPlatformMessage
导出成、dart语言的,在dart中,方法名是_sendPlatformMessage
,关于为什么在dart中使用native关键字就可以定义出native方法的导出,后面再研究,最终我们找到c++的源码
window.cc
void _SendPlatformMessage(Dart_NativeArguments args) {
tonic::DartCallStatic(&SendPlatformMessage, args);
}
Dart_Handle SendPlatformMessage(Dart_Handle window,
const std::string& name,
Dart_Handle callback,
Dart_Handle data_handle) {
UIDartState* dart_state = UIDartState::Current();
if (!dart_state->window()) {
return tonic::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_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();
}
void DartCallStatic(Sig func, Dart_NativeArguments args) {
DartArgIterator it(args, 0);
using Indices = typename IndicesForSignature<Sig>::type;
DartDispatcher<Indices, Sig> decoder(&it);
if (it.had_exception())
return;
decoder.Dispatch(func);
}
template <size_t... indices, typename... ArgTypes>
struct DartDispatcher<IndicesHolder<indices...>, void (*)(ArgTypes...)>
: public DartArgHolder<indices, ArgTypes>... {
using FunctionPtr = void (*)(ArgTypes...);
DartArgIterator* it_;
explicit DartDispatcher(DartArgIterator* it)
: DartArgHolder<indices, ArgTypes>(it)..., it_(it) {}
void Dispatch(FunctionPtr func) {
(*func)(DartArgHolder<indices, ArgTypes>::value...);
}
};
上面的2段代码,第一段还好,第二段就非常晦涩了,DartDispatcher
这个结构体的定义就有好几种,我们先姑且认为这段代码是用来做消息分发的,并且将参数列表args数组中的对象传递给对应的函数,最终调用到了SendPlatformMessage
这个方法,这里会根据callback
和data_handle
是否为空进行特殊的处理,大部分情况会走到dart_state->window()->client()->HandlePlatformMessage
, 这里首先会把data_handle
转换成data
, 然后写到内存buffer
,然后调用HandlePlatformMessage
, 一路跟踪会看到client实际是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;
virtual void SetNeedsReportTimings(bool value) = 0;
virtual std::shared_ptr<const fml::Mapping> GetPersistentIsolateData() = 0;
protected:
virtual ~WindowClient();
};
HandlePlatformMessage
是抽象方法,真实的定义不在Window.cc中,而在Runtime_controller
runtime_controller.h
class RuntimeController final : public WindowClient {
public:
RuntimeController(
RuntimeDelegate& client,
DartVM* vm,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
TaskRunners task_runners,
fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
fml::WeakPtr<IOManager> io_manager,
fml::RefPtr<SkiaUnrefQueue> unref_queue,
fml::WeakPtr<ImageDecoder> image_decoder,
std::string advisory_script_uri,
std::string advisory_script_entrypoint,
const std::function<void(int64_t)>& idle_notification_callback,
const WindowData& window_data,
const fml::closure& isolate_create_callback,
const fml::closure& isolate_shutdown_callback,
std::shared_ptr<const fml::Mapping> persistent_isolate_data);
// |WindowClient|
~RuntimeController() override;
//----------------------------------------------------------------------------
/// @brief Clone the the runtime controller. This re-creates the root
/// isolate with the same snapshots and copies all window data to
/// the new instance. This is usually only used in the debug
/// runtime mode to support the cold-restart scenario.
///
/// @return A clone of the existing runtime controller.
///
std::unique_ptr<RuntimeController> Clone() const;
以下省略
}
runtime_controller.cc
// |WindowClient|
void RuntimeController::HandlePlatformMessage(
fml::RefPtr<PlatformMessage> message) {
client_.HandlePlatformMessage(std::move(message));
}
client_
是RuntimeDelegate
的引用,所以要看谁继承或者就是RuntimeDelegate
shell/common/engine
class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
public:
private:
Engine::Delegate& delegate_;
}
可见,Engine
正是我们要寻找的RuntimeDelegate
, 在engine.cc中我们找到了HandlePlatformMessage
engine.h
void Engine::HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) {
if (message->channel() == kAssetChannel) {
HandleAssetPlatformMessage(std::move(message));
} else {
delegate_.OnEngineHandlePlatformMessage(std::move(message));
}
}
class Delegate {
public:
virtual void OnEngineHandlePlatformMessage(
fml::RefPtr<PlatformMessage> message) = 0;
以下省略
}
这里engine::Delegate也是一个抽象类,还要找它的实现类
shell.h
class Shell final : public PlatformView::Delegate,
public Animator::Delegate,
public Engine::Delegate,
public Rasterizer::Delegate,
public ServiceProtocol::Handler {
}
shell是delegate的一个实现
shell.cc
// |Engine::Delegate|
void Shell::OnEngineHandlePlatformMessage(
fml::RefPtr<PlatformMessage> message) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
if (message->channel() == kSkiaChannel) {
HandleEngineSkiaMessage(std::move(message));
return;
}
task_runners_.GetPlatformTaskRunner()->PostTask(
[view = platform_view_->GetWeakPtr(), message = std::move(message)]() {
if (view) {
view->HandlePlatformMessage(std::move(message));
}
});
}
我们看到上面的代码还是很人性化的,Shell作为Engine::Delegate的实现,还是有所标注的,这里会根据message->channel
是否是kSkiaChannel
进行不同的处理,kSkiaChannel
具体做了哪些事情后面再研究。正常的路径,用户自定义的channel
将会调用
view->HandlePlatformMessage(std::move(message))
, 这里platform_view_
我们先简单理解为实际对应平台的view。关于上述一系列C++抽象类是怎么找到最终具体的实现类的,我们再另一篇博文详细分析
// |PlatformView|
void PlatformViewIOS::HandlePlatformMessage(fml::RefPtr<flutter::PlatformMessage> message) {
platform_message_router_.HandlePlatformMessage(std::move(message));
}
// |PlatformView|
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;
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);
}
}
现在可以确定PlatformViewAndroid
对应的应该是Android平台的,PlatformViewIOS
对应的是iOS的,以iOS为例,这里PlatformViewIOS
继续分发,把HandlePlatformMessage
让platform_message_router_
来处理,
namespace flutter {
class PlatformMessageRouter {
public:
PlatformMessageRouter();
~PlatformMessageRouter();
void HandlePlatformMessage(
fml::RefPtr<flutter::PlatformMessage> message) const;
void SetMessageHandler(const std::string& channel,
FlutterBinaryMessageHandler handler);
private:
std::unordered_map<std::string, fml::ScopedBlock<FlutterBinaryMessageHandler>>
message_handlers_;
FML_DISALLOW_COPY_AND_ASSIGN(PlatformMessageRouter);
};
}
void PlatformMessageRouter::HandlePlatformMessage(
fml::RefPtr<flutter::PlatformMessage> message) const {
fml::RefPtr<flutter::PlatformMessageResponse> completer = message->response();
auto it = message_handlers_.find(message->channel());
if (it != message_handlers_.end()) {
FlutterBinaryMessageHandler handler = it->second;
NSData* data = nil;
if (message->hasData()) {
data = GetNSDataFromVector(message->data());
}
handler(data, ^(NSData* reply) {
if (completer) {
if (reply) {
completer->Complete(GetMappingFromNSData(reply));
} else {
completer->CompleteEmpty();
}
}
});
} else {
if (completer) {
completer->CompleteEmpty();
}
}
}
终于看到了我们iOS开发熟悉的mm文件了,从message_handlers_
里找到存储的channel对应的handler, 然后如果有数据就把数据转成NSData,最后执行handler,这里handler已经是OC代码定义的一个block了,block执行的时候会传递2个参数,一个是message
,这个message
自从执行到C++代码后就封装成了fml::RefPtr<PlatformMessage>
类型的指针,期间Native层面一直未曾改变,第二个参数FlutterBinaryReply
也是一个block,当completer
存在就会调用completer->Complete
或者completer->CompleteEmpty()
,用于接收原生代码的回调,有关原生代码如果回调或者调用dart代码,会在另外一篇博文分析
FlutterBinaryMessenger.h
typedef void (^FlutterBinaryMessageHandler)(NSData* _Nullable message, FlutterBinaryReply reply);
typedef void (^FlutterBinaryReply)(NSData* _Nullable reply);
到这里就已经完成了Native层的调用,可以看到,里面的逻辑还是非常复杂的,最终原生代码又是如何被执行的,我们需要找到传递或者存储message_handlers_
的地方。
PlatformMessageRouter.mm
void PlatformMessageRouter::SetMessageHandler(const std::string& channel,
FlutterBinaryMessageHandler handler) {
message_handlers_.erase(channel);
if (handler) {
message_handlers_[channel] =
fml::ScopedBlock<FlutterBinaryMessageHandler>{handler, fml::OwnershipPolicy::Retain};
}
}
这里SetMessageHandler
定义的地方在FlutterBasicMessageChannel
类,我们直接可以在iOS工程中找到这个类的头文件,文件名是FlutterChannels.h
,所以我们去找对应的源码
FlutterChannels.mm
@implementation FlutterBasicMessageChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMessageCodec>* _codec;
}
- (void)setMessageHandler:(FlutterMessageHandler)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
return;
}
// Grab reference to avoid retain on self.
NSObject<FlutterMessageCodec>* codec = _codec;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
handler([codec decode:message], ^(id reply) {
callback([codec encode:reply]);
});
};
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
}
这里在FlutterMessageHandler
不为空的时候,会调用FlutterBinaryMessenger
类的setMessageHandlerOnChannel
方法,继续找FlutterBinaryMessenger.mm
, 没有找到这个文件,因为我忽略了,_messenger
是一个遵守FlutterBinaryMessenger
协议的对象,而不是FlutterBinaryMessenger
类的对象,所以我们只要找到是哪个类实现了FlutterBinaryMessenger
中的setMessageHandlerOnChannel
接口就可以了。
我们发现大多数时候messenger
都是作为参数进行传递的,只有FlutterPluginRegistrar
这个协议里定义了这个接口
@protocol FlutterPluginRegistrar <NSObject>
/**
* Returns a `FlutterBinaryMessenger` for creating Dart/iOS communication
* channels to be used by the plugin.
*
* @return The messenger.
*/
- (NSObject<FlutterBinaryMessenger>*)messenger;
以下省略
}
同理,registrar
也是通过实现接口registerWithRegistrar
拿到的
@protocol FlutterPlugin <NSObject, FlutterApplicationLifeCycleDelegate>
@required
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar;
以下省略
@end
我们所有定义的插件都是要遵守FlutterPlugin
协议的,例如JPushPlugin
@interface JPushPlugin : NSObject<FlutterPlugin>
@property FlutterMethodChannel *channel;
@end
这样每个组件通过实现registerWithRegistrar
拿到FlutterPluginRegistrar
进而拿到FlutterBinaryMessenger
对象,那到底谁调用了registerWithRegistrar
呢?
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
GeneratedPluginRegistrant.m
#if __has_include(<jpush_flutter/JPushPlugin.h>)
#import <jpush_flutter/JPushPlugin.h>
#else
@import jpush_flutter;
#endif
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
[JPushPlugin registerWithRegistrar:[registry registrarForPlugin:@"JPushPlugin"]];
}
这次应该算是抛到祖坟了,didFinishLaunchingWithOptions
是iOS App启动后执行的第一个方法,一般初始化操作会放在这里进行。GeneratedPluginRegistrant
应该是一个插件初始化的管理类,这里的代码都是自动生成的,有关代码自动生成的实现原理后面分析, registry
就是AppDelegate
,FlutterApp的AppDelegate
继承自FlutterAppDelegate
@interface FlutterAppDelegate
: UIResponder <UIApplicationDelegate, FlutterPluginRegistry, FlutterAppLifeCycleProvider>
FlutterAppDelegate
类或其子类需要实现FlutterPluginRegistry
协议的registrarForPlugin
接口
在FlutterAppDelegate.mm中
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
UIViewController* rootViewController = _window.rootViewController;
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
return
[[(FlutterViewController*)rootViewController pluginRegistry] registrarForPlugin:pluginKey];
}
return nil;
}
所以有了这个实现就不难解释GeneratedPluginRegistrant
类中,[registry registrarForPlugin:@"JPushPlugin"]
实际调用的是上面的这段代码,FlutterViewController
的源码中pluginRegistry
实际返回的是一个遵守FlutterPluginRegistry
协议的_engine
对象
@implementation FlutterViewController {
std::unique_ptr<fml::WeakPtrFactory<FlutterViewController>> _weakFactory;
fml::scoped_nsobject<FlutterEngine> _engine;
// We keep a separate reference to this and create it ahead of time because we want to be able to
// setup a shell along with its platform view before the view has to appear.
fml::scoped_nsobject<FlutterView> _flutterView;
fml::scoped_nsobject<UIView> _splashScreenView;
fml::ScopedBlock<void (^)(void)> _flutterViewRenderedCallback;
UIInterfaceOrientationMask _orientationPreferences;
UIStatusBarStyle _statusBarStyle;
flutter::ViewportMetrics _viewportMetrics;
BOOL _initialized;
BOOL _viewOpaque;
BOOL _engineNeedsLaunch;
NSMutableSet<NSNumber*>* _ongoingTouches;
// This scroll view is a workaround to accomodate iOS 13 and higher. There isn't a way to get
// touches on the status bar to trigger scrolling to the top of a scroll view. We place a
// UIScrollView with height zero and a content offset so we can get those events. See also:
// https://github.com/flutter/flutter/issues/35050
fml::scoped_nsobject<UIScrollView> _scrollView;
}
...省略若干行
- (id<FlutterPluginRegistry>)pluginRegistry {
return _engine;
}
其实[(FlutterViewController*)rootViewController pluginRegistry]
就是获取FlutterEngine
对象了, Engine中
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
self.pluginPublications[pluginKey] = [NSNull null];
FlutterEngineRegistrar* result = [[FlutterEngineRegistrar alloc] initWithPlugin:pluginKey
flutterEngine:self];
self.registrars[pluginKey] = result;
return [result autorelease];
}
实际是把,pluginKey
和self
传给FlutterEngineRegistrar
并获取了FlutterEngineRegistrar
类的对象,然后用字典self.registrars
保存pluginKey
和result
,这里应该是引擎的核心代码了,主要是管理插件。FlutterEngineRegistrar
就是我们代码里用到的registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry
中的registry
, FlutterEngineRegistrar
类的源码也在Engine中
@implementation FlutterEngineRegistrar {
NSString* _pluginKey;
}
- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_pluginKey = [pluginKey copy];
_flutterEngine = flutterEngine;
return self;
}
- (NSObject<FlutterBinaryMessenger>*)messenger {
return _flutterEngine.binaryMessenger;
}
初始化就是把传进来的pluginKey
和flutterEngine
存起来了,并且在FlutterEngineRegistrar
中,终于找到了我们寻觅已久的messenger
@implementation FlutterEngine {
fml::scoped_nsobject<FlutterDartProject> _dartProject;
flutter::ThreadHost _threadHost;
std::unique_ptr<flutter::Shell> _shell;
NSString* _labelPrefix;
std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory;
fml::WeakPtr<FlutterViewController> _viewController;
fml::scoped_nsobject<FlutterObservatoryPublisher> _publisher;
std::unique_ptr<flutter::FlutterPlatformViewsController> _platformViewsController;
std::unique_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
std::unique_ptr<flutter::SamplingProfiler> _profiler;
// Channels
fml::scoped_nsobject<FlutterPlatformPlugin> _platformPlugin;
fml::scoped_nsobject<FlutterTextInputPlugin> _textInputPlugin;
fml::scoped_nsobject<FlutterMethodChannel> _localizationChannel;
fml::scoped_nsobject<FlutterMethodChannel> _navigationChannel;
fml::scoped_nsobject<FlutterMethodChannel> _platformChannel;
fml::scoped_nsobject<FlutterMethodChannel> _platformViewsChannel;
fml::scoped_nsobject<FlutterMethodChannel> _textInputChannel;
fml::scoped_nsobject<FlutterBasicMessageChannel> _lifecycleChannel;
fml::scoped_nsobject<FlutterBasicMessageChannel> _systemChannel;
fml::scoped_nsobject<FlutterBasicMessageChannel> _settingsChannel;
int64_t _nextTextureId;
BOOL _allowHeadlessExecution;
FlutterBinaryMessengerRelay* _binaryMessenger;
}
这个_binaryMessenger
是FlutterBinaryMessengerRelay
类的实例, FlutterBinaryMessengerRelay.h中也可以看到,确实是遵守了FlutterBinaryMessenger
协议,并且这里我们终于找到了setMessageHandlerOnChannel
这个方法
- (void)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
if (self.parent) {
[self.parent setMessageHandlerOnChannel:channel binaryMessageHandler:handler];
} else {
FML_LOG(WARNING) << "Communicating on a dead channel.";
}
}
在 Engine中
- (instancetype)initWithName:(NSString*)labelPrefix
project:(FlutterDartProject*)project
allowHeadlessExecution:(BOOL)allowHeadlessExecution {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
NSAssert(labelPrefix, @"labelPrefix is required");
_allowHeadlessExecution = allowHeadlessExecution;
_labelPrefix = [labelPrefix copy];
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self);
if (project == nil)
_dartProject.reset([[FlutterDartProject alloc] init]);
else
_dartProject.reset([project retain]);
_pluginPublications = [NSMutableDictionary new];
_registrars = [[NSMutableDictionary alloc] init];
_platformViewsController.reset(new flutter::FlutterPlatformViewsController());
_binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
这段是注册通知的代码,省略
return self;
}
可以看到FlutterBinaryMessengerRelay
里的parent
就是FlutterEngine
, 在 Engine中也确实有这个实现
- (void)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
NSParameterAssert(channel);
NSAssert(_shell && _shell->IsSetup(),
@"Setting a message handler before the FlutterEngine has been run.");
self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler);
}
self.iosPlatformView->GetPlatformMessageRouter().SetMessageHandler(channel.UTF8String, handler)
调用的就是下面的代码
void PlatformMessageRouter::SetMessageHandler(const std::string& channel,
FlutterBinaryMessageHandler handler) {
message_handlers_.erase(channel);
if (handler) {
message_handlers_[channel] =
fml::ScopedBlock<FlutterBinaryMessageHandler>{handler, fml::OwnershipPolicy::Retain};
}
}
好么,总算找到message_handlers_
添加元素的地方了,整理一下调用堆栈
PlatformMessageRouter::SetMessageHandler
Engine::setMessageHandlerOnChannel
FlutterBinaryMessengerRelay::setMessageHandlerOnChannel
FlutterBasicMessageChannel::setMessageHandler
搜索工程的代码发现并没有调用FlutterBasicMessageChannel::setMessageHandler
的地方,好吧,我们搞清了后面的流程,但是还没有人使用,这是我们从dart端开始分析,一直分析到原生代码得到的一条路径。
代码中更多的用法是这样的堆栈
大方向还是对的,问题出在我们刚才分析PlatformMessageRouter
这个类的时候,被存储handler
的message_handlers_
字典给影响了,我们根据实际的堆栈,找到FlutterChannels源码
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
return;
}
// Make sure the block captures the codec, not self.
NSObject<FlutterMethodCodec>* codec = _codec;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [codec decodeMethodCall:message];
handler(call, ^(id result) {
if (result == FlutterMethodNotImplemented)
callback(nil);
else if ([result isKindOfClass:[FlutterError class]])
callback([codec encodeErrorEnvelope:(FlutterError*)result]);
else
callback([codec encodeSuccessEnvelope:result]);
});
};
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
}
原来,setMethodCallHandler
里面也是调用setMessageHandlerOnChannel
来进行注册的
PlatformMessageRouter::SetMessageHandler
Engine::setMessageHandlerOnChannel
FlutterBinaryMessengerRelay::setMessageHandlerOnChannel
FlutterMethodChannel::setMethodCallHandler
这样通过显示调用setMethodCallHandler
就可以在回调里面进行dart
代码调用的处理了,但是我们还发现更多的插件是像JPush这样
JPushPlugin.m
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
JPLog(@"handleMethodCall:%@",call.method);
if ([@"getPlatformVersion" isEqualToString:call.method]) {
result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
} else if([@"setup" isEqualToString:call.method]) {
[self setup:call result: result];
} else if([@"applyPushAuthority" isEqualToString:call.method]) {
[self applyPushAuthority:call result:result];
} else if([@"setTags" isEqualToString:call.method]) {
[self setTags:call result:result];
}
}
后面省略类似的代码
JPushPlugin组件初始化的时候注册方法了方法回调addMethodCallDelegate
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
getRidResults = @[].mutableCopy;
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:@"jpush"
binaryMessenger:[registrar messenger]];
JPushPlugin* instance = [[JPushPlugin alloc] init];
instance.channel = channel;
[registrar addApplicationDelegate:instance];
[registrar addMethodCallDelegate:instance channel:channel];
}
经过上面的分析,我们不难找到源码还是在FlutterEngine.mm
中
- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
channel:(FlutterMethodChannel*)channel {
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
[delegate handleMethodCall:call result:result];
}];
}
这样原生代码通过channel
名来注册methodCallHandler
, 最终handler
存储到PlatformMessageRouter
的message_handlers_
中,当有dart
通过invokeMethod
方法调用原生方法的时候,就会跟进channel
取出对应的handler
,然后执行handler
,使原生代码setMethodCallHandler
的回调执行,然后这里又使用Plugin
传进来的delegate
将调用信息回调到handleMethodCall
方法上,由于delegate
是遵守FlutterPlugin
协议的,所以只要在有类实现FlutterPlugin
的- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result
方法就可以处理来自dart
的方法调用了。
总结
dart
代码调用原生接口,经历了两个阶段:
- dart封装层的转发,最终来到ui.window.sendPlatformMessage
- Native层的转发,最终会根据不同平台调用不同的方法,iOS: HandlePlatformMessage, Android: FlutterViewHandlePlatformMessage
原生代码能够正确接收到消息,是通过注册handler的方式:
- Plugin通过
addMethodCallDelegate
或setMethodCallHandler
注册 - FlutterChannel通过
setMethodCallHandler
进行注册 - Engine通过
setMessageHandlerOnChannel
注册 - PlatformMessageRouter通过
SetMessageHandler
注册,存在message_handlers_
中
遗留问题
-
StandardMethodCodec
中用到的WriteBuffer
实现细节 -
_DefaultBinaryMessenger
中用到_mockHandlers
实现细节 - window.dart中使用了
Zone
,Zone
如何保证注册和执行的时候是同一个zone
- 为什么在dart中使用native关键字就可以定义出C++方法的导出
- DartDispatcher是如何分发消息最终调用到
SendPlatformMessage
的 - 当
channel
是kSkiaChannel
的时候,后面具体做了哪些事情 - 一系列C++抽象类是怎么找到最终具体的实现类的
- 原生代码如果回调或者调用dart代码,会在另外一篇博文分析
- GeneratedPluginRegistrant自动生成代码原理