惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
Intezer
S
Schneier on Security
AI
AI
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Hacker News
The Hacker News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
C
Cyber Attacks, Cyber Crime and Cyber Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
PCI Perspectives
PCI Perspectives
www.infosecurity-magazine.com
www.infosecurity-magazine.com
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
Forbes - Security
Forbes - Security
L
Lohrmann on Cybersecurity
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Help Net Security
Help Net Security
A
Arctic Wolf
T
Troy Hunt's Blog
Scott Helme
Scott Helme
T
The Exploit Database - CXSecurity.com
S
Security @ Cisco Blogs
T
Threatpost
The Cloudflare Blog
月光博客
月光博客
Cyberwarzone
Cyberwarzone
S
Security Affairs
M
MIT News - Artificial intelligence
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
人人都是产品经理
人人都是产品经理
Application and Cybersecurity Blog
Application and Cybersecurity Blog
AWS News Blog
AWS News Blog
The Register - Security
The Register - Security
H
Hacker News: Front Page
P
Privacy International News Feed
Cisco Talos Blog
Cisco Talos Blog
Know Your Adversary
Know Your Adversary
宝玉的分享
宝玉的分享
V
V2EX
F
Fortinet All Blogs
罗磊的独立博客
B
Blog RSS Feed
腾讯CDC
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - Franky
Security Latest
Security Latest

十月指南

使用AI开发一个应用最好的时间是现在 Kimi K2 发布并开源,实测效果确实很强! Vibe Coding实战:从翻车到上线,我开发了一款提前还贷计算器小程序 告别熬夜写代码!AI编程让我用Vibe Coding轻松上线 10 + 工具 用Vibe Coding方式打造 AI 内容创作平台 我连代码都没写,却做出JSON格式化工具—Vibe Coding了解一下 GPT 4.1、DeepSeek R1、Claude 3.7 代码生成横评,谁才是设计稿还原之王? AI 时代程序员生存指南 Cursor+Figma MCP 设计稿还原如此简单 MCP(模型上下文协议)是什么?它如何简化与API的AI集成 号外!我用Trae+Claude3.7开发的小程序上线啦 30分钟开发完成!Trae+Claude 3.7 打造小程序全流程揭秘! 5 分钟打造超酷小程序设计稿,Trae+Claude 3.7 太神了! 一句话生成设计稿,Trae+Claude 3.7 Sonnet,设计师也要被AI取代了 一句话生成应用!程序员效率翻倍的AI代码生成工具大盘点 我用AI创作了一首歌:Suno、海绵音乐、天工AI音乐生成效果测评 从哪吒2热潮到AI短片创作:AI视频实战测评 shadcn_ui:2024年最受欢迎的前端项目 实战:基于 Next.js+MoonShot API 开发一个 Github Trending 总结助手 技术篇:构建与部署 技术篇:Google 统计服务 技术篇:Next.js 多环境配置 技术篇:数据库-Prisma 技术篇:身份验证-NextAuth 技术篇:CSR、SSR、SSG、ISR 与客户端、服务端组件 技术篇:Next.js 路由与页面管理 技术篇:Next.js 项目结构 技术篇:Next.js 简介与概览 技术篇:从零开始掌握 Tailwind CSS 设计篇:独立开发设计入门 开篇:走上独立开发之路 Ollama:本地大模型运行指南 TooTool.net 理解 CSS 中的 Containing Block(包含块) 独立开发者工具大全(持续更新) TimeFlow 如何快速拥有一个技术博客 一个人从零开发上线一款App需要多少钱?终于有答案了! 深入理解HTTPS Nginx实战 Android SDK类产品开发总结 360插件化框架 RePlugin 之 ClassLoader Hook Mac下Charles抓包配置 十一大西北体验 利用ContentProvider实现同步Binder macOS修改Android模拟器Hosts 走过 登山记 期待成长 小雨 静夜思 关于
Flutter Platform Channel 使用与源码分析
十月 · 2019-08-21 · via 十月指南

1. 为什么要有 PlatformChannel #

1、如果 Flutter 要获取设备的电量信息怎么办?
2、如果 Flutter 要实时监控网络状态怎么办?

由于 Flutter 特点如下:

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.

1、Flutter 是一个跨平台的 UI 库,专注于构建高效的 UI。
2、多平台的支持,下图是 Flutter 目前支持的平台,每个平台的都有自己的平台特性。


基于以上两点,目前 Flutter 如果要和平台相关的部分通信需要有一个通道即 PlatformChannel。

2. 架构图 #

3. PlatformChannel 类型 #

  • BasicMessageChannel:用于数据传递。platform 和 dart 可互相传递数据(asynchronous message passing)
  • MethodChannel:用于传递方法调用。platform 和 dart 可互相调用方法(asynchronous method calls)
  • EventChannel:用于数据流通信。建立连接之后,platform 发送消息,dart 接收消息(event streams)

三种类型的 channel 都定义在 platform_channel.dart 中,从源码中可以看到三种 channel 都用到了以下三个属性。

  • name:String 类型,表示 channel 的名字,全局唯一(The logical channel on which communication happens)
  • codec:MessageCodec 类型,消息的编码解码器(The message codec used by this channel)
  • binaryMessenger:BinaryMessenger类型,用于发送数据(The messenger used by this channel to send platform messages)

3.1 channel name #

channel 的名字,每个 Flutter 应用可能有多个 channel,但是每个 channel 必须有一个唯一的名字。

3.2 codec #

codec 用来对数据编码解码,以便两端可以正确读取数据。

3.3 binaryMessenger #

用于发送数据

4. PlatformChannel 使用 #

4.1 MethodChannel #

  • Dart 调用 Android 方法

method_channel_page.dart 主要代码

第一步
static const methodChannel = MethodChannel("method_channel_sample");

第二步  
Future<dynamic> getUserInfo(String method, {String userName}) async {
  return await methodChannel.invokeMethod(method, userName);
}

第三步    
MaterialButton(
  color: Colors.blue,
  textColor: Colors.white,
  child: new Text('获取 snow 用户信息'),
  onPressed: () {
    getUserInfo("getInfo", userName: "snow")
      ..then((result) {
        setState(() {
          messageFromNative = result;
        });
      });
  },
),

MainActivity.java 主要代码

private void addMethodChannel() {
    mMethodChannel = new MethodChannel(getFlutterView(), "method_channel_sample");
    mMethodChannel.setMethodCallHandler((methodCall, result) -> {

        String method = methodCall.method;

        if ("getInfo".equals(method)) {

            String userName = (String) methodCall.arguments;

            if (userName.equals("rocx")) {
                String user = "name:rocx, age:18";
                result.success(user);
            } else {
                result.success("user not found");

                invokeSayHelloMethod();
            }
        }

    });
}

从以上代码可以看出
Dart 调用 Android 代码分三步。首先在 Dart 端定义 MethodChannel 名字为 method_channel_sample。然后定义getUserInfo方法,传入要调用的方法名和参数。最后点击按钮执行方法,获取用户信息。
在 Android 端定一个 MethodChannel 名字和 Dart 端保持一致。设置 MethodCallHandler。当调用的是getInfo方法时,根据参数返回信息。

  • Android 调用 Dart 方法

MainActivity.java 主要代码

private void invokeSayHelloMethod() {
    mMethodChannel.invokeMethod("sayHello", "", new MethodChannel.Result() {
        @Override
        public void success(Object o) {

            Toast.makeText(MainActivity.this, o.toString(), Toast.LENGTH_LONG).show();
        }

        @Override
        public void error(String s, String s1, Object o) {

        }

        @Override
        public void notImplemented() {

        }
    });
}

method_channel_page.dart 主要代码

Future<dynamic> addHandler(MethodCall call) async {
  switch (call.method) {
    case "sayHello":
      return "Hello from Flutter";
      break;
  }
}

@override
void initState() {
  super.initState();

  methodChannel.setMethodCallHandler(addHandler);
}

从代码可以看出,在 Dart 端设置 MethodCallHandler 然后在 Android 端调用即可。

4.2 BasicMessageChannel #

  • Dart 向 Android 发送消息

basic_message_channel_page.dart 主要代码

第一步
static const basicMessageChannel = BasicMessageChannel(
      "basic_message_channel_sample", StandardMessageCodec());

第二步
Future<dynamic> sayHelloToNative(String message) async {
  String reply = await basicMessageChannel.send(message);

  setState(() {
    msgReplyFromNative = reply;
  });

  return reply;
}

第三步
MaterialButton(
  color: Colors.blue,
  textColor: Colors.white,
  child: new Text('say hello to native'),
  onPressed: () {
    sayHelloToNative("hello");
  },
),

MainActivity.java 主要代码

private void addBasicMessageChannel() {
    mBasicMessageChannel = new BasicMessageChannel<>(getFlutterView(), "basic_message_channel_sample", StandardMessageCodec.INSTANCE);
    mBasicMessageChannel.setMessageHandler((object, reply) -> {

        reply.reply("receive " + object.toString() + " from flutter");

        mBasicMessageChannel.send("native say hello to flutter");
    });
}

从以上代码可以看出
Dart 向 Android 发送消息依然分为三步。首先在 Dart 端定义 BasicMessageChannel 名字为 basic_message_channel_sample。然后定义发送消息的方法sayHelloToNative。最后点击按钮向 Android 端发送消息。
在 Android 端定一个 BasicMessageChannel 名字和 Dart 端保持一致。设置 MethodCallHandler。当收到消息时发一个回复。

  • Android 向 Dart 发送消息

MainActivity.java 主要代码

mBasicMessageChannel.send("native say hello to flutter");

basic_message_channel_page.dart 主要代码

Future<dynamic> addHandler(Object result) async {
  setState(() {
    msgReceiveFromNative = result.toString();
  });
}

void addMessageListener() {
  basicMessageChannel.setMessageHandler(addHandler);
}

@override
void initState() {
  super.initState();
  addMessageListener();
}

从代码可以看出,在 Dart 端设置 MessageHandler 然后在 Android 端直接发送消息即可。

4.3 EventChannel #

event_channel_page.dart 主要代码

第一步
static const eventChannel = EventChannel("event_channel_sample");

void _onEvent(Object event) {
  setState(() {
    if (_streamSubscription != null) {
      eventMessage = event.toString();
    }
  });
}

void _onError(Object error) {
  setState(() {
    if (_streamSubscription != null) {
      eventMessage = "error";
    }
  });
}

@override
void initState() {
  super.initState();
  eventMessage = "";
  第二步
  _streamSubscription = eventChannel
      .receiveBroadcastStream()
      .listen(_onEvent, onError: _onError);
}

MainActivity.java 主要代码

private void addEventChannel() {
    mEventChannel = new EventChannel(getFlutterView(), "event_channel_sample");
    mEventChannel.setStreamHandler(new EventChannel.StreamHandler() {
        @Override
        public void onListen(Object o, EventChannel.EventSink eventSink) {

            task = new TimerTask() {
                @Override
                public void run() {
                    runOnUiThread(() -> eventSink.success("i miss you " + System.currentTimeMillis()));

                }
            };
            timer = new Timer();
            timer.schedule(task, 2000, 3000);
        }

        @Override
        public void onCancel(Object o) {
            task.cancel();
            timer.cancel();
            task = null;
            timer = null;
        }
    });
}

Dart 接受 Android stream event。首先在 Dart 端定义 EventChannel 名字为 event_channel_sample。然后设置receiveBroadcastStream监听,当 Android 端有消息发过来会回调_onEvent方法。
在 Android 端启动一个定时器,每隔3s向 Dart 端发送一次消息。

4.4 总结 #

如下图,在 Dart 与 Platform 通信过程中,通过 channel name 找到对方,然后把消息通过 codec 进行编解码,最后通过 binaryMessenger 进行发送。

5. 源码分析-以 MethodChannel 为例 #

5.1 调用 MethodChannel 的 invokeMethod 方法,会调用到 binaryMessenger.send 方法。即 binaryMessenger.send 传入 channel name 和编码好的参数。 #
  @optionalTypeArgs
  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;
  }
5.2 binary_messenger.dart 的 send 方法会调用当前对象的 _sendPlatformMessage 方法,最终会调用 window.sendPlatformMessage 方法。 #
  @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;
  }
5.3 在 window.dart 中又调用了 native 方法 _sendPlatformMessage。 #
  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';
5.4 接下来进入 engine 中的 window.cc,可以看到最终调用的是 dart_state->window()->client()->HandlePlatformMessage。 #
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},
      {"Window_setNeedsReportTimings", SetNeedsReportTimings, 2, true},
  });
}
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();
}

window.cc 源码

5.5 我们进入 window.h 中找到 client 其实是 WindowClient。 #
  WindowClient* client() const { return client_; }

window.h 源码

5.6 在 runtime_controller.h 中可以看到 RuntimeController 是 WindowClient 的实际实现,调用的是 RuntimeController 的 HandlePlatformMessage 方法。 #
class RuntimeController final : public WindowClient {

...
  // |WindowClient|
  void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
...
}

runtime_controller.h 源码

5.7 在 runtime_controller.cc 中,HandlePlatformMessage 调用了 client_ 的 HandlePlatformMessage 方法,client_ 实际是代理对象 RuntimeDelegate。 #
void RuntimeController::HandlePlatformMessage(
    fml::RefPtr<PlatformMessage> message) {
  client_.HandlePlatformMessage(std::move(message));
}
RuntimeDelegate& p_client

runtime_controller.cc 源码

5.8 engine.h 是 RuntimeDelegate 的具体实现类。 #
class Engine final : public RuntimeDelegate {
...
  // |RuntimeDelegate|
  void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
...  
}  

engine.h 源码

5.9 engine.cc 中调用了 delegate_ 的 OnEngineHandlePlatformMessage 方法。 #
void Engine::HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) {
  if (message->channel() == kAssetChannel) {
    HandleAssetPlatformMessage(std::move(message));
  } else {
    delegate_.OnEngineHandlePlatformMessage(std::move(message));
  }
}

engine.cc 源码

5.10 shell.h 是 Engine 的代理。 #
  // |Engine::Delegate|
  void OnEngineHandlePlatformMessage(
      fml::RefPtr<PlatformMessage> message) override;

shell.h 源码

5.11 调用流程又进入了 shell.cc 的 HandleEngineSkiaMessage 方法,把消费放到 TaskRunner 中。 #
// |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.cc 源码

5.12 当 task 执行是会调用 platform_view_android.h 的 HandlePlatformMessage 方法。 #
class PlatformViewAndroid final : public PlatformView {
...
  // |PlatformView|
  void HandlePlatformMessage(
      fml::RefPtr<flutter::PlatformMessage> message) override;
...
}

platform_view_android.h 源码

5.13 在 platform_view_android.cc 的 HandlePlatformMessage 中,开始通过 jni 调用 java 端的方法,java_channel 即要找的 channel。 #
// |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);
  }
}

platform_view_android.cc 源码

5.14 在 platform_view_android_jni.cc 中可以看到 g_handle_platform_message_method 就是 FindClass(“io/flutter/embedding/engine/FlutterJNI”) 类的 handlePlatformMessage 方法。至此 engine 代码执行结束。 #
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));
}
  g_handle_platform_message_method =
      env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage",
                       "(Ljava/lang/String;[BI)V");
  g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
      env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
  if (g_flutter_jni_class->is_null()) {
    FML_LOG(ERROR) << "Failed to find FlutterJNI Class.";
    return false;
  }

platform_view_android_jni.cc 源码

5.15 在 FlutterJNI 中调用了 this.platformMessageHandler.handleMessageFromDart 方法。也就是 DartMessenger 的 handleMessageFromDart 方法。 #
    private void handlePlatformMessage(@NonNull String channel, byte[] message, int replyId) {
        if (this.platformMessageHandler != null) {
            this.platformMessageHandler.handleMessageFromDart(channel, message, replyId);
        }

    }
5.16 DartMessenger 中 messageHandlers 通过 channel 名找到对应的 handler 进行处理,这个 handler 就是我们在 java 代码里通过 channel 设置的,整个调用流程完成。 #
    public void handleMessageFromDart(@NonNull String channel, @Nullable byte[] message, int replyId) {
        Log.v("DartMessenger", "Received message from Dart over channel '" + channel + "'");
        BinaryMessageHandler handler = (BinaryMessageHandler)this.messageHandlers.get(channel);
        if (handler != null) {
            try {
                Log.v("DartMessenger", "Deferring to registered handler to process message.");
                ByteBuffer buffer = message == null ? null : ByteBuffer.wrap(message);
                handler.onMessage(buffer, new DartMessenger.Reply(this.flutterJNI, replyId));
            } catch (Exception var6) {
                Log.e("DartMessenger", "Uncaught exception in binary message listener", var6);
                this.flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
            }
        } else {
            Log.v("DartMessenger", "No registered handler for message. Responding to Dart with empty reply message.");
            this.flutterJNI.invokePlatformMessageEmptyResponseCallback(replyId);
        }

    }

Demo 地址 #

flutter_platform_channel 使用

参考资源 #

Writing custom platform-specific code
platform channel 官方示例
深入理解Flutter Platform Channel