协会宣传网站开发方案,一键logo设计网,集团logo设计公司,wordpress 菜单大小一#xff0c;需求背景#xff1a;
APP 发布到市场后#xff0c;难免会遇到严重的 BUG 阻碍用户使用#xff0c;因此有在不发布新版本 APP 的情况下使用热更新技术立即修复 BUG 需求。原生 APP#xff08;例如#xff1a;Android IOS#xff09;的热更新需求已经…一需求背景
APP 发布到市场后难免会遇到严重的 BUG 阻碍用户使用因此有在不发布新版本 APP 的情况下使用热更新技术立即修复 BUG 需求。原生 APP例如Android IOS的热更新需求已经比较成熟但 Flutter 技术栈目前还缺少类似的技术方案因此 Flutter 研发团队也需要类似的热更新技术。
二Flutter 热更新技术方向分析
经过分析目前可能有三种可行的方案1类似 RN 框架2页面动态组件框架3Dart 虚拟机定制方案 方案名称原理优点缺点开源方案类似 RN 的方案用 JS 以 Flutter 语法写 dart然后用 JavaScript 把 XML DSL 转为 Flutter 的原子 widget 组件然后再让 Flutter 来渲染由于 ios 系统内置支持 jsios 上完全可以实现更新1) 由于跨语言执行对于性能有影响学习成本高 2) Android 端需要额外引入 JS 库手 Q 的 MXFlutter58 同城的 Fair页面动态组件方案编译期时插桩 / 预埋好 DynamicWidget 到代码中然后动态下发 Json 数据通过协定好的语义匹配到 JSON 内的数据动态替换 Widget 内容来实现更新能支持 Android/iOS 两端的更新1UI 更新相对较容易业务逻辑动态化较麻烦2语义解析器开发成本相对较大且不易维护 3需要一整套前后端服务和工具天猫的 Tangram淘宝的 DinamicX 等Dart 虚拟机定制方案通过分析 Dart 虚拟机的原理修改 Flutter Engine 层 Java/C 代码实现热更新的目标性能影响小动态性很高技术上可以替换所有 Flutter 页面包括 UI逻辑资源文件由于使用的是定制引擎需要维护不同版本的 Flutter 引擎代码未开源 因为其他方式都有开源的示例本案将重点以第三种 “Dart 虚拟机定制方案” 为目标做方案的研究讲解。
三预备知识
在开始了解技术方案之前需要提前了解一些相应的技术概念
3.1 Flutter 编译模式
Flutter 开发语言是 Dart它的编译模式来自 Dart 的编译模式主要有 JIT (Just In Time) 和 AOT (Ahead Of Time)。 编译模式名称特点优点缺点JIT即时编译典型例子 V8它可以即时编译运行 JS只需要输入源代码字符串就可以编译运行代码可以动态下发和执行代码不用管 CPU 架构可以提供动态化内容1, 大量字符串代码让 JIT 编译器花费时间和内存2, 性能不好AOT预先编译典型例子 C/C通过 GCC 编译成二进制代码然后安装取得权限后才可以加载执行事先编译好的加载和执行速度快1, 编译时区分 CPU 架构2, 生成的二进制代码包比较大3, 二进制代码需要取得权限才可以执行无法在 ios 系统上动态更新 Flutter 编译模式有DebugReleaseProfile Flutter 编译模式特点Debug对应 JIT 模式支持设备和模拟器打开了断言支持快速开发支持 HotReload并未对包大小执行速度做优化Release对应 AOT 模式支持真机不支持模拟器禁止了所有断言调试信息对包大小启动和执行速度进行了优化Profile类似 Release 模式保留了一些调试功能帮助性能分析 3.2 Flutter 编译产物分析 Flutter 下的 iOS/Android 工程本质上是一个标准的 iOS/Android 的工程IOS 平台 Flutter 通过在 BuildPhase 中添加 shell (xcode_backend.sh) 来生成和嵌入 App.framework 和 Flutter.framework 到 ios; Android 平台 Flutter 通过 gradle 来添加 flutter.jar 和编译完的二进制文件添加到 Android;3.2.1 引擎层结构分析
3.2.2 Android 编译产物的分析
3.2.3 IOS 编译产物的分析
四热更新技术方案分析
4.1 业务代码分析
根据 “3.3.1” “3.3.2” 的分析可以确定无论是 IOS 还是 Android APP 业务代码都是由四个段组成kDartVmSnapshotData、kDartVmSnapshotInstructions、kDartIsolateSnapshotData、kDartIsolateSnapshotInstructions理论上只要能动态替换加载的代码段 数据段代码即可实现目标。 名称注释作用注释kDartIsolateSnapshotDataDart isolate 数据段类信息全局变量函数指针等允许动态下发kDartIsolateSnapshotInstructionsDart isolate 指令段包含由 Dart isolate 执行的 AOT 代码IOS 不允许动态下发kDartVmSnapshotDatavm isolate 数据段isolate 之间共享的 Dart 堆 (heap) 的初始状态允许动态下发kDartVmSnapshotInstructionsvm isolate 指令段包含 VM 中所有 Dart isolate 之间共享的通用程序的 AOT 指令IOS 不允许动态下发 注释isolate, snapshot, vm isolate 含义解释如下 名称含义isolateDart 是单线程isolate 跟线程差不多可以理解为 Dart 中的线程。isolate 与线程的区别线程与线程之间是共享内存的而 isolate 和 isolate 之间是内存不共享的。不存在锁竞争问题两个 Isolate 完全是两条独立的执行线且每个 Isolate 都有自己的事件循环它们之间只能通过发送消息通信所以它的资源开销低于线程。snapshot将类信息、全局变量、函数指令直接以序列化的方式存在磁盘中称为 Snapshot快照。vm isolate同一个进程里可以有很多 isolate但两个 isolate 的堆区是不能共享的所以官方设计了 VM isolate也就是 kDartVmSnapshot用来多个 isolate 之间的交互。 4.2 业务代码的加载分析运行时
按照 4.1 的分析思路我们首先需要了解 Flutter 运行时代码加载的完整流程经过梳理分析流程如下1 Android- APP 业务代码的加载流程 2IOS- APP 业务代码的加载流程
4.3 业务代码的编译生成编译时
根据以上的分析我们知道了 Flutter 业务代码的数据结构也知道了在运行时如何加载因此我们只需要在编译时做更改产生自己需要的代码段和数据段文件。在运行时加载自己的构建产物即可达到目标。1在此以 IOS 构建自己的业务代码流程做详细分析
** 有完成构建流程可以分析基本流程是 “Dart Code业务代码” - (通过 Dart 编译器 gen_snapshot.cc) 生成 snapshot_assemble.S 的汇编文件 - (通过 xcrun 工具) 生成 snapshot_assemble.o 的 obj 文件 - (通过 xcun clang 工具链) 生成了 App.Framework。2Android 的产物构建流程和 IOS 类似。由于 Android 有其他更简单的方案 因此省略详细的构建流程分析大致如下 4.4 实现热更新的方案探索
根据上面的技术分析结果已经可以独立生成自己的代码段数据段文件。通过需改虚拟机底层代码的方式也可以动态的加载运行。但由于 IOS 系统目前底层的系统还不能动态加载可读写的代码段数据到内存中所以还有技术难点需要突破。但 Android 端有更简单的路径可以解决因此下面以 Android 端为例重点分析思路大致如下图所示
由上图可以得知Android 端 热修复核心步骤如下
1 修改 Flutter Engine 代码加载指定路径的 libapp.so 和 flutter_aasets比如私有目录 (data/data/files)
2 编译 APK 时利用 Gradle Transform 插件根据 Flutter SDK 的 engine version 动态替换官方的 Flutter engine最终写入修改后的 engine 到 APK
3 生成补丁包利用 BSdiff 算法比较新旧 APK 文件生成 patch 补丁包
4 APP 启动时访问后端接口根据参数app 的版本号补丁包版本号md5flutter SDK 版本号Engine 版本号拉取补丁包
5 合成补丁包校验 md5app 版本号补丁版本号安装时间
6 自定义 Flutter Engine 加载指定路径的 libapp.so 和 flutter_assets 资源文件