Flutter学习系列(11)— Platform Channel通信(下)

向Android发送数据

前面一篇已经介绍了Android主动向Flutter发送数据请求的流程,这一篇主页看看Flutter向Android发送数据的流程。有了前面的基础,反过来基本区别不大。

先看一下Flutter端的Demo, 创建了一个MethodChannel,然后调用了invokeMethod方法, 唯一的区别在于Flutter端的方法调用没有回调。但是有返回值,Android端MethodChannel 实际是包装了一下返回结果。

const platform = const MethodChannel('com.test.native/logger'); 
platform.invokeMethod("d", {"tag": "MyApp", "log": "build"});

类图

先看一下Dart中和消息相关的类图,整体结构和前面Java的差不多。 只是BinaryMessenger的Callback中缺少了一个Reply参数, 这是因为Dart中send是有返回值的 (应该得益于async/await机制)。DefaultBinaryMessenger扮演了Java中DartMessenger的角色。

Dart发送数据


//创建MethodChannel const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), this.binaryMessenger = defaultBinaryMessenger ]) : assert(name != null), assert(binaryMessenger != null), assert(codec != null); //发送数据 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 channelname'); } final T typedResult = codec.decodeEnvelope(result); return typedResult; }

前面已经介绍过来了,Dart使用的是defaultBinaryMessenger来发送数据,最终调用的他的send方法。 注意这里用了await。

  @override
  Future<ByteData> send(String channel, ByteData message) {
    final MessageHandler handler = _mockHandlers[channel];
    if (handler != null)
      return handler(message);
    return _sendPlatformMessage(channel, message);
  }

Dart这边包含了一个mockHandler用来测试,正常是null,所以不用管它。最终调用了内部方法:

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

这里使用了Dart中的window进行消息发送。他实际调用了C++层Window.cc的方法。 因为Dart这边没有Reply,所以也没有replayid。

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

    //把Dart层的callback包装成C++层的PlatformMessageResponseDart
  fml::RefPtr<PlatformMessageResponse> response;
  if (!Dart_IsNull(callback)) {
    response = fml::MakeRefCounted<PlatformMessageResponseDart>(
        tonic::DartPersistentValue(dart_state, callback),
        dart_state->GetTaskRunners().GetUITaskRunner());
  }

    // 处理Dart发来的消息,把name,data,response转换为PlatformMessage
  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();
}

这里调用HandlePlatformMessage的顺序是 : RuntimeController(WindowClient) –> Engine(RuntimeDelegate) –> Shell(Engine::Delegate) –> PlatformViewAndroid

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;

  // 因为Java层有Repaly,所有这里构建一个response_Id,并且把id和response对应关系存放到了pending_responses_
  int response_id = 0;
  if (auto response = message->response()) {
    response_id = next_response_id_++;
    pending_responses_[response_id] = response;
  }

    // 从PlatformMessage中取出channel 和 data,转换为java的数据类型, 然后
  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);
  }
}

这里和上一篇从Dart返回数据给Java是一样的

static jmethodID g_handle_platform_message_method = nullptr;
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));
}

// 指向的Java方法
  g_handle_platform_message_method =
      env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
                       "(Ljava/lang/String;[BI)V");

这里很熟了,回到FluterJNI, 熟悉的java代码,熟悉的platformMessageHandler

  private void handlePlatformMessage(@NonNull final String channel, byte[] message, final int replyId) {
    if (platformMessageHandler != null) {
      platformMessageHandler.handleMessageFromDart(channel, message, replyId);
    }
    // TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391)
  }

最终回到了DartMessenger

@Override
  public void handleMessageFromDart(
      @NonNull final String channel,
      @Nullable byte[] message,
      final int replyId
  ) {
    Log.v(TAG, "Received message from Dart over channel '" + channel + "'");

    // 根据chennel name找到对应的handler
    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处理消息

   protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);

        // flutter -> Android
        new MethodChannel(getFlutterView(), "com.test.native/logger").setMethodCallHandler((call, result) -> {

            if (call.method.equals("d")) {
                Logger.d(call.argument("tag"), call.argument("log"));
                result.success(null);
            } else if (call.method.equals("e")) {
                Logger.d(call.argument("tag"), call.argument("log"));
                result.success(null);
            } else {
                result.notImplemented();
            }
        });
    }

这个是Android的Demo,接收Flutter发来的消息。和上一篇一样,看看是如何把2端的channel关联起来的。


// MethodChannel public void setMethodCallHandler(final @Nullable MethodCallHandler handler) { messenger.setMessageHandler(name, handler == null ? null : new IncomingMethodCallHandler(handler)); } // DartMessenage @Override public void setMessageHandler(@NonNull String channel, @Nullable BinaryMessenger.BinaryMessageHandler handler) { if (handler == null) { Log.v(TAG, "Removing handler for channel '" + channel + "'"); messageHandlers.remove(channel); } else { Log.v(TAG, "Setting handler for channel '" + channel + "'"); messageHandlers.put(channel, handler); } }

DartMessenage 把创建的handler和channel 保存在了Map中。 所以当收到消息后,通过BinaryMessenger.BinaryMessageHandler handler = messageHandlers.get(channel); 就能找到对应的handler进行处理。

依赖关系

Android向Flutter发送数据时,是在ServiceBinding初始化时建立的关系,但是没有找到ServiceBinding初始化的实际,只是推测是framework加载时。 而从Flutter向Android发数据时,是从c++层调用FlutterJni的方法, 而FlutterJNI在FlutterView创建时就有了。

而Dart的功能是基于FlutterView的,所以任意时间Flutter给Android发送消息,都是可以收到的,反过来就不一定,因为可能Dart那边channel还没有注册好。

Android返回结果

找到Channle对应的BinaryMessageHandler后,就交给我们自己的代码去处理了。

final ByteBuffer buffer = (message == null ? null : ByteBuffer.wrap(message));
handler.onMessage(buffer, new Reply(flutterJNI, replyId));

在Dart端使用async/await实现了异步操作,而Java端不支持只能使用回调了,所以这里创建了一个Reply对象。这个对象也是实现在DartMessenger中。

 private static class Reply implements BinaryMessenger.BinaryReply {
    @NonNull
    private final FlutterJNI flutterJNI;
    private final int replyId;
    private final AtomicBoolean done = new AtomicBoolean(false);

    Reply(@NonNull FlutterJNI flutterJNI, int replyId) {
      this.flutterJNI = flutterJNI;
      this.replyId = replyId;
    }

    @Override
    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());
      }
    }
  }

这里如果自定义的Channle处理完成后调用了reply接口,就会把replyId和data返回到Flutter那边。

void PlatformViewAndroid::InvokePlatformMessageResponseCallback(
    JNIEnv* env,
    jint response_id,
    jobject java_response_data,
    jint java_response_position) {
  if (!response_id)
    return;
  auto it = pending_responses_.find(response_id);
  if (it == pending_responses_.end())
    return;
  uint8_t* response_data =
      static_cast<uint8_t*>(env->GetDirectBufferAddress(java_response_data));
  std::vector<uint8_t> response = std::vector<uint8_t>(
      response_data, response_data + java_response_position);
  auto message_response = std::move(it->second);
  pending_responses_.erase(it);
  message_response->Complete(
      std::make_unique<fml::DataMapping>(std::move(response)));
}

这里根据response_id从pending_responses_中找到PlatformMessageResponseDart对象, 把数据转换为Dart数据,调用Dart层的PlatformMessageResponseCallback对象

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

这里的callback就是 _DefaultBinaryMessenger 中发送时的第三个参数

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

这里factory Completer() => new _AsyncCompleter<T>(); 最终异步执行return completer.future把结果返回给了调用方。


Platform Channle 通信流程图

点击查看大图


Channel和编解码

Flutter中系统定义了3种Channel,使用在不同的场景。这里以Java层的为例。

点击查看大图

这3个channel用途不同,所以发送数据时接收的参数也不同。但DartMessenger发送的ByteBuffer类型的数据。所以每个channle中有一个codec,作用是同ByteBuffer进行数据转换。系统会默认给channel一个对应的codec,我们在创建的channel的时候也可以自己来指定。

MethodChannel

前面的例子都是使用Methodchannel,Java层和Dart层的定义基本相同。它主要作用就行进行跨语言的方法调用。传递的数据是要调用的方法名参数。 所以它默认使用的时MethodCodec的子类StandardMethodCodec使用二进制传输, 还支持JSONMethodCodec使用Json字符串传输。

   @Override
    public ByteBuffer encodeMethodCall(MethodCall methodCall) {
        final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();
        messageCodec.writeValue(stream, methodCall.method);
        messageCodec.writeValue(stream, methodCall.arguments);
        final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
        buffer.put(stream.buffer(), 0, stream.size());
        return buffer;
    }

    @Override
    public MethodCall decodeMethodCall(ByteBuffer methodCall) {
        methodCall.order(ByteOrder.nativeOrder());
        final Object method = messageCodec.readValue(methodCall);
        final Object arguments = messageCodec.readValue(methodCall);
        if (method instanceof String && !methodCall.hasRemaining()) {
            return new MethodCall((String) method, arguments);
        }
        throw new IllegalArgumentException("Method call corrupted");
    }

主要的工作就是处理MethodCall对象和ByteBuffer之间的相互转换。 Dart层的定义也一样。然后对结果进行包装和解析。

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

在dart端,MethodChannel还提供了invokeListMethodinvokeMapMethod,可以一次调用多个Java方法。 同时还提供了一个子类OptionalMethodChannel。 因为当channel没有注册或不存在时,默认会抛出MissingPluginException异常,如果使用OptionalMethodChannel, 就会返回null而不抛出异常。

EventChannel

从UML图上看到,它内部也是默认使用StandardMethodCodec,但是和MethodChannle不同的是,它并没有定义一个发送的接口,只有一个setStreamHandler 用户接收Flutter发来的数据。EventChannel主要作用就是用于监听Java层的消息。

  new EventChannel(getFlutterView(), "com.test.native/event").setStreamHandler(new EventChannel.StreamHandler() {
            @Override
            public void onListen(Object arguments, EventChannel.EventSink events) {

            }

            @Override
            public void onCancel(Object arguments) {

            }
        });

使用很简单,StreamHandler有两个回调方法,一个监听一个取消。在看下Dart端的用法,也只提供了receiveBroadcastStream方法

const platform_event = const EventChannel('com.test.native/event');

  void _onEnvent(Object obj){

    }

    void _onError(Object obj){

    }

    platform_event.receiveBroadcastStream().listen(_onEnvent, onError: _onError);

在Java层中EventMethod没有发送接口,看起来两边都是接收,那谁来发送呢?具体看一下Dart层receiveBroadcastStream的代码:

这里使用了Stream流异步处理方式,Dart | 什么是Stream。对于一个Stream来说,经过输入–>处理–>输出这样一个流程。其中StreamController定义了处理的过程。 所以下面代码主要是构建了消息的处理过程,而上面的listen是用来监听Stream处理结果的。

 Stream<dynamic> receiveBroadcastStream([ dynamic arguments ]) {
    final MethodChannel methodChannel = MethodChannel(name, codec);  //创建了一个MethodChannle

     ////创建_AsyncBroadcastStreamController
    StreamController<dynamic> controller;
    controller = StreamController<dynamic>.broadcast(onListen: () async { 
        // Stream开始工作时,设置channle消息监听
      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); //调用java层 listen方法
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('while activating platform stream on channel name'),
        ));
      }
    }, onCancel: () async {

        //Stream停止工作时,取消channel消息监听
      defaultBinaryMessenger.setMessageHandler(name, null);      try {
        await methodChannel.invokeMethod<void>('cancel', arguments); //调用java层cancel方法
      } catch (exception, stack) {
        FlutterError.reportError(FlutterErrorDetails(
          exception: exception,
          stack: stack,
          library: 'services library',
          context: ErrorDescription('while de-activating platform stream on channelname'),
        ));
      }
    });
    return controller.stream;  //_ControllerStream
  }

上面的方法中创建了一个MethodChannel,可以调用Java层的listencancel的方法(对应Java层StreamHandler的两个回调函数)。Dart端使用了Stream来进行异步处理。

通过broadcast创建了一个_AsyncBroadcastStreamController对象,并且设置了onListen和onCancel事件的处理方法(注意这里onListen、onCancel是Stream处理的概念,和我们自己发送的数据无关)。我们调用Stream的listen开始监听时,会执行StreamController中的onListen方法。

// stream_impl.dart
StreamSubscription<T> listen(void onData(T data),
      {Function onError, void onDone(), bool cancelOnError}) {
    cancelOnError = identical(true, cancelOnError);
    StreamSubscription<T> subscription =
        _createSubscription(onData, onError, onDone, cancelOnError);
    _onListen(subscription);
    return subscription;
  }

所以抛开Stream,EventChannel工作流程如下:
1. Java层创建EventChannel并设置StreamHandler
2. Dart层创建EventChannel,通过MethodChannle调用Java层的listen方法
3. Java层收到listen方法调用,转换为StreamHandler的onListen方法
4. Java层把Dart层需要监听的数据,通过EventChannel.EventSink不断的发送给Dart,使用codec编码
5. Dart层收到数据后,通过codec解码,然后传递给listen中的onEvent方法
6. Dart层在onEvent方法中处理返回的数据

从上面可以看到,EventChannel是用于Dart监听Java的事件,比如电量、网络连接状态等等。本质还是数据传输。 如果要从Java层获取Dart的某个状态,我们可以根据这个自己实现一个Channel。

BasicMessageChannel

前面2个Channel主要用于方法调用和状态监听,而BasicMessageChannel主要用户数据传输。BasicMessageChannel没有指定默认的codec,而是需要自己指定MessageCodec

public interface MessageCodec<T> {
    /**
     * Encodes the specified message into binary.
     */
    @Nullable
    ByteBuffer encodeMessage(@Nullable T message);

    /**
     * Decodes the specified message from binary.
     */
    @Nullable
    T decodeMessage(@Nullable ByteBuffer message);
}

它下面4个codec实现都比较简单,这里就不用多介绍了。


系统Channel

前面介绍过,在FlutterView创建的时候,会创建系统使用的Channel。

       // Create all platform channels
        navigationChannel = new NavigationChannel(dartExecutor);
        keyEventChannel = new KeyEventChannel(dartExecutor);
        lifecycleChannel = new LifecycleChannel(dartExecutor);
        localizationChannel = new LocalizationChannel(dartExecutor);
        platformChannel = new PlatformChannel(dartExecutor);
        systemChannel = new SystemChannel(dartExecutor);
        settingsChannel = new SettingsChannel(dartExecutor);

这里看一下LifecycleChannel是用来通知Flutter当前APP的生命周期的。在FlutterView中对应生命周期函数中会调用(来自于FlutterActivityDelegate)

    public void onStart() {
        lifecycleChannel.appIsInactive();
    }

    public void onPause() {
        lifecycleChannel.appIsInactive();
    }

    public void onPostResume() {
        for (ActivityLifecycleListener listener : mActivityLifecycleListeners) {
            listener.onPostResume();
        }
        lifecycleChannel.appIsResumed();
    }

    public void onStop() {
        lifecycleChannel.appIsPaused();
    }

看一下它的实现

public class LifecycleChannel {

  @NonNull
  public final BasicMessageChannel<String> channel;

  public LifecycleChannel(@NonNull DartExecutor dartExecutor) {
    this.channel = new BasicMessageChannel<>(dartExecutor, "flutter/lifecycle", StringCodec.INSTANCE);
  }

  public void appIsInactive() {
    Log.v(TAG, "Sending AppLifecycleState.inactive message.");
    channel.send("AppLifecycleState.inactive");
  }

  public void appIsResumed() {
    Log.v(TAG, "Sending AppLifecycleState.resumed message.");
    channel.send("AppLifecycleState.resumed");
  }

  public void appIsPaused() {
    Log.v(TAG, "Sending AppLifecycleState.paused message.");
    channel.send("AppLifecycleState.paused");
  }

}

创建了一个BasicMessageChannel对象, 使用StringCodec来进行编码,通知是单向的。在Flutter中我们前面看到过,当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));
}

这个把消息交给了HandleLifecyclePlatformMessage进行处理

bool Engine::HandleLifecyclePlatformMessage(PlatformMessage* message) {
  const auto& data = message->data();
  std::string state(reinterpret_cast<const char*>(data.data()), data.size());
  if (state == "AppLifecycleState.paused" ||
      state == "AppLifecycleState.suspending") {
    activity_running_ = false;
    StopAnimator();
  } else if (state == "AppLifecycleState.resumed" ||
             state == "AppLifecycleState.inactive") {
    activity_running_ = true;
    StartAnimatorIfPossible();
  }

这里内部处理很简单,根据状态来控制动画。系统消息的处理明显简单不少,注意这里并没有使用codec去解码,因为在C++层直接处理掉了。 而且也不像自定义的Channel,还要找对应的Handler。



如果本文对您有帮助,可以扫描下方二维码打赏!您的支持是我的动力!
微信打赏 支付宝打赏

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注