去年的生活较为紧凑,上班在深圳两个区跑,说不上累但是却没了自己的时间。一直在做 PrivCh(Private Channel)的平台能力插件化改造,以至于少量空闲时间都在设计和实现,在创造。我觉得“热爱”这个说法也适用于创造活动,如果你热爱软件热爱创造,那么软件设计和开发则是一件有趣的事情,你的创造有可能影响很多人,那不是无聊的工作任务。热爱美术的人画画不累,热爱钢琴的人弹钢琴是一种享受,热爱舞台的人台上一分钟台下十年功。。。所以说,基于热爱的前提下做一件事很重要,找到你的热爱很重要

It's about the network
我说了跨平台,是么

回头一看已有一年没写点东西了,这篇博文就接着说 PrivCh 的故事吧。上偏博客讲了 PrivCh 使用 Flutter 重构的设想以及原型版本设计和开发,其实去年初那个原型版本即已经完成,到现在我已经用了有一年。让我印象深刻的是原型版本的 App 运行也都很好,一年以来都没发现什么大问题,甚至 Debug 版本都没太降低用户体验,以至于我想更新都没找到合适的理由,这也从侧面反应了 Flutter 的可靠性。

去年提到了 Flutter 插件以及我对插件的消极态度,在 PrivCh 重构的中过程中,我用平台通道实现了一套 Dart 与平台能力的交互方法,使得原型版本的 App 完全避开了构建工具版本零散所带来的问题。但是,当我试图扩展 Windows 平台支持时我这个方案的弊端就显现了,除了对 Native C/C++ 实现做 Windows 平台支持,还需要再次用平台通道连接 Native 实现以及相关的关平台 API 调用,还得把 Native 能力整合到 Flutter (Win32)App 里。再看看 Plugin,人家已经把平台能力做了很好的抽象,提供了一个小框架来支持 Native 层能力的多平台扩展和动态调用,站在模块化及可维护的角度来看 Plugin 的方式无疑更胜一筹。

于是,终于找到了升级的理由:平台能力插件化改造以及扩展 Windows 平台支持。

讲到这里先要理一理 PrivCh 的历史,PrivCh 目前可以分两个阶段:2020 年之前的定制阶段和 2020 年之后的全新跨平台设计阶段,当然了,定制版本已经成为了过去。事实上 2020 之前还开发了一个 Windows 桌面版本,是个 C# .NET WPF 应用程序,在 Windows 上用,不过这个版本取消了,确切的说是我放弃了 .NET。并不是说 .NET 不好,其实 .NET 也有跨平台计划并且有它自己的优势,相对于 Flutter、RN 等框架 .NET 在桌面领域会更专业更有经验,还有 Microsoft Windows 多年以来打下的坚实基础,其实我很看好 .NET 跨平台。只不过,人生也需要做减法,不能什么都抱着不放,况且那时 Flutter 已经测试性质的开放对 Windows 的支持了。

定制阶段做的事情主要是在官方版本的基础上移除对 Firebase 云的依赖,因为在国内不能可靠的访问 Firebase。还好 App 走云端能力的不多,印象中也就广告和二维码,广告直接删除,二维码功能则迁移到了客户端本地实现。其实还有 App 的后台 Log 及分析,这个功能放在本地还没用必须走云端,不过当时在国内没找到与我的要求很契合的产品也就搁置了。下面是原版本与第一阶段改造的差异:

原版,Kotlin
Android UI,SQLite(Room),Native VPN 加密通道,SDK 能力调用

改造,Java、Kotlin
Android UI,SQLite(Room),Native VPN 加密通道,Java 功能实现

2020 年随着 Flutter 跨平台框架的兴起,PrivCh 也朝着跨平台的方向演变,虽然定制版本已经成为过去,但是“能力本地化”的策略却一直贯彻到全新开发的跨平台版本。2020 年夏天对 Flutter 框架做了全面的了解和评估,之后便断断续续的对 PrivCh 进行重构,记得 2021 春节 PrivCh 最初的原型版本就已经在我手机上运行了。正如前面所说,很稳当,我甚至期望它出点问题这样我好排查改进,但是 PrivCh 让我失望了,它始终不出问题。原型只有 Android 平台版本,数据库和功能实现都是通过平台通道连接到 Dart,Flutter 版本的 PrivCh 是这样的:

PrivCh 2020,Dart、Java、Kotlin
Flutter UI,SQLite(Room),Native VPN 加密通道,Java 功能实现

时间来到 2021 夏天,放弃了 .NET 就得上 Flutter Desktop 否则 PrivCh 的桌面平台没法继续。Flutter UI 层容易,有了 Web 前端的技术背景基本无障碍,但是 PrivCh 毕竟不是内容展示型的 App,摆在眼前的问题是扩展 Native 层能力到 Windows 平台,可以 Porting 也可以替换。依赖于对 Win32 的了解还有以往 Windows 平台 Native 模块的建设,才让支持 Windows 显得是个时间问题。如前面所说,在激情的夏日开展了 App 平台能力插件化改造的活动,这活动断断续续的持续到冬季才基本完成。至此 PrivCh 已经进化成这样:

PrivCh Recent,Dart,Java,Kotlin,C/C++
Flutter UI,HIVE 数据库(Dart),VPN 加密通道 Plugin,平台调用、二维码等 Plugin

下图展示的是 PrivCh 进化的痕迹。最初的原型版本按钮是放在上面的并且图标较粗糙,2020 年移到了下方,图表做了一些修饰,按钮使用了不同的图标来反映 Native 层网路的状态,2021 年优化的图表渲染,可以看到线条已经平滑,按钮则更加细化的反映 Native 层的状态,数据显示除了当前速度还支持一段时间内的最大速度统计。

It's about the network
服务控制按钮的变化,从左到右依次为:原型,2020,2021

现在 PrivCh 已经实现了插件化,除了 VPN 流量加密插件其余均支持 Android、Windows 或更多平台。数据库方面,Flutter 提供的 SQLite 插件支持了 Android 但是 Windows 端却比较麻烦,为了更轻松的跨平台,数据库改用了 Dart 实现的 HIVE 对象存储数据库,是一种很轻量的本地实现,不是 HIVE 服务器。

下面这些抽象出来的中间件(均已经发布到各大平台)基本与 PrivCh 有关,其中 Armoury AAR 和 ZXing Win32 Static-Library 是为了既可以让 Flutter 插件调用,也可以支持平台应用调用,这些 Package 都放到一个仓库里。

  • xinlake_text:用于文本方面的处理,像流量信息的字节转化为 KB、MB,检查输入信息是否为 IP 地址,生成随机密码等事情就由它完成。

  • xinlake_responsive:支持“响应式”设计的 Widgets,暂时没用到。它很酷,我觉得它能强调 Flutter 与 Android/Windows 原生 UI 的特性差异,展示 Flutter 框架在人机交互上更为广泛的可能性。

  • xinlake_qrcode:是二维码能力实现,可以检测、解析图像中的二维码,也可以输入文本创建二维码图像,对接摄像头或图片文件即可实现扫码。Android 平台由 Google MLKit 支持,Windows 平台用的是 C++ 开源实现,它的 C++ 代码有 5 万行。

  • xinlake_platform:系统交互插件,如选取一个或多个文件、设置 UI 模式、获得 App 版本信息等等。

  • window_interface:Windows 系统上通过 Win32 API 操作窗口的插件。桌面应用不像移动端那样有着固定大小的窗口,桌面用户可以随时调整应用的窗口大小,高效的自适应窗口是桌面应用的基本要求,而通过插件协同操作窗口可以提升自适应的效率。

流量加密插件的 Windows 端实现与 Android 端实现很不一样,Android 上由 App 对接系统 VPN 接口来接管流量,再将 VPN Tunnel IP 层流量转给加密单元。而 Windows 上看起来没这么简单,因为很多方案都是走 Windows Proxy 接口没走 VPN。走 Proxy 的话则需要应用层主动支持,这会导致在 Windows 平台下的功能体验则会不一致,怎么把差异较大的平台实现抽象成一致的接口也是个棘手的问题。

流量加密也是体量最大的插件,加密单元的流量输入是本地 Socks5 Proxy 接口,只有较新的应用软件才支持它,多数应用支持的还是 HTTP Proxy 接口,为了兼容更多应用软件,插件 Windows 端需要有一级 HTTP Proxy。通过 PrivCh 加密插件的 HTTP Proxy 对接应用软件,再把 HTTP 流量转发至 Socks5,这样就不要去应用软件支持 Socks5 接口了。总结一下,流量加密插件将是下面这样,虽然体量大但是有一点很明确:一切都在可控范围。

Android:VpnService,Tun 流量转发,加密单元(Android 端实现)
Windows:Proxy API,HTTP Proxy,加密单元(Windows 端实现)

笼统的讲,“跨平台”或“跨端”都不影响意思表达。“端”一般蕴含着通讯的关系,像客户端与服务端、前端与后端、点对点或短对端、服务商与终端等等,这些“端”之间都存在某种通讯,是通过通讯机制组织在一起的整体,可以形成大规模的分布式的信息交互系统,单独存在则没有意义。“平台”的背后则是支撑和管理,平台提供一个基础的环境和工具集并定义规则让事务有序。像购物平台、服务平台,舞台等,每个“平台”都有它固有的特性和规则,事务需要遵循平台的规则来使用平台的工具和服务。

跨平台很好理解,即一个应用在多个(操作系统)平台上运行,框架适配了系统也抽象了平台能力。“跨端”我个人最直观的理解是跨客户端,像支持不同的客户端,“端”作为一种通讯对象“跨端”更像是一种通讯上的兼容性架构设计。

如果把 Dart 与 Native 层能力实现比作前端与后端,那么:Flutter 不只是前端,开发一个跨平台 App 不可避免的会遇到前端和多平台后端的协同开发;声明式的 UI 和 Web 前端很像,但是 Flutter 框架在类似 MVVM(状态变化驱动渲染更新)的机制上提供了大量的支持,Dart 像前端也像平台 App;后端调用不走 HTTP,而是非常直接高效的本地通讯;把页面交给浏览器只是适配了浏览器,而浏览器默默的适配了操作系统平台。开发者需要有一定的广度和深度才能架构一个跨平台应用。

春天带来新机遇,2022 新年祝大佬们生意红火!