尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

HarmonyOS 弦乐调音器开发实战 01:先分清 ArkTS 1.0.7 与 Flutter 1.0.8 两条工程线

HarmonyOS 弦乐调音器开发实战 01:先分清 ArkTS 1.0.7 与 Flutter 1.0.8 两条工程线 拿到一个已经开发了一段时间的 HarmonyOS 项目最容易犯的错误不是看不懂代码而是先认错了工程。这个弦乐调音器的顶层目录同时保留了原生 ArkTS/ArkUI 实现和 Flutter 实现两者使用相同的应用名称与 Bundle Name功能也高度相似。如果只凭文件夹名称、宣传图或某张手机截图判断很容易把 1.0.7 的原生代码、1.0.8 的 Flutter 页面以及不同时间生成的构建产物拼成一篇看似完整、实际上无法复核的文章。所以本系列第一篇不急着讲 NSDF 音高检测也不先展示调音表盘而是先建立工程地图。本文只回答四个问题两套实现各自在哪里版本号和页面入口如何确认截图与构建产物应该归到哪条工程线当前哪些结论已经有证据哪些还必须留在“未验证”一栏。为什么第一篇必须先做工程地图这个项目不是常见的“一个产品目录对应一套源码”。顶层既有AppScope、entry、hvigor等标准 HarmonyOS Stage 工程结构又有一个完整的string_tuner_flutter子目录。后者不是几张 Flutter 设计图而是包含pubspec.yaml、lib、HarmonyOS 平台壳和 ArkTS 插件的第二套可构建实现。两条工程线共享产品概念但技术责任不同原生工程的页面、状态和音频服务主要由 ArkTS 直接承担Flutter 工程的页面由 Dart 构建HarmonyOS 麦克风、隐私同意和音频事件则通过 ArkTS 插件桥接。相同的“自动调音”“曲库”“麦克风权限”文案背后不一定是同一份文件。工程审计时我采用的是“配置文件确认身份、入口文件确认运行框架、路由确认页面归属、构建日志确认编译范围、截图层级确认画面来源”的顺序。任何一步都不能被另一种证据替代。例如看到 signed 命名的 HAP 只能说明签名打包流程产生了文件不能反推某张截图就是这个 HAP 的当前运行画面。同一目录中确实存在两套实现下面这张工程图把两条线拆开。左侧是根目录原生 Stage 工程右侧是 Flutter 子工程。中间共享的是产品概念和少量视觉素材不代表可以交叉引用源码或运行结论。从目录层面看可以先用下面这个最小树状结构定位不需要扫描构建缓存和上架材料. ├─ AppScope/ # 原生应用级配置版本 1.0.7 ├─ entry/ # 原生 ArkTS/ArkUI entry 模块 │ └─ src/main/ets/ │ ├─ pages/ │ ├─ components/ │ ├─ services/ │ ├─ models/ │ └─ common/ ├─ build-profile.json5 # 原生 HarmonyOS 产品与 SDK 配置 └─ string_tuner_flutter/ ├─ pubspec.yaml # Flutter 版本 1.0.81000008 ├─ lib/ # Dart 页面、状态与调音逻辑 └─ ohos/ # Flutter 的 HarmonyOS 平台工程与 ArkTS 插件这张图还说明了一个写作原则后续原生文章引用entry/src/main/ets/services/AudioService.etsFlutter 文章则引用string_tuner_flutter/ohos/entry/src/main/ets/plugins/StringTunerPlugin.ets。两份代码都实现了音频采集和 NSDF 思路但不能把其中一份的常量、函数名或验证结果说成另一份的实现。原生 ArkTS 线Stage 模型与 1.0.7根目录AppScope/app.json5给出了原生应用版本。下面只摘录非敏感字段签名配置不进入文章{ app: { bundleName: com.example.stringttuning, vendor: example, versionCode: 1000007, versionName: 1.0.7, icon: $media:layered_image, label: $string:app_name } }根目录build-profile.json5的产品配置进一步确认运行系统是HarmonyOS目标和兼容 SDK 都是6.0.2(22)。这也是为什么本系列使用HarmonyOS、ArkTS和ArkUI标签而不沿用其他项目的OpenHarmony、开源鸿蒙、LiteOS-M 或.fwpkg口径。products: [ { name: default, signingConfig: default, targetSdkVersion: 6.0.2(22), compatibleSdkVersion: 6.0.2(22), runtimeOS: HarmonyOS } ]entry/build-profile.json5中的apiType是stageModeentry/src/main/module.json5声明了phone、tablet和2in1并以EntryAbility作为主入口。原生页面由 ArkUI 组件直接构建EntryAbility负责初始化 AppStorage、监听窗口宽度、初始化设置与历史服务再加载pages/Index。Flutter 线Dart 页面、HarmonyOS 壳与 1.0.8Flutter 子工程的版本证据来自string_tuner_flutter/pubspec.yaml不是根目录的 AppScopename:string_tuner_flutterversion:1.0.81000008environment:sdk:^3.11.5它自己的ohos/AppScope/app.json5同样写入versionCode: 1000008和versionName: 1.0.8。入口类继承FlutterAbility在 FlutterEngine 建立后注册GeneratedPluginRegistrant和项目自定义的StringTunerPlugin。Dart 侧通过两个明确的通道访问原生能力staticconstMethodChannel_methodMethodChannel(string_tuner/native);staticconstEventChannel_eventsEventChannel(string_tuner/audio_events);MethodChannel 用于隐私状态、麦克风启动与停止等命令EventChannel 用于把频率、清晰度、波形摘要等连续数据送回 Flutter。也就是说Flutter 1.0.8 并非简单 WebView也不是把原生 ArkUI 页面嵌进去它是 Flutter 页面加 HarmonyOS ArkTS 插件的组合。路由和页面归属不能混看原生工程的main_pages.json注册了五个路由{src:[pages/Index,pages/SettingsPage,pages/PcLayoutPage,pages/InstrumentSelectPage,pages/MusicLibraryPage]}这里有一个容易误判的细节原生的首页、调音、历史和“我的”四个主页面并不各自出现在main_pages.json中而是由Index.ets以四个 Tab 组合。设置、乐器选择和曲库通过 Router 进入PcLayoutPage虽然注册了路由但当前项目内没有找到跳转到ROUTE_PC_LAYOUT的实际入口。因此可以写“主页面会根据窗口宽度把底部导航重排为侧边导航”却不能写“独立 PC 工作台已经接入完整导航闭环”。Flutter 的 HarmonyOSmain_pages.json只有pages/Index。这不是 Flutter 只有一个产品页面而是 HarmonyOS 平台侧只承载 Flutter 容器真正的首页、调音、历史、我的和曲库等页面切换发生在 Dart Widget 树中。看到平台壳只有一个路由时不能据此删掉 Flutter 的页面描述反过来也不能拿 Flutter 五项导航去解释原生Index.ets的四个 Tab。从运行截图反推界面归属截图是文章最直观的证据也是最容易被误用的证据。项目中存在两组不同日期、不同实现的运行图必须连同时间和来源一起说明。上图来自 2026-05-14 的原生早期运行记录。画面底部是“首页、调音、历史、我的”四项导航调音区包含琴弦选择、圆形刻度、Hz、音分偏差和输入柱状反馈与原生TunerPage.ets、TunerDial.ets、StringSelector.ets的结构相符。它能够证明当时的原生界面曾经在设备形态的窗口中运行也能作为交互演进素材但它早于当前源码和 1.0.7 本轮构建按钮文案与布局细节已经可能变化所以不能写成“当前版本逐像素截图”。画面中的某次频率读数也不是经过标准信号源标定后的精度报告。第二张图同样属于原生早期记录但展示的是宽屏形态。它证明项目曾经探索过乐器卡片、自动/手动入口、快捷功能和输入柱状区在宽窗口中的排布。当前源码已经改为根据windowWidth和600/840 vp断点决定底部或侧边导航因此这张图适合说明“多尺寸设计过程”不适合替代本轮手机、平板、2in1 三类设备的重新验收。第三张图拍摄于 2026-07-05归属 Flutter 调试实现。对应的布局层级中存在 Flutter 的XComponent底部导航有五个入口页面文案也与 Dart 版本一致。截图本身和布局导出都没有版本字段而且素材时间早于当前pubspec.yaml与 Flutter AppScope 的版本元数据修改时间所以它只能证明当时 Flutter 工程曾启动并显示不能绑定为精确的 1.0.8 包更不能自动证明后来生成的 reviewfix 构建包已经完成实机回归。第四张图展示 Flutter 曲库。这里的“曲库”是当前实现中的曲目与参考音入口不应扩写成在线曲库、完整乐曲播放、乐谱下载或云端同步。截图能够支持页面存在和基础内容展示无法单独证明每个按钮、每种乐器音色以及后台生命周期都通过了真机测试。因此文章使用截图时至少要同时记录三项信息截图日期、所属实现、能够证明的最小结论。只写“项目运行效果如下”而不说明证据边界会让读者无法判断画面究竟对应当前原生工程、Flutter 工程还是某个更早的开发状态没有画面内版本号或同期包证据时也不能自行补上精确版本。构建产物也必须按实现分账两套工程都可能生成entry-default-signed.hap仅凭文件名无法判断来源。原生产物位于根工程的entry/build/default/outputs/defaultFlutter 产物位于string_tuner_flutter/ohos/entry/build/default/outputs/default。发布或归档时应把“实现、版本、生成命令、生成时间、大小、哈希”放在同一条记录中。同理release_upload中可见 Flutter 资源和 Flutter 平台打包结构因此不能拿这些文件反向证明原生 1.0.7 已经执行了某次上架流程。现有历史材料只能支持较窄的结论1.0.7 当时已有上架记录1.0.8 当时仍在准备和修订阶段。本文不把“准备了上传材料”写成“已经提交”也不把“存在 signed 文件”写成“已经发布”。为了降低以后再次混淆的风险可以在工程根目录分别计算关键配置和产物哈希。命令使用相对路径不需要在文章中公开本机目录Get-FileHash.\AppScope\app.json5-Algorithm SHA256Get-FileHash.\entry\build\default\outputs\default\entry-default-signed.hap-Algorithm SHA256Get-FileHash.\string_tuner_flutter\pubspec.yaml-Algorithm SHA256Get-FileHash.\string_tuner_flutter\ohos\entry\build\default\outputs\default\entry-default-signed.hap-Algorithm SHA256哈希不是运行测试但它可以确保文章引用、后续重新构建和发布交接讨论的是同一份文件。尤其在缺少可用 Git 提交历史时这种逐文件快照比“应该是最新版”更可靠。版本证据矩阵把结论固定下来这张矩阵把版本、入口、页面技术、音频实现、截图和产物分开。阅读它时要注意横向同一行表示同一类证据不表示两列可以相互替代。ArkTS 1.0.7 的版本以根AppScope/app.json5为准页面入口是EntryAbility → pages/Index。Flutter 1.0.8 的产品版本同时出现在pubspec.yaml和 Flutter 的 HarmonyOS AppScope 中页面由FlutterAbility → FlutterEngine → Dart Widget承载。原生音频核心是AudioService.etsFlutter 的系统音频入口是StringTunerPlugin.ets。2026-05-14 图片属于原生早期记录2026-07-05 图片属于 Flutter 调试记录。两套 signed HAP 必须依据各自的目录、时间和哈希区分。这也是本系列后续文章的引用规则第 2、3 篇以原生 1.0.7 为主第 4 篇再单独讲 Flutter 1.0.8 与 ArkTS 插件桥接。即使两个实现使用相同的 NSDF 阈值也会分别标注源文件不把代码合并成一个虚构的“统一音频服务”。源码摘录怎样保持可复核技术文章里的代码不能只做到“逻辑上差不多”。文件名、类名、常量和值都应能在当前项目中找到。例如原生入口确实使用 AppStorage 建立共享状态Flutter 平台入口则确实注册自定义插件// entry/src/main/ets/entryability/EntryAbility.etsAppStorage.setOrCreate(a4Reference,440);AppStorage.setOrCreate(currentInstrument,Violin);AppStorage.setOrCreate(tunerMode,auto);AppStorage.setOrCreate(windowWidth,360);AppStorage.setOrCreate(privacyAccepted,false);AppStorage.setOrCreate(privacyReady,false);AppStorage.setOrCreate(microphonePermissionGranted,false);// string_tuner_flutter/ohos/entry/src/main/ets/entryability/EntryAbility.etsexportdefaultclassEntryAbilityextendsFlutterAbility{configureFlutterEngine(flutterEngine:FlutterEngine){super.configureFlutterEngine(flutterEngine)GeneratedPluginRegistrant.registerWith(flutterEngine)flutterEngine.getPlugins()?.add(newStringTunerPlugin())}}这两段代码表达的是不同架构前者由 ArkTS 页面直接消费 AppStorage 和服务单例后者把 HarmonyOS 能力注册给 FlutterEngine再由通道连接 Dart。写文章时如果把StringTunerPlugin说成原生 ArkUI 页的服务或者把AudioService说成 Flutter 插件读者就无法按照路径复现。同样需要避免把设计稿当源码。项目早期数据流图出现过 FFT、YIN、pYIN、带通滤波、AGC、16/24 bit 切换等设想但当前两套业务链路的核心口径都是 NSDF原生采音参数固定为 44.1 kHz、16 bit、单声道。设计稿可以用来说明需求演进不能作为“已经实现”的证明。当前能够确认的构建基线2026-08-03 对两套工程分别进行了本地验证。原生 ArkTS 工程执行assembleHap退出码为 0日志出现[2026-08-03T14:55:03.403] [INFO] debug-file - BUILD SUCCESSFUL in 28 s 88 ms同时生成 signed 命名的 HAP文件大小为 29,450,429 字节。构建仍有 26 条弃用 API 警告涉及pushUrl、back、animateTo、showToast、AlertDialog.show、AudioRenderer.write和STREAM_USAGE_MEDIA等。这些警告没有阻断本次编译但属于后续 API 迁移事项不能写成“零警告构建”。Flutter 1.0.8 的本地基线分开记录flutter analyze为零问题当前组件测试 1/1 通过release HAP 构建成功Flutter 平台的 hvigor 日志在 2026-08-03 14:59:41 记录BUILD SUCCESSFUL in 38 s 747 ms。这三个结论各自覆盖静态分析、现有测试和打包不应合并为“全部功能通过”。一个只有一项组件用例的测试结果也不能被描述为完整回归测试。更重要的是本轮 HDC 设备列表为[Empty]。因此当前可以确认的是源码能够通过相应构建流程并生成产物不能确认本轮产物已经安装、启动、授权麦克风、完成参考音播放或通过真实乐器校准。根目录空 .git 对取证有什么影响根目录存在名为.git的目录但它是空目录Git 命令会返回“not a git repository”。这意味着当前不能提供分支名、提交号、提交时间、工作区是否干净等常规版本证据也不能把某个构建产物可靠地绑定到一个 commit。这里要区分“没有 Git 证据”和“没有源码”。源码、配置、构建日志与产物都存在只是缺少提交历史。文章仍然可以依据当前文件编写但要使用逐文件 SHA-256、文件相对路径、版本字段和构建时间建立证据锚点。后续如果要恢复 Git 管理应该另行初始化并明确首个基线本文不会编造一个不存在的历史提交也不会因为目录名叫.git就宣称仓库状态干净。当前仍不能宣称的内容建立双工程地图的意义最终体现在结论边界上。根据当前证据以下说法都不成立或证据不足不能把这个新系列标为 OpenHarmony 或开源鸿蒙项目当前配置明确是 HarmonyOS。不能说 ArkTS 1.0.7 与 Flutter 1.0.8 共用同一套页面源码它们只共享产品目标和部分数据设计。不能把 2026-05-14 的原生截图说成当前 1.0.7 的逐像素运行结果。不能把 2026-07-05 的 Flutter 调试截图说成 reviewfix 包或当前重新构建 HAP 的实机验收图。不能因为出现BUILD SUCCESSFUL就宣称音高精度达到 ±2 cents、稳定 60 FPS、低延迟、低功耗或指定温升。不能把首页 32 段时域幅值摘要写成 FFT 实时频谱也不能把未接入的PitchDetector.ets写成当前主算法入口。不能把曲库扩写为联网曲库、完整歌曲播放、乐谱下载或云端同步。不能把 signed 命名产物等同于已提交 AppGallery、已审核或已公开上架。不能公开build-profile.json5中的签名口令、证书路径或任何账号、设备标识。本文展示构建配置时主动省略了整个敏感对象。对于调音器而言最关键的缺口仍是真实设备验证。后续应使用已知频率的信号源或经过校准的乐器记录目标频率、检测频率、音分误差、环境噪声、设备型号、窗口长度与重复次数参考音则需要检查扬声器响度、是否失真、停止和切弦时是否残留。没有这些记录之前算法公式与编译成功都不能替代精度结论。后续文章怎样沿两条工程线展开工程身份明确之后后续内容就可以各自深入而不串线。第 2 篇将只沿原生 1.0.7 的AudioService.ets → TunerPage.ets → FrequencyUtils.ets链路分析 AudioCapturer 的 44.1 kHz/16 bit/mono PCM、4096 点窗口、NSDF、清晰度阈值、自动选弦和音分换算同时说明 32 柱显示为什么不是 FFT。第 3 篇继续留在原生线讨论ReferenceToneService的小提琴 PCM 优先与程序合成回退、HistoryService的 Preferences 记录、AppStorage 页面协同以及设置项真正接通到业务的范围。第 4 篇才切换到 Flutter 1.0.8单独解释 MethodChannel、EventChannel、StringTunerPlugin、Dart Widget 和 HarmonyOS 平台构建之间的关系。先分清工程并不会让技术内容变少反而能让每段代码、每张图和每个验证结论都有明确归属。对这个项目而言当前最准确的总览是原生 ArkTS 1.0.7 与 Flutter 1.0.8 都有真实源码和本地构建证据两者都有历史运行画面但本轮没有连接设备尚无当前产物的安装、麦克风、参考音、精度和性能回归。后续文章将以这个边界为起点而不是把两条实现拼成一个不存在的版本。
返回列表