通过此篇文章,你将了解到:

  1. Flutter插件的基本介绍;
  2. windows插件开发的真实踩坑经验。

⚠️本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!

前言

我们都知道,Flutter的定位更多是作为一个跨平台的UI框架,对于原生平台的功能,开发过程中经常需要插件来提供。不幸的是Windows的生态又极其不完整,插件开发必不可少。但网上windows的文章少之又少,所以本篇文章,我们一起来聊聊插件开发的一些技巧。

插件介绍

Flutter的插件主要分两种:package和plugin。

  • Package是纯dart代码的库,不涉及原生平台的代码;
  • Plugin是原生插件库,是一种特殊的Package。Plugin需要开发者分别在各原生平台实现对应的能力。

其中Plugin是我们要着重讲的,既然是原生平台实现,那跟dart层就势必需要通讯。Flutter Plugin的通讯主要有:methodChannel、eventChannel、basicMessageChannel。

  • MethodChannel:同步调用的通道,调用后可以通过result返回结果。可以 Native 端主动调用,也可以Flutter主动调用,属于双向通信。这种通信方式是我们日常开发中为最常用的方式, 关键点是Native 端的调用需要在主线程中执行
  • EventChannel:异步事件通知的通道,一般是Native端主动发出通知,Flutter接收通信信息。
  • BasicMessageChannel:长链接的通道,双端可以随时发出消息,对方收到消息后可以使用reply进行回复。一般常用于需要双向通信可不知道何时需要发送的场景。

windows插件编写

Flutter Android的生态算是比较完整的,而且网上95%的插件文章,都是以移动端为主,对于不熟悉Windows开发的同学极度不友好。因此本篇文章我们不讲Android端的实现,重点讲Windows端的实践,不过我也不是C++技术栈的,只能浅浅分享我踩过的坑。

  1. 如何创建通信通道?
// MethodChannel
void XXXPlugin::RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar) {// 创建一个MethodChannelauto channel =std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(registrar->messenger(), "usb_tool",&flutter::StandardMethodCodec::GetInstance());// 创建插件对象auto plugin = std::make_unique<XXXPlugin>();// 把通道设置给插件,同时传入消息的处理入口channel->SetMethodCallHandler([plugin_pointer = plugin.get()](const auto& call, auto result) {plugin_pointer->HandleMethodCall(call, std::move(result));});
}
// EventChannel// 创建事件流处理对象
auto eventHandler = std::make_unique<
StreamHandlerFunctions<EncodableValue>>([plugin_pointer = plugin.get()](const EncodableValue* arguments,std::unique_ptr<EventSink<EncodableValue>>&& events)-> std::unique_ptr<StreamHandlerError<EncodableValue>> {return plugin_pointer->OnListen(arguments, std::move(events));},[plugin_pointer = plugin.get()](const EncodableValue* arguments)-> std::unique_ptr<StreamHandlerError<EncodableValue>> {return plugin_pointer->OnCancel(arguments);});
// 创建EventChannel对象
auto eventChannel = std::make_unique<flutter::EventChannel<flutter::EncodableValue>>(registrar->messenger(), eventChannelName,&flutter::StandardMethodCodec::GetInstance());
// 把通道设置给插件
eventChannel->SetStreamHandler(std::move(eventHandler));

最后我们还需要把插件注册进项目中

registrar->AddPlugin(std::move(plugin));
  1. 如何处理消息? 在上面创建的过程中,其实已经把处理方法的传递给插件了。
// MethodChannel的处理
// result即通信的对象
void XXXPlugin::HandleMethodCall(const flutter::MethodCall<flutter::EncodableValue>& method_call,std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {// 匹配通信的接口if (method_call.method_name().compare("getPlatformVersion") == 0) {std::ostringstream version_stream;version_stream << "Windows ";if (IsWindows10OrGreater()) {version_stream << "10+";}else if (IsWindows8OrGreater()) {version_stream << "8";}else if (IsWindows7OrGreater()) {version_stream << "7";}// 通过result->Succes回复消息result->Success(flutter::EncodableValue(version_stream.str()));} else {result->NotImplemented();}
}
// 主动向Flutter端发送消息
std::unique_ptr < flutter::StreamHandlerError<flutter::EncodableValue>> XXXPlugin::OnListen(const flutter::EncodableValue* arguments,std::unique_ptr<flutter::EventSink<flutter::EncodableValue>>&& events) {// 主动发送events_.reset(events.release());return nullptr;
}// Flutter取消监听时触发
std::unique_ptr < flutter::StreamHandlerError<flutter::EncodableValue>> UsbToolPlugin::OnCancel(const flutter::EncodableValue* arguments) {return nullptr;
}

BasicMessageChannel我暂时还没有用过,这里就不做记录了。但是看C++的api,还是很简单就能找到的。至于Flutter端的,无需多言。只要通信层连通了,其他想怎么玩都可以。

Windows插件的一些坑

这是本篇文章的重点。我们都知道Flutter是单线程的机制,来到原生平台也一样,Platform是运行在Flutter的主线程的,自然是不能做任何耗时的,不然会卡住主线程,系统会把我们认为无响应的应用,从而杀死应用。

我们经常会在使用windows插件时,感觉点击卡顿,其实就是很多插件没有做这个处理,导致事件队列等待调度。这主要是因为在windows的开发习惯上,耗时操作会丢到子线程异步执行,然后主线程如何等待执行结果?使用while一直去查询是否执行完成,这在windows上成为挂起。

不过一个有趣的现象是:当有耗时操作的时候,Flutter的动画是可以流程播放的,但是点击事件却卡住了,这时候C++的同学就会扯,你看动画都是流程的,问题肯定出在Flutter上?其实是因为动画在Flutter中属于微任务,它的优先级是高于事件队列的。而while也是分配到事件队列中,所以动画优先执行,点击却需要一直等到while结束。

在Android中,为了避免这个问题,我们一般会使用协程,把耗时操作丢给协程,让系统帮我们进行任务调度,通过await拿到执行完之后的结果,再把结果返回给dart层。整个机制其实还是保留了flutter的单线程机制,从而避免了卡顿问题。

在Windows端,其实也有协程这个概念,比如WinRT、C++都有提供协程的能力。但问题在于协程这个东西,对于C++来说太新了,同时C++的历史包袱实在太重,到现在还是用着很老版本的库。这就导致很多C++的库没办法迁移到协程这种方式,至少在我现在的业务中,切换成本极高,几乎没办法完成。

但问题总得解决,目前我们主要使用异步通知的方式,来解决这个问题。此异步是真异步,非flutter单线程任务调度的异步。我们会把耗时的操作丢给子线程,但是我们不再通过while进行异步转同步,而是在子线程中,主动通过channel去通知会Dart层。

if (*method == "getAsync") {async_pipe_stream_->Get(request, std::bind(&XXXPlugin::OnResponse, this, std::placeholders::_1, *uuid));// 直接返回true,但真正的执行结果再OnResponse中主动返回result->Success(EncodableValue(true)); return;}

在插件的dart代码中,我们需要主动创建一个MethodChannel的接收器,异步接收到后,通过执行业务端传入的回调通知回去。

class NativePlugin {static const MethodChannel _channel =MethodChannel('com.open.flutter/xxx/xxx');static NativePlugin? _instance;// 获取实例,单例static NativePlugin getInstance({String defaultToken = _token}) {_instance ??= NativePlugin._internal(defaultToken);return _instance!;}// 私有命名构造函数,做一次初始化NativePlugin._internal(String defaultToken) {_defaultToken = defaultToken;_channel.setMethodCallHandler((MethodCall call) async {if (call.method == 'onResponse') {final arguments = Map<String, dynamic>.from(call.arguments);// 执行业务端传入的回调await _onResponse(arguments);}});}

插件的Flutter层需要接收/维护回调列表,不过此方式有隐患,传入的回调容易造成闭包问题,增加一些内存泄露的风险;但是对于没办法使用协程的C++插件来说,此方案确实可以解决不少问题。亲测可用的!

写在最后

这篇文章,适合熟悉Flutter插件开发,但是想接触C++的同学学习讨论。
此专栏从窗口管理、分辨率适配、桌面小工具、项目框架、插件编写;下次我们讲讲如何进行打包!

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

二、源码解析合集


三、开源框架合集


欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

Flutter桌面开发 - windows插件开发相关推荐

  1. Flutter桌面开发 — Windows App打包以及使用Inno Setup生成.exe文件安装包

    文章目录 1 打包 Flutter Windows App 1.1 开发环境准备 1.2 支持Windows 1.3 构建Windows App 2 使用Inno Setup生成.exe文件安装包 2 ...

  2. Flutter 桌面开发 | 键盘快捷键功能 - Shortcuts 组件

    theme: cyanosis 在桌面端的开发中,键盘快捷键是非常常见而必要的,比如 Ctrl + F 搜索, Ctrl + C 复制等.Flutter 既然可以开发桌面端应用,那必然要提供自定义快捷 ...

  3. Flutter教程之Windows桌面应用程序开发

    Flutter教程之Windows桌面应用程序开发 一.前言 二.环境安装 三.创建项目 四.已有项目添加其他平台支持 一.前言 Flutter 是 Google 开源的 UI 工具包,帮助开发者通过 ...

  4. Google 要用 Flutter 一统移动、桌面开发江湖?

    "Flutter 的核心是一个独立的可执行二进制文件,所以它不仅能改变移动开发的世界,也能改变桌面开发的世界.你只需编写一次代码,就可以在 Android.iOS.Windows.Mac 和 ...

  5. 开发Windows Mobile今日插件 -- 内存电量,桌面便笺,桌面记单词

    本篇文章讲解的是开发 Windows Mobile 上的今日插件.关于是今日插件,在 PPC 或者 SP SDK 的帮助文档中有相关的章节介绍,在网络上也有一些帖子和资源讲解.在这里简要回顾一下.今日 ...

  6. 开发Windows Mobile今日插件 -- 内存电量,桌面便笺,桌面记单词(转自hoodlum1980 ( 發發 ) 的技术博客)

    本篇文章讲解的是开发 Windows Mobile 上的今日插件.关于是今日插件,在 PPC 或者 SP SDK 的帮助文档中有相关的章节介绍,在网络上也有一些帖子和资源讲解.在这里简要回顾一下.今日 ...

  7. Flutter桌面小工具 -- 灵动岛【Windows+Android版本】

    通过此篇文章,你将了解到: Flutter动画实现灵动岛: Flutter如何开发一个置顶可自由拖拽的小工具: 分享一些关于灵动岛的想法. ⚠️本文为稀土掘金技术社区首发签约文章,14天内禁止转载,1 ...

  8. 【Visual Studio】Visual Studio 2019 创建 Windows 控制台程序 ( 安装 ‘使用 C++ 的桌面开发‘ 组件 | 创建并运行 Windows 控制台程序 )

    文章目录 一.安装 C++ 桌面开发组件 二.创建并运行 Windows 控制台程序 一.安装 C++ 桌面开发组件 打开 Visual Studio Installer , 点击 " 修改 ...

  9. Go实战--使用golang开发Windows Gui桌面程序(lxn/walk)

    生命不止,继续 go go go!!! golang官方并没有提供Windows gui库,但是今天还是要跟大家分享一下使用golang开发Windows桌面程序,当然又是面向github编程了. 知 ...

最新文章

  1. ubuntu14 安装JDK
  2. 活动目录之用户配置文件(转载)
  3. 解决 “OperationalError: (sqlite3.OperationalError) no such table: ...“问题
  4. 1.嵌入式系统的简介
  5. Nginx的rewrite案例之防盗链
  6. CentOS 7 Flannel的安装与配置
  7. 震惊!Fibonacci Again
  8. 如何优雅地本地化构建Mybatis源码
  9. 虚拟环境virtualenv
  10. [expimp]exp导出笔记
  11. [转载] 十种方式拼接Python字符串
  12. 怎么设置某个用户生成hdfs文件的权限_管理 HDFS 服务
  13. 项目日报模板_接手一个新项目应该如何入手
  14. android省市区三级联动,NumberPicker实现省市区三级联动的效果
  15. vs2008的永久破解安装
  16. x黑客X档案2006年07期
  17. 直播行业市场分析:2022年构建多元化的直播生态体系
  18. 阿里云服务器安全组宝塔端口8888开放教程
  19. 【转】什么是乐观锁,什么是悲观锁
  20. 100层楼,2个鸡蛋,最少要几次才能测试出鸡蛋能承受的最大楼层?

热门文章

  1. EditPlus设置Java代码格式化
  2. SAP TR手动导入系统操作手册
  3. minio常见问题处理
  4. SQL server无法启动服务,提示“错误1069: 由于登录失败而无法启动服务”
  5. onegreen的绿软word2003绿色版删除不掉的解决方案
  6. python语言程序设计实践教程答案实验六_Python语言程序设计实验指导与习题--详细介绍...
  7. R语言 - 安装R及RStudio(Linux、Windows双重记录)
  8. EJB3创建Timer
  9. aloge alogw alogi alogd alogv
  10. 仪表盘 图表 仪表图