Flutter Channel通信原理(下)

上一篇文章主要分析了dart調(diào)用原生代碼的實(shí)現(xiàn)原理,本文將重點(diǎn)講原生代碼是如何調(diào)用和回調(diào)dart的。

感性認(rèn)識(shí)

當(dāng)使用AndroidStudio調(diào)試模式調(diào)試dart代碼的時(shí)候蟹肘,當(dāng)一個(gè)dart代碼被調(diào)用前雇初,通常會(huì)有類似的堆棧


image.png

dart層面域庇,是通過setMethodCallHandler來實(shí)現(xiàn)監(jiān)聽的字币,當(dāng)原生代碼發(fā)生調(diào)用,會(huì)觸發(fā)handler被執(zhí)行一忱,然后進(jìn)入到Plugin的dart代碼進(jìn)入分發(fā)邏輯莲蜘,dart代碼被執(zhí)行。

JPushPlugin.m
+ (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];
}

- (void)networkDidReceiveMessage:(NSNotification *)notification {
    [_channel invokeMethod:@"onReceiveMessage" arguments: [notification userInfo]];
}

在原生層面帘营,原生調(diào)用dart代碼都是通過FlutterMethodChannelinvokeMethod進(jìn)行的票渠,由于invokeMethod并沒有以源碼的形式集成進(jìn)Flutter SDK,所以在原生代碼上芬迄,這里就已經(jīng)是調(diào)試的盡頭了问顷。

原理分析

invokeMethod的實(shí)現(xiàn)在FlutterChannels.mm

- (void)invokeMethod:(NSString*)method arguments:(id)arguments {
  FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:method
                                                                    arguments:arguments];
  NSData* message = [_codec encodeMethodCall:methodCall];
  [_messenger sendOnChannel:_name message:message];
}

- (void)invokeMethod:(NSString*)method arguments:(id)arguments result:(FlutterResult)callback {
  FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:method
                                                                    arguments:arguments];
  NSData* message = [_codec encodeMethodCall:methodCall];
  FlutterBinaryReply reply = ^(NSData* data) {
    if (callback) {
      callback((data == nil) ? FlutterMethodNotImplemented : [_codec decodeEnvelope:data]);
    }
  };
  [_messenger sendOnChannel:_name message:message binaryReply:reply];
}

這里不管原生的方法是否需要reply,都會(huì)對(duì)調(diào)用的方法名和參數(shù)進(jìn)行一個(gè)封裝禀梳,封裝成FlutterMethodCall類型的對(duì)象杜窄,再對(duì)methodCall對(duì)象進(jìn)行編碼,轉(zhuǎn)換成二進(jìn)制數(shù)據(jù)message算途,這里有必要說一下這個(gè)_codec

FlutterChannels.mm
+ (instancetype)messageChannelWithName:(NSString*)name
                       binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
  NSObject<FlutterMessageCodec>* codec = [FlutterStandardMessageCodec sharedInstance];
  return [FlutterBasicMessageChannel messageChannelWithName:name
                                            binaryMessenger:messenger
                                                      codec:codec];
}

通常在初始化的時(shí)候塞耕,如果沒有傳codec就使用FlutterStandardMessageCodec作為默認(rèn)的codec,我們看原生代碼

    JPushPlugin.m
    FlutterMethodChannel* channel = [FlutterMethodChannel
                                     methodChannelWithName:@"jpush"
                                     binaryMessenger:[registrar messenger]];

在創(chuàng)建channel的時(shí)候確實(shí)大部分也是不傳codec的嘴瓤,在FlutterStandardCodec.mmencodeMethodCall方法是這樣定義的

- (NSData*)encodeMethodCall:(FlutterMethodCall*)call {
  NSMutableData* data = [NSMutableData dataWithCapacity:32];
  FlutterStandardWriter* writer = [_readerWriter writerWithData:data];
  [writer writeValue:call.method];
  [writer writeValue:call.arguments];
  return data;
}

初始化data為4個(gè)字節(jié)扫外,然后將methodarguments寫入莉钙, 這里不展開講FlutterStandardWriter的工作機(jī)制, 只需知道筛谚,原生調(diào)用dart時(shí)磁玉,會(huì)將方法名和參數(shù)轉(zhuǎn)換成二進(jìn)制數(shù)據(jù)。[_messenger sendOnChannel:_name message:message binaryReply:reply] 這里的_messengerFlutterBinaryMessengerRelay驾讲,這里具體的分析可以參考上一篇文章

- (void)sendOnChannel:(NSString*)channel message:(NSData*)message {
  if (self.parent) {
    [self.parent sendOnChannel:channel message:message binaryReply:nil];
  } else {
    FML_LOG(WARNING) << "Communicating on a dead channel.";
  }
}

- (void)sendOnChannel:(NSString*)channel
              message:(NSData*)message
          binaryReply:(FlutterBinaryReply)callback {
  if (self.parent) {
    [self.parent sendOnChannel:channel message:message binaryReply:callback];
  } else {
    FML_LOG(WARNING) << "Communicating on a dead channel.";
  }
}

所以我們很容易就找到了sendOnChannel蚊伞,還是上一篇文章的分析,得知parent就是FlutterEngine蝎毡,也找到了實(shí)現(xiàn)的[代碼]:

- (void)sendOnChannel:(NSString*)channel
              message:(NSData*)message
          binaryReply:(FlutterBinaryReply)callback {
  NSParameterAssert(channel);
  NSAssert(_shell && _shell->IsSetup(),
           @"Sending a message before the FlutterEngine has been run.");
  fml::RefPtr<flutter::PlatformMessageResponseDarwin> response =
      (callback == nil) ? nullptr
                        : fml::MakeRefCounted<flutter::PlatformMessageResponseDarwin>(
                              ^(NSData* reply) {
                                callback(reply);
                              },
                              _shell->GetTaskRunners().GetPlatformTaskRunner());
  fml::RefPtr<flutter::PlatformMessage> platformMessage =
      (message == nil) ? fml::MakeRefCounted<flutter::PlatformMessage>(channel.UTF8String, response)
                       : fml::MakeRefCounted<flutter::PlatformMessage>(
                             channel.UTF8String, flutter::GetVectorFromNSData(message), response);

  _shell->GetPlatformView()->DispatchPlatformMessage(platformMessage);
}

如果reply存在就構(gòu)造一個(gè)PlatformMessageResponseDarwin類型的response

platform_message_response_darwin.h

class PlatformMessageResponseDarwin : public flutter::PlatformMessageResponse {
 public:
  void Complete(std::unique_ptr<fml::Mapping> data) override;

  void CompleteEmpty() override;

 private:
  explicit PlatformMessageResponseDarwin(PlatformMessageResponseCallback callback,
                                         fml::RefPtr<fml::TaskRunner> platform_task_runner);

  ~PlatformMessageResponseDarwin() override;

  fml::ScopedBlock<PlatformMessageResponseCallback> callback_;
  fml::RefPtr<fml::TaskRunner> platform_task_runner_;

  FML_FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseDarwin);
};

}

platform_message_response_darwin.mm


namespace flutter {

PlatformMessageResponseDarwin::PlatformMessageResponseDarwin(
    PlatformMessageResponseCallback callback,
    fml::RefPtr<fml::TaskRunner> platform_task_runner)
    : callback_(callback, fml::OwnershipPolicy::Retain),
      platform_task_runner_(std::move(platform_task_runner)) {}

PlatformMessageResponseDarwin::~PlatformMessageResponseDarwin() = default;

void PlatformMessageResponseDarwin::Complete(std::unique_ptr<fml::Mapping> data) {
  fml::RefPtr<PlatformMessageResponseDarwin> self(this);
  platform_task_runner_->PostTask(fml::MakeCopyable([self, data = std::move(data)]() mutable {
    self->callback_.get()(GetNSDataFromMapping(std::move(data)));
  }));
}

void PlatformMessageResponseDarwin::CompleteEmpty() {
  fml::RefPtr<PlatformMessageResponseDarwin> self(this);
  platform_task_runner_->PostTask(
      fml::MakeCopyable([self]() mutable { self->callback_.get()(nil); }));
}

}

上面是設(shè)置方法的回調(diào)厚柳,_shell->GetPlatformView()->DispatchPlatformMessage(platformMessage)進(jìn)行消息的再次轉(zhuǎn)發(fā),這里用到了_shell沐兵,那究竟_shell是在哪里初始化的呢,在FlutterEngine中搜索發(fā)現(xiàn)是在- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI方法中便监,而這個(gè)方法的調(diào)用是在下面的代碼

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

@synthesize displayingFlutterUI = _displayingFlutterUI;

#pragma mark - Manage and override all designated initializers

- (instancetype)initWithEngine:(FlutterEngine*)engine
                       nibName:(nullable NSString*)nibName
                        bundle:(nullable NSBundle*)nibBundle {
  NSAssert(engine != nil, @"Engine is required");
  self = [super initWithNibName:nibName bundle:nibBundle];
  if (self) {
    _viewOpaque = YES;
    if (engine.viewController) {
      FML_LOG(ERROR) << "The supplied FlutterEngine " << [[engine description] UTF8String]
                     << " is already used with FlutterViewController instance "
                     << [[engine.viewController description] UTF8String]
                     << ". One instance of the FlutterEngine can only be attached to one "
                        "FlutterViewController at a time. Set FlutterEngine.viewController "
                        "to nil before attaching it to another FlutterViewController.";
    }
    _engine.reset([engine retain]);
    _engineNeedsLaunch = NO;
    _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
    _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
    _ongoingTouches = [[NSMutableSet alloc] init];

    [self performCommonViewControllerInitialization];
    [engine setViewController:self];
  }

  return self;
}

- (instancetype)initWithProject:(nullable FlutterDartProject*)project
                        nibName:(nullable NSString*)nibName
                         bundle:(nullable NSBundle*)nibBundle {
  self = [super initWithNibName:nibName bundle:nibBundle];
  if (self) {
    _viewOpaque = YES;
    _weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
    _engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter"
                                              project:project
                               allowHeadlessExecution:NO]);
    _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
    [_engine.get() createShell:nil libraryURI:nil];
    _engineNeedsLaunch = YES;
    _ongoingTouches = [[NSMutableSet alloc] init];
    [self loadDefaultSplashScreenView];
    [self performCommonViewControllerInitialization];
  }

  return self;
}

- (instancetype)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil {
  return [self initWithProject:nil nibName:nil bundle:nil];
}

- (instancetype)initWithCoder:(NSCoder*)aDecoder {
  return [self initWithProject:nil nibName:nil bundle:nil];
}

- (instancetype)init {
  return [self initWithProject:nil nibName:nil bundle:nil];
}
 省略若干行
}
@end

創(chuàng)建FlutterApp后扎谎,我們會(huì)發(fā)現(xiàn)Main.storyboard類型是FlutterViewController,這個(gè)類在初始化的時(shí)候會(huì)執(zhí)行initWithNibName進(jìn)而執(zhí)行initWithProject

- (instancetype)initWithProject:(nullable FlutterDartProject*)project
                        nibName:(nullable NSString*)nibName
                         bundle:(nullable NSBundle*)nibBundle

這個(gè)初始化中會(huì)調(diào)用createShell烧董,至于為什么這里調(diào)用的時(shí)候要用_engine.get()以及為什么這里的_engine要用fml::scoped_nsobject<FlutterEngine>先不糾結(jié)
shell.h得知毁靶,返回的是PlatformView類型的指針

fml::WeakPtr<PlatformView> GetPlatformView();

所以在PlatformView類中可以找到DispatchPlatformMessage的實(shí)現(xiàn)

void PlatformView::DispatchPlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  delegate_.OnPlatformViewDispatchPlatformMessage(std::move(message));
}

根據(jù)delegate_的定義

PlatformView::Delegate& delegate_;

OnPlatformViewDispatchPlatformMessage的實(shí)現(xiàn)是在PlatformView::Delegate或其子類中

class Shell final : public PlatformView::Delegate,
                    public Animator::Delegate,
                    public Engine::Delegate,
                    public Rasterizer::Delegate,
                    public ServiceProtocol::Handler

Shell類就是PlatformView::Delegate的子類了,我們也找到了OnPlatformViewDispatchPlatformMessage的真實(shí)實(shí)現(xiàn)

void Shell::OnPlatformViewDispatchPlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  FML_DCHECK(is_setup_);
  FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());

  task_runners_.GetUITaskRunner()->PostTask(
      [engine = engine_->GetWeakPtr(), message = std::move(message)] {
        if (engine) {
          engine->DispatchPlatformMessage(std::move(message));
        }
      });
}

繞了一圈逊移,原來预吆,_shell->GetPlatformView()->DispatchPlatformMessage(platformMessage)就是調(diào)用Shell::OnPlatformViewDispatchPlatformMessage,這里task_runners_.GetUITaskRunner()->PostTask應(yīng)該是把一段代碼加到一個(gè)任務(wù)隊(duì)列里執(zhí)行胳泉,可能類似dispatch_async的作用拐叉,PostTask真實(shí)的實(shí)現(xiàn)原理不再這里深入討論,這里先給出大膽的假設(shè)扇商。engine_->GetWeakPtr()這里使用弱引用估計(jì)也是為了在block中不引起對(duì)engine的強(qiáng)引用吧凤瘦。實(shí)際還是Engine類的實(shí)例調(diào)用DispatchPlatformMessage

engine.cc

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

  FML_DLOG(WARNING) << "Dropping platform message on channel: "
                    << message->channel();
}

上面的一段代碼應(yīng)該是系統(tǒng)定義的一些channel案铺,kLifecycleChannel蔬芥,kLocalizationChannelkSettingsChannel具體的定義后面再研究控汉,下面的是有關(guān)runtime_controller_的調(diào)用笔诵,源碼詳情

bool RuntimeController::IsRootIsolateRunning() const {
  std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
  if (root_isolate) {
    return root_isolate->GetPhase() == DartIsolate::Phase::Running;
  }
  return false;
}

bool RuntimeController::DispatchPlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  if (auto* window = GetWindowIfAvailable()) {
    TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage",
                 "mode", "basic");
    window->DispatchPlatformMessage(std::move(message));
    return true;
  }
  return false;
}

這代代碼有關(guān)DartIsolate的,是為了防止引擎沒有在運(yùn)行? 或者說runtime沒工作姑子?
接下來的HandleNavigationPlatformMessage應(yīng)該就是針對(duì)這種異常的處理乎婿,暫時(shí)不深入研究

runtime_controller.cc

bool RuntimeController::DispatchPlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  if (auto* window = GetWindowIfAvailable()) {
    TRACE_EVENT1("flutter", "RuntimeController::DispatchPlatformMessage",
                 "mode", "basic");
    window->DispatchPlatformMessage(std::move(message));
    return true;
  }
  return false;
}

至此調(diào)用window->DispatchPlatformMessage(std::move(message)),應(yīng)該可以說壁酬,我又回來了次酌,上一篇文章離開dart就是進(jìn)入了window.cc

void Window::DispatchPlatformMessage(fml::RefPtr<PlatformMessage> message) {
  std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
  if (!dart_state) {
    FML_DLOG(WARNING)
        << "Dropping platform message for lack of DartState on channel: "
        << message->channel();
    return;
  }
  tonic::DartState::Scope scope(dart_state);
  Dart_Handle data_handle =
      (message->hasData()) ? ToByteData(message->data()) : Dart_Null();
  if (Dart_IsError(data_handle)) {
    FML_DLOG(WARNING)
        << "Dropping platform message because of a Dart error on channel: "
        << message->channel();
    return;
  }

  int response_id = 0;
  if (auto response = message->response()) {
    response_id = next_response_id_++;
    pending_responses_[response_id] = response;
  }

  tonic::LogIfError(
      tonic::DartInvokeField(library_.value(), "_dispatchPlatformMessage",
                             {tonic::ToDart(message->channel()), data_handle,
                              tonic::ToDart(response_id)}));
}

這里將message->data()轉(zhuǎn)化成Dart_Handle恨课,tonic::DartInvokeField進(jìn)入dart_invoke.cc

Dart_Handle DartInvokeField(Dart_Handle target,
                            const char* name,
                            std::initializer_list<Dart_Handle> args) {
  Dart_Handle field = Dart_NewStringFromCString(name);
  return Dart_Invoke(target, field, args.size(),
                     const_cast<Dart_Handle*>(args.begin()));
}

Dart_Handle DartInvoke(Dart_Handle closure,
                       std::initializer_list<Dart_Handle> args) {
  int argc = args.size();
  Dart_Handle* argv = const_cast<Dart_Handle*>(args.begin());
  Dart_Handle handle = Dart_InvokeClosure(closure, argc, argv);
  LogIfError(handle);
  return handle;
}

這里Dart_InvokeClosure完成調(diào)用,這里為什么Dart_InvokeClosure就能完成C++代碼到dart代碼的調(diào)用岳服,據(jù)說是有dart_vm剂公,但找了半天也沒找到dart_vm的源碼,只找到一些代碼片段

//sdk/runtime/include/dart_api.h
DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
Dart_InvokeClosure(Dart_Handle closure,
                   int number_of_arguments,
                   Dart_Handle* arguments);

//dart/sdk/runtime/vm/dart_api_impl.cc
DART_EXPORT Dart_Handle Dart_InvokeClosure(Dart_Handle closure,
                                           int number_of_arguments,
                                           Dart_Handle* arguments) {
  DARTSCOPE(Thread::Current());
  API_TIMELINE_DURATION(T);
  CHECK_CALLBACK_STATE(T);
  const Instance& closure_obj = Api::UnwrapInstanceHandle(Z, closure);
  if (closure_obj.IsNull() || !closure_obj.IsCallable(NULL)) {
    RETURN_TYPE_ERROR(Z, closure, Instance);
  }
  if (number_of_arguments < 0) {
    return Api::NewError(
        "%s expects argument 'number_of_arguments' to be non-negative.",
        CURRENT_FUNC);
  }

  // Set up arguments to include the closure as the first argument.
  const Array& args = Array::Handle(Z, Array::New(number_of_arguments + 1));
  Object& obj = Object::Handle(Z);
  args.SetAt(0, closure_obj);
  for (int i = 0; i < number_of_arguments; i++) {
    obj = Api::UnwrapHandle(arguments[i]);
    if (!obj.IsNull() && !obj.IsInstance()) {
      RETURN_TYPE_ERROR(Z, arguments[i], Instance);
    }
    args.SetAt(i + 1, obj);
  }
  // Now try to invoke the closure.
  return Api::NewHandle(T, DartEntry::InvokeClosure(args));
}

最終就會(huì)調(diào)用到

hooks.dart
@pragma('vm:entry-point')
void _dispatchPlatformMessage(String name, ByteData data, int responseId) {
  if (name == ChannelBuffers.kControlChannelName) {
    try {
      channelBuffers.handleMessage(data);
    } catch (ex) {
      _printDebug('Message to "$name" caused exception $ex');
    } finally {
      window._respondToPlatformMessage(responseId, null);
    }
  } else if (window.onPlatformMessage != null) {
    _invoke3<String, ByteData, PlatformMessageResponseCallback>(
      window.onPlatformMessage,
      window._onPlatformMessageZone,
      name,
      data,
      (ByteData responseData) {
        window._respondToPlatformMessage(responseId, responseData);
      },
    );
  } else {
    channelBuffers.push(name, data, (ByteData responseData) {
      window._respondToPlatformMessage(responseId, responseData);
    });
  }
}

這里會(huì)判斷方法名是否是kControlChannelName吊宋,如果是將進(jìn)行一些系統(tǒng)的處理纲辽,我們自定義的方法不會(huì)走到這里,來到下面的_invoke3這里已經(jīng)能夠和本文最上面給出的堆棧吻合上了璃搜,下面執(zhí)行_invoke3方法拖吼,這里是不能調(diào)試的, 所以只能從源碼上進(jìn)行分析这吻,這里_invoke1 _invoke2 _invoke3是根據(jù)參數(shù)的不同來定義的吊档,除了window.onPlatformMessagewindow._onPlatformMessageZone后面的參數(shù)個(gè)數(shù)就是invoke的編號(hào),_invoke3接收3個(gè)參數(shù)唾糯,方法名怠硼,方法的參數(shù)和回調(diào),原生代碼調(diào)用dart也是可以傳遞回調(diào)的移怯。

void _invoke3<A1, A2, A3>(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) {
  if (callback == null)
    return;

  assert(zone != null);

  if (identical(zone, Zone.current)) {
    callback(arg1, arg2, arg3);
  } else {
    zone.runGuarded(() {
      callback(arg1, arg2, arg3);
    });
  }
}

首先會(huì)對(duì)invoke的前兩個(gè)默認(rèn)參數(shù)進(jìn)行校驗(yàn)香璃,這里也會(huì)涉及到上一篇文章說到的ZonerunGuarded就是增加了保護(hù)舟误,大神寫代碼就是會(huì)比較飄逸

 /**
   * Executes the given [action] in this zone and catches synchronous
   * errors.
   *
   * This function is equivalent to:
   * ```
   * try {
   *   this.run(action);
   * } catch (e, s) {
   *   this.handleUncaughtError(e, s);
   * }
   * ```
   *
   * See [run].
   */
  void runGuarded(void action());

然后執(zhí)行callback(arg1, arg2, arg3)葡秒,window.onPlatformMessage實(shí)際是_DefaultBinaryMessenger的對(duì)象的handlePlatformMessage方法,是在WidgetsFlutterBinding.ensureInitialized()初始化綁定的時(shí)候進(jìn)行賦值的:

mixin ServicesBinding on BindingBase {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _defaultBinaryMessenger = createBinaryMessenger();
    window
      ..onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
    initLicenses();
    SystemChannels.system.setMessageHandler(handleSystemMessage);
  }

所以執(zhí)行callback(arg1, arg2, arg3)會(huì)調(diào)用handlePlatformMessage方法

  @override
  Future<void> handlePlatformMessage(
    String channel,
    ByteData data,
    ui.PlatformMessageResponseCallback callback,
  ) async {
    ByteData response;
    try {
      final MessageHandler handler = _handlers[channel];
      if (handler != null) {
        response = await handler(data);
      } else {
        ui.channelBuffers.push(channel, data, callback);
        callback = null;
      }
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'services library',
        context: ErrorDescription('during a platform message callback'),
      ));
    } finally {
      if (callback != null) {
        callback(response);
      }
    }
  }

這里會(huì)根據(jù)channel取出不同的handler嵌溢,_handlerdartsetMessageHandler存儲(chǔ)到字典中的

class MethodChannelWebViewPlatform implements WebViewPlatformController {
  /// Constructs an instance that will listen for webviews broadcasting to the
  /// given [id], using the given [WebViewPlatformCallbacksHandler].
  MethodChannelWebViewPlatform(int id, this._platformCallbacksHandler)
      : assert(_platformCallbacksHandler != null),
        _channel = MethodChannel('plugins.flutter.io/webview_$id') {
    _channel.setMethodCallHandler(_onMethodCall);
  }

組件在dart端初始化的時(shí)候如果設(shè)置了setMethodCallHandler眯牧,會(huì)調(diào)用_DefaultBinaryMessengersetMessageHandler方法堵腹,這樣通過_handleAsMethodCall方法封裝的handler就被存儲(chǔ)在_DefaultBinaryMessenger中的_handlers里了炸站,當(dāng)_handler執(zhí)行的時(shí)候會(huì)調(diào)用_handleAsMethodCall

  void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) {
    binaryMessenger.setMessageHandler(
      name,
      handler == null ? null : (ByteData message) => _handleAsMethodCall(message, handler),
    );
  }
  Future<ByteData> _handleAsMethodCall(ByteData message, Future<dynamic> handler(MethodCall call)) async {
    final MethodCall call = codec.decodeMethodCall(message);
    try {
      return codec.encodeSuccessEnvelope(await handler(call));
    } on PlatformException catch (e) {
      return codec.encodeErrorEnvelope(
        code: e.code,
        message: e.message,
        details: e.details,
      );
    } on MissingPluginException {
      return null;
    } catch (e) {
      return codec.encodeErrorEnvelope(code: 'error', message: e.toString(), details: null);
    }
  }

這里會(huì)將message轉(zhuǎn)化成MethodCall,執(zhí)行handler(call)就會(huì)回調(diào)_channel.setMethodCallHandler(_onMethodCall)里的_onMethodCall

  Future<bool> _onMethodCall(MethodCall call) async {
    switch (call.method) {
      case 'javascriptChannelMessage':
        final String channel = call.arguments['channel'];
        final String message = call.arguments['message'];
        _platformCallbacksHandler.onJavaScriptChannelMessage(channel, message);
        return true;
      case 'navigationRequest':
        return await _platformCallbacksHandler.onNavigationRequest(
          url: call.arguments['url'],
          isForMainFrame: call.arguments['isForMainFrame'],
        );
      case 'onPageFinished':
        _platformCallbacksHandler.onPageFinished(call.arguments['url']);
        return null;
      case 'onPageStarted':
        _platformCallbacksHandler.onPageStarted(call.arguments['url']);
        return null;
      case 'onWebResourceError':
        _platformCallbacksHandler.onWebResourceError(
          WebResourceError(
            errorCode: call.arguments['errorCode'],
            description: call.arguments['description'],
            domain: call.arguments['domain'],
            errorType: call.arguments['errorType'] == null
                ? null
                : WebResourceErrorType.values.firstWhere(
                    (WebResourceErrorType type) {
                      return type.toString() ==
                          '$WebResourceErrorType.${call.arguments['errorType']}';
                    },
                  ),
          ),
        );
        return null;
    }

    throw MissingPluginException(
      '${call.method} was invoked but has no handler',
    );
  }

這里就是dart層面的分發(fā)了疚顷,這樣就完成了原生代碼對(duì)dart代碼的調(diào)用旱易。

總結(jié)

原生代碼調(diào)用Dart接口,經(jīng)歷了兩個(gè)階段:

  • 原生層的轉(zhuǎn)發(fā)腿堤,最終執(zhí)行Dart_InvokeClosure
  • Dart VM實(shí)現(xiàn)了原生對(duì)Dart接口的調(diào)用阀坏,進(jìn)入vm:entry-point_dispatchPlatformMessage此后方法在Dart層進(jìn)行一層層傳遞,最終調(diào)用通過閉包的層層回溯完成了setMethodCallHandler的回調(diào)笆檀。

遺留問題

  • FlutterStandardWriter的工作機(jī)制忌堂,這里也會(huì)結(jié)合上上一篇文章WriteBuffer進(jìn)行講解,并深入研究codec的作用酗洒,了解通信過程中數(shù)據(jù)是如何傳遞的士修。
  • FlutterViewController中為什么使用engine_.get()以及_engine的類型為什么是fml::scoped_nsobject<FlutterEngine>
  • task_runners_.GetUITaskRunner()->PostTask的實(shí)現(xiàn)原理和作用枷遂。
  • 系統(tǒng)定義的channel: kLifecycleChannelkLocalizationChannel棋嘲,kSettingsChannel是做什么的酒唉,這里也會(huì)結(jié)合上一篇文章kSkiaChannel進(jìn)行
  • DartIsolate的核心作用是什么?
  • Dart VM的工作原理是怎樣的沸移?原生代碼為什么可以調(diào)用到Dart代碼痪伦,這里也將集合上一篇文章中Dart代碼調(diào)用原生代碼的原理進(jìn)行。

參考文獻(xiàn)

Dart和Java通信源碼分析和實(shí)踐

寫在最后

Flutter現(xiàn)在還不能說很穩(wěn)定雹锣,截止發(fā)稿网沾,查閱了一些資料,也對(duì)比了一些源碼蕊爵,發(fā)現(xiàn)有些引擎層的實(shí)現(xiàn)代碼已經(jīng)發(fā)生了改變辉哥,而且現(xiàn)階段也還有些設(shè)計(jì)不夠合理或者嚴(yán)謹(jǐn)?shù)牡胤接写倘叮磥鞦lutter底層的研究以及性能的優(yōu)化還會(huì)持續(xù)一段時(shí)間攒射。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末证薇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子匆篓,更是在濱河造成了極大的恐慌,老刑警劉巖寇窑,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鸦概,死亡現(xiàn)場離奇詭異,居然都是意外死亡甩骏,警方通過查閱死者的電腦和手機(jī)窗市,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饮笛,“玉大人咨察,你說我怎么就攤上這事「G啵” “怎么了摄狱?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長无午。 經(jīng)常有香客問我媒役,道長,這世上最難降的妖魔是什么宪迟? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任酣衷,我火速辦了婚禮,結(jié)果婚禮上次泽,老公的妹妹穿的比我還像新娘穿仪。我一直安慰自己席爽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布啊片。 她就那樣靜靜地躺著只锻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上冕茅,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天样屠,我揣著相機(jī)與錄音,去河邊找鬼沈矿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咬腋,可吹牛的內(nèi)容都是我干的羹膳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼根竿,長吁一口氣:“原來是場噩夢啊……” “哼陵像!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寇壳,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤醒颖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后壳炎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泞歉,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年匿辩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腰耙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铲球,死狀恐怖挺庞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情稼病,我是刑警寧澤选侨,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站溯饵,受9級(jí)特大地震影響侵俗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜丰刊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一隘谣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦寻歧、人聲如沸掌栅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猾封。三九已至,卻和暖如春噪珊,著一層夾襖步出監(jiān)牢的瞬間晌缘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國打工痢站, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留磷箕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓阵难,卻偏偏與公主長得像岳枷,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呜叫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344