MethodChannel 原理之 Dart -> Native

StandardMethodCodec

public class StandardMessageCodec implements MessageCodec<Object> {
    public static final StandardMessageCodec INSTANCE = new StandardMessageCodec();
    
    // 根据数据类型,先向stream中写入类型标志值,及上述提到的14个常量值,然后将具体的
    // value值转成byte继续写入到stream
    protected void writeValue(ByteArrayOutputStream stream, Object value) {
        if (value == null) {
            stream.write(NULL);
        } else if (value == Boolean.TRUE) {
            stream.write(TRUE);
        } else if (value == Boolean.FALSE) {
            stream.write(FALSE);
        } else if (value instanceof Number) {
            if (value instanceof Integer || value instanceof Short || value instanceof Byte) {         // 1.写入类型标志值
                stream.write(INT);
                // value转为byte,继续写入到stream中
                writeInt(stream, ((Number) value).intValue());
            }
            .......
        }else if (value instanceof String) {
            stream.write(STRING);
            writeBytes(stream, ((String) value).getBytes(UTF8));
        }
        .......
    }

    // writeValue()方法反向过程,原理一致
    protected final Object readValue(ByteBuffer buffer) {
        .......
    }
}

在StandardMessageCodec中最重要的两个方法是writeValue()readValue().前者用于将value值写入到字节输出流ByteArrayOutputStream中,后者从字节缓冲数组中读取.在Android返回电量的过程中,假设电量值为100,该值转换成二进制数据流程为:首先向字节流stream中写入表示int类型的标志值3,再将100转为4个byte,继续写入到字节流stream中.当Dart中接受到该二进制数据后,先读取第一个byte值,根据此值得知后面需要读取一个int类型的数据,随后读取后面4个byte,并将其转为dart类型中int类型.

图示

Handler

Flutter中定义了一套Handler用于处理经过Codec解码后消息.在使用Platform Channel时,需要为其设置对应的Handler,实际上就是为其注册一个对应BinaryMessageHandler,二进制数据会被BinaryMessageHanler进行处理,首先使用Codec进行解码操作,然后再分发给具体Handler进行处理.与三种Platform Channel相对应,Flutter中也定义了三种Handler:

  • MessageHandler: 用于处理字符串或者半结构化消息,定义在BasicMessageChannel中.
public final class BasicMessageChannel<T> {
    ......
        
    public interface MessageHandler<T> {
        // onMessage()用于处理来自Flutter中的消息,
        // 该接受两个参数:T类型的消息以及用于异步返回T类型的result
        void onMessage(T message, Reply<T> reply);
    }
    
    ......
}
  • MethodCallHandler: 用于处理方法调用,定义在MethodChannel中.
  • StreamHandler: 用于事件流通信,定义在EventChannel中.

MethodChannel调用原理

Dart -> Native

class MethodChannel {
  // 构造方法,通常我们只需要指定该channel的name,  
  const MethodChannel(this.name, [this.codec = const StandardMethodCodec()]);
  // name作为通道的唯一标志符,用于区分不同的通道调用
  final String name;
  // 用于方法调用过程的编码 
  final MethodCodec codec;
  
  // 用于发起异步平台方法调用,需要指定方法名,以及可选方法参数  
  Future<dynamic> invokeMethod(String method, [dynamic arguments]) async {
    assert(method != null);
    // 将一次方法调用中需要的方法名和方法参数封装为MethodCall对象,然后使用MethodCodec对该
    //  对象进行进行编码操作,最后通过BinaryMessages中的send方法发起调用 
    final dynamic result = await BinaryMessages.send(
      name,
      codec.encodeMethodCall(MethodCall(method, arguments)),
    );
    if (result == null)
      throw MissingPluginException('No implementation found for method $method on channel $name');
    return codec.decodeEnvelope(result);
  }
}

final MethodChannel _channel = new MethodChannel('flutter.io/player')

Channel名称作为MethodChannel的唯一标识符,用于区分不同的MethodChannel对象.
拿到MethodChannel对象后,通过调用其invokeMethod()方法用于向平台发起一次调用.在invokeMethod()方法中会将一次方法调中的方法名method和方法参数arguments封装为MethodCall对象,然后使用MethodCodec对其进行二进制编码,最后通过BinaryMessages.send()发起平台方法调用请求.

// BinaryMessages类中提供了用于发送和接受平台插件的二进制消息.
class BinaryMessages {
   ...... 
   static Future<ByteData> send(String channel, ByteData message) {
    final _MessageHandler handler = _mockHandlers[channel];
    // 在没有设置Mock Handler的情况下,继续调用_sendPlatformMessage()   
    if (handler != null)
      return handler(message);
    return _sendPlatformMessage(channel, message);
  } 
 
   static Future<ByteData> _sendPlatformMessage(String channel, ByteData message) {
    final Completer<ByteData> completer = Completer<ByteData>();   
    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: 'during a platform message response callback',
        ));
      }
    });
    return completer.future;
  } 
   ......  
}
class Window{
    ......
    void sendPlatformMessage(String name,
                           ByteData data,
                           PlatformMessageResponseCallback callback) {
    final String error =
        _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
    if (error != null)
      throw new Exception(error);
  }
  // 和Java类似,Dart中同样提供了Native方法用于调用底层C++/C代码的能力  
  String _sendPlatformMessage(String name,
                              PlatformMessageResponseCallback callback,
                              ByteData data) native 'Window_sendPlatformMessage';
   ....... 
}

上述过程最终会调用到ui.Window._sendPlatformMessage()方法,该方法是一个native方法,这与Java中JNI技术非常类似.

调用过程

在调用该Native方法中,我们向native层发送了三个参数:

  • name: String类型,代表Channel名称
  • data: ByteData类型,代表之前封装的二进制数据
  • callback: Function类型,用于结果回调

_sendPlatformMessage()具体实现在 Window.cc 中:

void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
  natives->Register({
      {"Window_defaultRouteName", DefaultRouteName, 1, true},
      {"Window_scheduleFrame", ScheduleFrame, 1, true},
      {"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
      {"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
      {"Window_render", Render, 2, true},
      {"Window_updateSemantics", UpdateSemantics, 2, true},
      {"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
      {"Window_reportUnhandledException", ReportUnhandledException, 2, true},
  });
}
void _SendPlatformMessage(Dart_NativeArguments args) {
  // 最终调用SendPlatformMessage函数  
  tonic::DartCallStatic(&SendPlatformMessage, args);
}
Dart_Handle SendPlatformMessage(Dart_Handle window,
                                const std::string& name,
                                Dart_Handle callback,
                                const tonic::DartByteData& data) {
  UIDartState* dart_state = UIDartState::Current();
  // 1.只能在main iolate调用平台方法
  if (!dart_state->window()) {
    // Must release the TypedData buffer before allocating other Dart objects.
    data.Release();
    return tonic::ToDart(
        "Platform messages can only be sent from the main isolate");
  }
  // 此处response的作用? 
  fml::RefPtr<PlatformMessageResponse> response;
  if (!Dart_IsNull(callback)) {
    response = fml::MakeRefCounted<PlatformMessageResponseDart>(
        tonic::DartPersistentValue(dart_state, callback),
        dart_state->GetTaskRunners().GetUITaskRunner());
  }
  // 2.核心方法调用 
  if (Dart_IsNull(data.dart_handle())) {
    dart_state->window()->client()->HandlePlatformMessage(
        fml::MakeRefCounted<PlatformMessage>(name, response));
  } else {
    const uint8_t* buffer = static_cast<const uint8_t*>(data.data());
        
    dart_state->window()->client()->HandlePlatformMessage(
        fml::MakeRefCounted<PlatformMessage>(
            name, std::vector<uint8_t>(buffer, buffer + data.length_in_bytes()),
            response));
  }

  return Dart_Null();
}

HandlePlatformMessage()的实现类在RuntimeController

class RuntimeController final : public WindowClient {
    ......
        
    private:
      RuntimeDelegate& client_;
    
      .......
      void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
      ......

}
// runtime_controller.cc
void RuntimeController::HandlePlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  client_.HandlePlatformMessage(std::move(message));
}

在运行过程中,不同的平台有运行机制不同,需要不同的处理策略,因此RuntimeController中相关的方法实现都被委托到了不同的平台实现类RuntimeDelegate中,即上述代码中client_,定义如下:

class Engine final : public blink::RuntimeDelegate {
    ........
}

// engine.cc
void Engine::HandlePlatformMessage(
    fml::RefPtr<blink::PlatformMessage> message) {
  // kAssetChannel值为flutter/assets  
  if (message->channel() == kAssetChannel) {
    HandleAssetPlatformMessage(std::move(message));
  } else {
    delegate_.OnEngineHandlePlatformMessage(std::move(message));
  }
}
void Shell::OnEngineHandlePlatformMessage(
    fml::RefPtr<blink::PlatformMessage> message) {
  FML_DCHECK(is_setup_);
  FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());

  // kSkiaChannel值为flutter/skia  
  if (message->channel() == kSkiaChannel) {
    HandleEngineSkiaMessage(std::move(message));
    return;
  }
  // 其他情况下,向PlatformTaskRunner中添加Task
  task_runners_.GetPlatformTaskRunner()->PostTask(
      [view = platform_view_->GetWeakPtr(), message = std::move(message)]() {
        if (view) {
          view->HandlePlatformMessage(std::move(message));
        }
      });
}

Engine在处理message时,如果该message值等于kAssetChannel,即flutter/assets,表示当前操作想要获取资源,因此会调用HandleAssetPlatformMessage()来走获取资源的逻辑;否则调用delegate_.OnEngineHandlePlatformMessage()方法.
OnEngineHandlePlatformMessage在接收到消息后,首先判断要调用Channel是否是flutter/skia,如果是则调用HandleEngineSkiaMessage()进行处理后返回,否则向PlatformTaskRunner添加一个Task,在该Task中会调用PlatformView的HandlePlatformMessage()方法.根据运行平台不同PlatformView有不同的实现,对于Android平台而言,其具体实现是PlatformViewAndroid;对于IOS平台而言,其实现是PlatformViewIOS.

void PlatformViewAndroid::HandlePlatformMessage(
    fml::RefPtr<blink::PlatformMessage> message) {
  JNIEnv* env = fml::jni::AttachCurrentThread();
  fml::jni::ScopedJavaLocalRef<jobject> view = java_object_.get(env);
  if (view.is_null())
    return;
  // response_id在Flutter调用平台代码时,会传到平台代码中,后续平台代码需要回传数据时
  // 需要用到它
  int response_id = 0;
  // 如果message中有response(response类型为PlatformMessageResponseDart),则需要对
  // response_id进行自增  
  if (auto response = message->response()) {
    response_id = next_response_id_++;
    // pending_responses是一个Map结构  
    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类型的消息时,如果消息中有response,则对response_id自增,并以response_id为key,response为value存放在变量pending_responses_中.
接着将消息中的channel和data数据转成Java可识别的数据,并连同response_id一同作为FlutterViewHandlePlatformMessage()方法的参数,最终通过JNI调用的方式传递到Java层.

// platform_android_jni.cc
static jmethodID g_handle_platform_message_method = nullptr;
void FlutterViewHandlePlatformMessage(JNIEnv* env,
                                      jobject obj,
                                      jstring channel,
                                      jobject message,
                                      jint responseId) {
  // g_handle_platform_message_method中指向Java层的方法
  // 其在 RegisterApi 中被初始化
  env->CallVoidMethod(obj, g_handle_platform_message_method, channel, message,
                      responseId);
  FML_CHECK(CheckException(env));
}
bool PlatformViewAndroid::Register(JNIEnv* env) {
  ......  
  g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
      env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
    
  ......
      
  return RegisterApi(env);
}

bool RegisterApi(JNIEnv* env) {
  ......  
  g_handle_platform_message_method =
      env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
                       "(Ljava/lang/String;[BI)V");
  ......  
}

不难看出g_flutter_jni_class指向FlutterJNI.java类,g_handle_platform_message_method指向FlutterJN.javaI中的handlePlatformMessage()方法.

image

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345