flutter 訊息傳遞機制

無若葉發表於2020-03-06

flutter 訊息傳遞機制

flutter 中的 WidgetsFlutterBinding 整合了 GestureBinding、ServicesBinding、SchedulerBinding、PaintingBinding、SemanticsBinding、RendererBinding、WidgetsBinding 等 7 種 Binding,它們都有自己在功能上的劃分,其中,ServicesBinding 主要負責的是 flutter 與 native 之間傳遞資訊相關。

在 ServicesBinding 的 initInstances 函式中主要有兩個操作,將 window.onPlatformMessage 與 defaultBinaryMessenger.handlePlatformMessage 關聯起來,再讀取 license 相關的資訊,從使用上來說,license 主要是為了支撐 about 頁面的展示,除此之外好像沒有其他的用處。

而 defaultBinaryMessenger 就是 flutter 與 native 之間進行訊息傳遞的關鍵,它負責在二者之間提供接收、傳送資料的介面。首先,從介面上看,它有提供 send、setMessageHandler 這兩個函式,分別用於傳送資料、處理資料,flutter 上層的 BasicMessageChannel、MethodChannel、EventChannel 等都是基於它實現的。從 flutter 層來看,它的訊息傳遞機制包括三層:

  1. 基礎傳輸:主要實現位於 engine 中,內部不區分訊息型別
  2. 分類傳輸:主要實現位於 flutter 中,將訊息分為三種型別,BasicMessageChannel、MethodChannel 和 EventChannel,它們對資料傳輸做了封裝,分別支援跨層的單次資料傳遞(含接收資料)、方法呼叫(含呼叫和被呼叫)和持續資料接收。
  3. 上層呼叫:這部分主要是一些基礎資料傳輸實現的功能,比如 SystemChannels.navigation、SystemChannels.system 和 flutter 外掛的實現等。

以下分層瞭解:

基礎傳輸

這部分以 BinaryMessenger 為主體,看一下它的各個函式是如何實現的,實現主要分為兩部分:傳送訊息和接收訊息。傳送訊息包括兩部分,首先,flutter 將一個資料通過 engine 層傳遞到 native 層,native 處理完了之後會有一個結果返回的過程,表現出來就是回撥,這也正是 BinaryMessenger 的 send 函式返回結果是一個 Future 型別的原因。而把上面這個過程反過來,改為 native 向 flutter 傳送訊息,flutter 迴應,那麼迴應的這部分就對應著接收訊息的實現,處理結果通過 callback 回撥給 native 。所以 flutter 中的傳送、接收訊息可以看作是一個常規訊息傳遞的一半,再結合 native 的傳送、接收訊息,就構成了兩個傳送、接收的流程。

傳送流程

傳送過程從 send 開始,返回值是 Future,在 _sendPlatformMessage 中使用 Completer 將 Future 轉成了回撥函式:

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 準備進入 engine 層,與之對應的函式為 SendPlatformMessage:

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();
}
複製程式碼

在這裡先是將 callback 封裝成了 PlatformMessageResponseDart,方便之後的回撥,然後再將 name 和 PlatformMessageResponseDart 一起封裝成 PlatformMessage,作為單個訊息傳送。後通過 RuntimeController 轉遞給 Engine,在這裡,Engine 對 asset 請求訊息進行了攔截,HandleAssetPlatformMessage 對其負責。asset 請求一般用於 flutter 中獲取 asset 資料夾的資源,這個由 engine 層直接處理,不需要通過 native 層。

其他的訊息會再轉遞給 Shell,Shell 中又對 skia 訊息進行攔截,可以設定 skia 的引數,目前只支援設定 SetResourceCacheMaxBytes,可以呼叫 flutter 中的 SystemChannels.skia 設定。

再剩下的訊息就是需要 native 層處理的了,以 android 為例,

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);
  }
}
複製程式碼

這裡會將 PlatformMessage 拆解,將 PlatformMessageResponseDart 儲存在 pending_responses_ 中,將它的 id 傳遞到 native 中,供結果回撥時使用,最後由 FlutterViewHandlePlatformMessage 通過 jni 呼叫 android 中的方法,對應 FlutterJNI#handlePlatformMessage。

public void handleMessageFromDart(
    @NonNull final String channel, @Nullable byte[] message, final int replyId) {
  Log.v(TAG, "Received message from Dart over channel '" + channel + "'");
  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 中也有一個類似於 defaultBinaryMessenger 的物件 platformMessageHandler,handleMessageFromDart 方法接收訊息,並在其中從 messageHandlers 中取出對應 channel 的 handler(通過 setMessageHandler 設定),呼叫其 onMessage 方法進行處理,replyId 也就是之前生成的 responseId,它被封裝成了 Reply。handler 是 BinaryMessageHandler 物件,在 android 中也有三種 Channel,BasicMessageChannel、MethodChannel 和 EventChannel(具體不同類的處理過程,可以參照下面 flutter 中的介紹),其功用與 flutter 中對應類一致。

處理完之後則是呼叫 Reply 的 reply 方法,將結果回傳給 flutter 。

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());
  }
}
複製程式碼

最上面是一個檢驗,一次來自 flutter 的訊息,只能回傳一個資料。然後根據結果是否為空選擇呼叫 invokePlatformMessageEmptyResponseCallback 還是 invokePlatformMessageResponseCallback,二者只是引數的區別。

接著 invokePlatformMessageResponseCallback 會通過 jni 呼叫 InvokePlatformMessageResponseCallback 函式,然後轉到 PlatformViewAndroid::InvokePlatformMessageResponseCallback,之前在 flutter 傳送訊息時轉成 responseId 的 PlatformMessageResponse 這裡會被找回來,呼叫其 Complete 函式進行回撥。

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});
      }));
}
複製程式碼

主要就是通過 DartInvoke 呼叫 flutter 中的 callback 函式,將結果 byte_buffer 傳遞回去,這個 callback,就是 _sendPlatformMessage 中宣告的函式,complete 函式的入參。由此,Future 便可以得到結果,BinaryMessenger.send 函式返回的 Future 便可以得到結果,一次完整的訊息傳送過程結束。

接收流程

接收流程本質上就是傳送流程的逆過程,但是細節上會有不同,接收流程由 native 中的 send 函式開始,以 android 為例,就是 DartMessenger#send 方法。

在 android 中的 send 方法中,callback 就被轉換成了 resposeId,callback 被儲存在 pendingReplies 中,然後呼叫 flutterJNI.dispatchPlatformMessage,通過 jni 呼叫 DispatchPlatformMessage 函式,後面就是經過 jni 函式再呼叫 PlatformViewAndroid::DispatchPlatformMessage,在這個函式中,它也是對 callback 進行了進一步的封裝成了 PlatformMessageResponseAndroid,但是這裡的 callback 是在 android 中轉換之後的 responseId。同時把 name 和 PlatformMessageResponseAndroid 一起封裝成 PlatformMessage,這個與前面從 flutter 中傳送訊息的過程是類似的。

完了之後再經過 PlatformView、Shell 傳遞到達 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));
    return;
  }

  FML_DLOG(WARNING) << "Dropping platform message on channel: "
                    << message->channel();
}
複製程式碼

在這裡 engine 會嘗試攔截部分的訊息,比如使用 HandleLifecyclePlatformMessage 先處理一下生命週期相關的訊息,根據它的返回值決定是否會攔截,不過從函式的目前實現來看,是不會攔截的,這裡的更多的意義應該在於這些訊息在 engine 和 flutter 中都有被使用到,比如生命週期事件,會影響到 engine 中 activity_running_ 和 flutter 中 _framesEnabled 的值,從功用上看,二者其實是一致的,只不過位於不同層,並且二者之間貌似目前只能通過這裡來統一狀態。

後面還會攔截 localization 和 setting 訊息,然後才是呼叫 DispatchPlatformMessage 繼續向 flutter 層傳遞訊息,如果當前還處於初始化階段,runtime_ 未初始化完成時,會直接由 engine 處理 navigation 事件(目前只接收 setInitialRoute),將 initialRoute 儲存起來,這個值會在 flutter 啟動時作為初始的 route 來建立對應的 Widget 。

正常的傳遞,還要經過 RuntimeController 到 window,在 window 中再將 PlatformMessageResponseAndroid 存到 pending_responses_ 中,提供 responseId,再拆解 PlatformMessage,通過 DartInvokeField 呼叫 dart 中的 _dispatchPlatformMessage 進入到 flutter 裡。

void _dispatchPlatformMessage(String name, ByteData data, int responseId) {
  if (window.onPlatformMessage != null) {
    _invoke3<String, ByteData, PlatformMessageResponseCallback>(
      window.onPlatformMessage,
      window._onPlatformMessageZone,
      name,
      data,
      (ByteData responseData) {
        window._respondToPlatformMessage(responseId, responseData);
      },
    );
  } else {
    window._respondToPlatformMessage(responseId, null);
  }
}
複製程式碼

如上,這裡宣告瞭一個 callback 函式,作為入參呼叫 window.onPlatformMessage,也就是在 ServicesBinding 中給出的 defaultBinaryMessenger.handlePlatformMessage。

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);
  } catch (exception, stack) {
    FlutterError.reportError(FlutterErrorDetails(
      exception: exception,
      stack: stack,
      library: 'services library',
      context: ErrorDescription('during a platform message callback'),
    ));
  } finally {
    callback(response);
  }
}
複製程式碼

接收到訊息之後,BinaryMessager 需要選擇合適的 MessageHandler 處理訊息,MessageHandler 則是通過 setMessageHandler 進行註冊的,有三種不同的 MessageHandler,分別對應著 BasicMessageChannel、MethodChannel 和 EventChannel ,就是是下面分類傳輸的內容,總之,經過 MessageHandler 處理之後會得到 ByteData 型別的處理結果,最終由 callback 將結果回傳給 native 。

callback 中呼叫的就是 window._respondToPlatformMessage,對應 engine 中的函式 RespondToPlatformMessage。這裡會根據 response 是否為空決定呼叫 CompletePlatformMessageEmptyResponse 還是 CompletePlatformMessageResponse。

在 CompletePlatformMessageResponse 中,會根據 responseId 再找回 PlatformMessageResponseAndroid,呼叫其 Complete 將資料傳遞給 android。

void PlatformMessageResponseAndroid::Complete(
    std::unique_ptr<fml::Mapping> data) {
  platform_task_runner_->PostTask(
      fml::MakeCopyable([response = response_id_,               //
                         weak_java_object = weak_java_object_,  //
                         data = std::move(data)                 //
  ]() {
        // We are on the platform thread. Attempt to get the strong reference to
        // the Java object.
        auto* env = fml::jni::AttachCurrentThread();
        auto java_object = weak_java_object.get(env);

        if (java_object.is_null()) {
          // The Java object was collected before this message response got to
          // it. Drop the response on the floor.
          return;
        }

        // Convert the vector to a Java byte array.
        fml::jni::ScopedJavaLocalRef<jbyteArray> data_array(
            env, env->NewByteArray(data->GetSize()));
        env->SetByteArrayRegion(
            data_array.obj(), 0, data->GetSize(),
            reinterpret_cast<const jbyte*>(data->GetMapping()));

        // Make the response call into Java.
        FlutterViewHandlePlatformMessageResponse(env, java_object.obj(),
                                                 response, data_array.obj());
      }));
}
複製程式碼

這裡的 FlutterViewHandlePlatformMessageResponse 就是 jni 的函式,由 jni 將資料傳遞給 FlutterJNI 的 handlePlatformMessageResponse。

在 handlePlatformMessageResponse 的實現中,會根據 replyId 從 pendingReplies 中找到 callback,也就是 send 方法中傳進的,callback 是由上層實現的。

以上就是基礎傳輸過程中,兩個方向的處理步驟,在這個流程中主要關注的是訊息的傳遞,基本不關心訊息的型別及處理(不過在 Engine、Shell 類中還是有對訊息的攔截處理)。

分類傳輸

分類傳輸主要關心的就是資料的型別,在 flutter(包括 android 中也存在與之對應的)中存在三種型別的訊息,就是 BasicMessageChannel、MethodChanel 和 EventChannel,每一 Channel 的構造都至少需要兩個引數,與之繫結的訊息型別(訊息的 name)和編碼方式(MessageCodec/MethodCodec)。

BasicMessageChannel

BasicMessageChannel 用於雙向的單次訊息傳遞,包括髮送訊息、接收訊息兩個功能。傳送訊息使用的為 send 函式,返回值是 Future,

Future<T> send(T message) async {
  return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
}
複製程式碼

如上,BasicMessageChannel 主要的封裝就是在呼叫 binaryMessenger.send 前後對 message 進行編解碼,從這裡也可以看出,send 函式的引數與返回值是同一種型別的(雖然可以使用 dynamic 抹除 T 的作用)。

而 setMessageHandler 則是用於向 binaryMessenger 註冊一個訊息處理函式,

void setMessageHandler(Future<T> handler(T message)) {
  if (handler == null) {
    binaryMessenger.setMessageHandler(name, null);
  } else {
    binaryMessenger.setMessageHandler(name, (ByteData message) async {
      return codec.encodeMessage(await handler(codec.decodeMessage(message)));
    });
  }
}
複製程式碼

handler 是具體的處理邏輯,BasicMessageChannel 所做的也還是在處理前後將 message 編解碼。

資料轉換流程

從以上可以看出,一次 BasicMessageChannel 的 send 過程中,message 的型別轉換有如下過程:

T(in flutter) -> ByteData(in flutter) -> DartByteData(in engine) -> buffer(in engine) -> vector<uint8_t>(in engine) -> byte[](in jni) -> ByteBuffer(in java) -> T(in java) -> process -> ByteBuffer(in java) -> vector<uint8_t>(in engine) -> DataMapping(in engine) -> ByteData(in engine) -> T(in flutter)

MethodChannel

MethodCannel 用於雙向的方法呼叫,包括呼叫對端方法、響應對端呼叫,流程與 BasicMessageChannel 基本類似,只是資料轉換上略有不同,invokeMethod 用於呼叫對端方法,setMethodCallHandler 用於註冊一個方法的處理函式。

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;
}
複製程式碼

invokeMethod 有兩個引數,方法名和方法入參,在 MethodCannel 中這兩個引數會被封裝成 MethodCall 物件,然後利用 MethodCodec 將 MethodCall 編碼成 ByteData 傳遞到 binaryMessenger,並得到 ByteData 型別的返回值,後面再利用 MethodCodec 將其解碼成 T 型別資料,由此可以看到,MethodCannel 的 invokeMethod 函式的入參與返回值是不同型別的,MethodCodec 相對於 MessageCode 多了兩個函式,分別用於編解碼 MethodCall 和編解碼方法呼叫的返回值。

而 setMethodCallHandler 用於註冊呼叫方法供 native 呼叫,

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);
  }
}
複製程式碼

它會將 handler 封裝到 callback 函式中,在 _handleAsMethodCall 中進行協調,也就是先把 message 轉成 MethodCall 物件之後,再交給 handler 處理,處理完的結果再由 codec 轉成 ByteData 並返回,接著 binaryMessenger 再拿著這個資料回傳到 native 中。

資料轉換流程

flutter 呼叫 native 函式:

(method, message) -> MethodCall(in flutter) -> ByteData(in flutter) -> ...(一系列基礎傳輸流程) -> ByteBuffer(in java) -> MethodCall(in java) -> process -> Object(in java) -> ByteBuffer(in java) -> ...(一系列基礎傳輸流程) -> ByteData(in flutter) -> T

navtive 呼叫 flutter 函式:

(method, message) -> MethodCall(in java) -> ByteBuffer(in java) -> ...(一系列基礎傳輸流程) -> ByteData(in flutter) -> MethodCall(in flutter) -> process -> dynamic(in flutter) -> ByteData(in flutter) -> ...(一系列基礎傳輸流程) -> byte[](in java) -> ByteBuffer(in java) -> Object(in java)

另外,MethodChannel 還有一個子類,OptionalMethodChannel,支援返回 null 結果,這在 MethodChannel 中是不允許的。

EventChannel

EventChannel 用於接收一系列訊息,這些訊息被包裝到 Stream 中,receiveBroadcastStream 返回一個 Stream 物件,可以在 Stream 上新增監聽者,然後開始傳送 listen 訊息到 native 層後,native 中對應的 EventChannel 就開始處理訊息,在處理的過程中可以傳送多條訊息,這些訊息的在傳到 flutter 中後都會被加入到 Stream 中,然後通知所有的監聽者。

在 listen 開始之前,native 中的 EventChannel 無法主動開始工作,當 flutter 中 Stream 的第一個監聽者成功新增時,才會向 native 傳送 listen 訊息,此時,二者之間建立連線,開始工作。

在 listen 的過程中間,二者並不能進行交流,native 層收到 listen 訊息就開始不斷髮送訊息,flutter 中也只能通過 Stream 不斷接收訊息。

二者都可以主動結束連線,在 native 中可以呼叫 EventSink.endOfStream 結束連線,在 flutter 中移除 Stream 的所有監聽者也可以結束連線。如果需要再次建立連線,就需要向 Stream 中新增監聽者。

由上述可以得出,在 EventChannel 中,native 與 flutter 之間不平等,且二者向對方傳送訊息時都不需要接收返回的結果,而是直接通過新的 send 途徑向對方傳送訊息,這是 EventChannel 與以上兩種方式最大的不同。

下面具體介紹 EventChannel 的工作原理,EventChannel 在初始化時需要提供 name 和 MethodCodec,然後呼叫 receiveBroadcastStream 返回一個 Stream 物件,後續上層主要是通過在 Stream 中新增監聽來完成對 native 層訊息的接收,同時新增/刪除監聽的操作,可以開始/結束接收訊息。

Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
  final MethodChannel methodChannel = MethodChannel(name, codec);
  StreamController<dynamic> controller;
  controller = StreamController<dynamic>.broadcast(onListen: () async {
    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);
    } catch (exception, stack) {
      FlutterError.reportError(FlutterErrorDetails(
        exception: exception,
        stack: stack,
        library: 'services library',
        context: ErrorDescription('while activating platform stream on channel $name'),
      ));
    }
  }, onCancel: () async {
    defaultBinaryMessenger.setMessageHandler(name, null);
    try {
      await methodChannel.invokeMethod<void>('cancel', arguments);
    } 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;
}
複製程式碼

EventChannel 的主要邏輯都在上面這個函式中了,呼叫之後會先生成一個 MethodChannel 用於向 native 傳送訊息,然後就是建立了一個 StreamController,在它的 onListen 函式中註冊了一個訊息接收,在接收到訊息之後會將其轉存到 StreamController 中,StreamController 負責將訊息傳送到它的監聽者們,註冊之後就是向 native 傳送一個 listen 訊息,開始接收。在 onCancel 函式中取消訊息接收,同時傳送一個 cancel 訊息給 native ,讓 native 中的 EventChannel 做一些結尾工作等。

再看看 native (以 android 為例)中 EventChannel 的工作原理,EventChannel 通過呼叫 setStreamHandler 方法,StreamHandler 由上層提供,負責具體的處理邏輯,它有 onListen 和 onCancel 兩個方法,前者提供了 arguments(引數)和 events(用於傳送訊息、報錯和結束連線),後者只提供了 argments 引數。

在 setStreamHandler 中,EventChannel 將 StreamHandler 封裝到 IncomingStreamRequestHandler 中,IncomingStreamRequestHandler 中的 onMessage 就是 native 接收到訊息之後的回撥方法,接收到 onListen 訊息會呼叫 onListen 函式,在 onListen 中,除去一些維護正常狀態的程式碼,就是呼叫 StreamHandler 的 listen 函式。native 中的 EventChannel 也可以通過呼叫 EventSink.endOfStream 結束連線,它會向 flutter 中傳送一個空訊息,在 flutter 中接收到空訊息時便會呼叫 StreamController.close 結束接收訊息。

上層呼叫

對於上層呼叫,flutter 中主要集中在 SystemChannels 中,比如 navigation、lifecycle 等。

navigation

navigation 是一個 MethodChannel,在 WidgetsBinding 中會給它設定監聽,呼叫 SystemChannels.navigation.setMethodCallHandler 傳遞進去 _handleNavigationInvocation 函式,

Future<dynamic> _handleNavigationInvocation(MethodCall methodCall) {
  switch (methodCall.method) {
    case 'popRoute':
      return handlePopRoute();
    case 'pushRoute':
      return handlePushRoute(methodCall.arguments);
  }
  return Future<dynamic>.value();
}
複製程式碼

如上,這裡會根據 MethodCall 的方法名呼叫不同呼叫 pop/push 函式完成頁面切換。

lifecycle

lifcycle 是一個 BasicMessageChannel,在 SchedulerBinding 中設定監聽,傳進的 handler 為 _handleLifecycleMessage,

Future<String> _handleLifecycleMessage(String message) async {
  handleAppLifecycleStateChanged(_parseAppLifecycleMessage(message));
  return null;
}

void handleAppLifecycleStateChanged(AppLifecycleState state) {
  assert(state != null);
  _lifecycleState = state;
  switch (state) {
    case AppLifecycleState.resumed:
    case AppLifecycleState.inactive:
      _setFramesEnabledState(true);
      break;
    case AppLifecycleState.paused:
    case AppLifecycleState.suspending:
      _setFramesEnabledState(false);
      break;
  }
}
複製程式碼

如上,會根據 native 傳來的 activity 的狀態,決定是否允許更新介面。

總結

以上就是 flutter 與 native 之間進行訊息傳遞的過程、實現原理以及一些使用方法,通過訊息傳遞機制,flutter 能夠十分方便的呼叫 native 的能力,這一點的意義主要在於以下兩點:

  1. 能夠在 flutter 中使用一些 native 特有的能力,比如 shared_preference 這個用於本地持久化的 flutter 外掛,它的實現就依賴於 native,比如 android 中的 SharedPreference,ios 中的 UserDefaults,這種情況下,在三端分別實現 MethodChannel,flutter 中就可以在 android/ios 平臺中使用統一的呼叫方法。
  2. 方便在 flutter 中複用 native 中已有的能力,比如在已有的專案中加入部分 flutter 頁面,那麼在這些 flutter 頁面中也可以很方便地使用一些專案中原有的能力,降低開發成本。

相關文章