A
Air官方
【Air 系列 · 鸿蒙 PC 适配全记录】18 / 56
■ 应用:AirKam(from digiKam)(AirKam)
■ 定位:digiKam 移植,照片库管理
■ 上游:digiKam | 路线:Qt5 5.15+KF5+QPA | 版本:1.3.0
■ 开源: https://gitcode.com/Andy88/Harmony-digiKam
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917615090522471519
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-digiKam
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirKam
digiKam 是 KDE 生态中专业级照片管理应用,覆盖相册管理、图像查看与编辑、RAW 解码、元数据维护和人脸识别等能力。对摄影工作流来说,它承担的是"导入、整理、检索、加工、输出"这条完整链路,这类重数据、重界面的桌面应用恰恰是检验鸿蒙 PC 生态成熟度的试金石。
AirKam 是 digiKam 6.3.0-git 向 HarmonyOS 的移植工程,目标是全栈原生交叉编译——不是重写、也不是 Web 壳——在鸿蒙侧跑通"导入照片、相册浏览、图像查看与编辑、元数据与导出"全流程,并且界面全中文。工程沿用 AirKrita 验证过的 Qt5 + ohos QPA 双层架构,把 Qt 5.15.16、KF5 框架、exiv2、OpenCV、libraw 等依赖全部交叉编译到 arm64-v8a。
当前鸿蒙工程包名为 com.airkam.app,版本 1.0.0,deviceTypes 声明 phone、tablet、2in1 三种形态。汉化直接采用 digiKam 6.3.0 官方 po 翻译,digikam.mo 含 5737 条 zh_CN 文案,与 KF5 全套 .mo/.qm 一起随 HAP 分发。工程在真机上完成过多轮系统级验证,包括 MatePad 11.5、MatePad Mini 与 MateBook Pro(HAD-W32)三台设备,PC 自由窗口连续缩放回归也在工程档案中有完整记录。
▍二、先划清边界:不把编译通过当成适配完成
digiKam 的体量决定了"编过"与"可用"之间隔着很长的距离。它的启动链依赖 KXmlGui 菜单框架、模态向导、SQLite 相册库、dplugin 插件体系,任何一环在鸿蒙的沙箱与单窗口环境下都可能断链。AirKam 的口径因此很明确:以真实可交互的用户流程为验收单位,编译产物只是起点。
层次 原项目实现 鸿蒙侧当前方案
应用入口 main() + 桌面 QApplication Stage 模型 EntryAbility + Index.ets XComponent
窗口承载 桌面窗口系统 + QWidget 主窗 单 XComponent SURFACE + 自研 libqohos QPA
文件选择 QFileDialog/KFileDialog 桌面对话框 Digikam_OhosPickFile 桥接系统 DocumentViewPicker
菜单系统 KXmlGui 原生菜单栏 Qt 菜单树 6 秒周期刷新 + ArkTS 标签栏呈现
相册数据库 Qt SQL + SQLite 直连 同引擎,BUSY_TIMEOUT 调整为 10000ms 补丁
图片导出 Export webservices 插件族 系统分享面板 Digikam_ShareExport(选中不超过 8 张)
全屏放映 幻灯片放映模式 单表面模型不可用,预览工具栏已清场(P10 补丁)
这张表里最值得说明的是文件选择与导出两行:digiKam 内部所有打开、保存、选目录的调用点都被收敛到系统选择器,导出则改走鸿蒙系统分享面板。边界因此清晰——照片管理的核心链路在鸿蒙侧真实运行,依赖桌面特权的部分(全屏放映、联网导出服务)明确标注为当前不可用,不伪装。
▍三、鸿蒙版的整体架构
整体是 AirKrita 验证过的双层架构:ArkTS 壳负责 Ability 生命周期、数据部署与系统桥接;Native 侧按拓扑序 dlopen 全部依赖后,在受控线程里创建 QApplication 与 DigikamApp 主窗口,由专门的 Step 泵驱动 Qt 事件循环。
text
EntryAbility(rawfile→沙箱部署 / stamp 幂等 / 分享接收)
└── Index.ets:XComponent SURFACE + splash + 选择器桥
│ NAPI:libdigikambridge.so(薄壳,只链系统库)
▼ initDigikam 按拓扑序 dlopen 全部依赖(RTLD_GLOBAL)
libdigikam_core.so(digikam_patches/digikam_ohos_entry.cpp)
├── QApplication + DigikamApp(Qt 主窗口,KXmlGui)
├── RenderThread:Digikam_Step(8ms 保底节拍 + 事件驱动唤醒)
├── 指针/键盘注入:QWindowSystemInterface(原生像素,Qt 内一次 ÷dpr)
├── 选择器桥:Digikam_OhosPickFile ← Digikam_CompletePick(ArkTS 回传)
└── digikamcore + digikamgui + digikamdatabase(官方库结构)
dplugins(Generic_/Editor_/Bqm_ 插件 .so 平铺 entry/libs/arm64-v8a)
仓库主要目录如下:
text
AirKam/
├── AppScope/ # 应用级配置(com.airkam.app / 1.0.0)
├── digikam-master/ # digiKam 6.3.0-git 上游源码(补丁锚点替换目标)
├── deploy_fast.sh # fix_needed + 打包 + 部署的快捷链
├── build-profile.json5 # 工程级构建与签名配置
├── hvigorfile.ts / hvigor/ # hvigor 构建系统
└── entry/
├── libs/arm64-v8a/ # 164 个 .so(digiKam 四库、dplugins、Qt5/KF5 依赖)
└── src/main/
├── module.json5 # 权限、sendData skills、backup 扩展
├── ets/pages/Index.ets # XComponent 宿主 + 菜单标签栏
├── resources/rawfile/digikam/ # share 数据树、breeze 图标、zh_CN locale
└── cpp/
├── napi_init.cpp # libdigikambridge.so 薄桥
├── digikam_patches/ # apply_ohos_patches.py + digikam_ohos_entry.cpp
├── build_kf5_new.sh / build_thirdparty.sh / build_digikam_ohos.sh
├── fix_needed.py # DT_NEEDED 版本名重写,部署前必跑
├── qt5_source/ # Qt 5.15.16 自建源码树
├── ohos_deps/ # 复用 AirKrita 的依赖与本项目新编库
└── qt_platform/ # libqohos QPA 插件源码
▍四、核心适配过程
### 1. 拓扑序 dlopen 与 Step 事件泵
napi_init.cpp 生成的 libdigikambridge.so 只链接系统库,真正的启动由 digikam_patches/digikam_ohos_entry.cpp 的 Digikam_Init 完成:按依赖拓扑序 dlopen 164 个 .so(RTLD_GLOBAL),创建 QApplication 与主窗口,再启动 RenderThread 执行 Digikam_Step——8ms 保底节拍加事件驱动唤醒,负责 sendWindowSystemEvents 与 processEvents。事件泵在 QApplication 创建后立即可用(踩坑 A12 的结论),否则 Init 期任何意外模态框都会因为拿不到输入而卡死启动链。
### 2. 幂等补丁系统与宏全局化
对 digiKam 上游源码的修改全部走 digikam_patches/apply_ohos_patches.py 的幂等锚点替换,P1 到 P12 共十余处:P1/P7 把 DFileDialog 的静态方法与实例式 exec 都短路到系统选择器;P2/P4 跳过 KXmlGui 的快捷键冲突检查;P3 把 SQLite BUSY_TIMEOUT 从 0 调到 10000ms;P5 修复相册树点击信号链;P9 把扫描控制器的嵌套 exec 改成分片等待;P12 抑制冷启动必弹的 No Fingerprints 信息框。工程曾因宏只定义在单个编译目标上导致 P1 静默失效,此后补丁系统在 add_subdirectory 之前全局注入 Q_OS_OHOS=1 OHOS_DIGIKAM_PORT=1,杜绝按目标漏定义的问题。
### 3. 窗口分层、输入路由与选择器桥
鸿蒙侧只有一块 XComponent 表面,Qt 的二级弹窗采用 overlay 矩形混合叠加:flush 按 g_windowRegistry 栈序合成 z-order,指针事件自顶向下做命中测试找到包含触点的可见顶层窗注入,Press 起隐式抓取整条序列,键盘投给最顶可见对话框。物理键盘经 OH_NativeXComponent_RegisterKeyEventCallback 在 NDK 层直收(ArkUI 焦点链不可靠),双击由 QPA 在 400ms/8px 窗口内合成。文件选择统一走 Digikam_OhosPickFile 嵌套循环等待 ArkTS 的 DocumentViewPicker 回传,保存路径由 native 守护线程轮询暂存文件、稳定 1.5 秒后触发回拷到用户选择的 URI。
### 4. 暗色主题、汉化与系统分享
applyForcedDarkTheme() 在 QApplication 创建后以 Fusion 风格套用 Breeze Dark 全角色配色,同时写 kdeglobals 的 ColorScheme 与 Icon 主题,ArkTS 壳三点钳制 COLOR_MODE_DARK,构成 Qt 与系统双通道暗黑。汉化方面,菜单标签经 fixupMenuLabel 修正个别条目并完成 digikam 到 AirKam 的去品牌,菜单树每 6 秒刷新一次以覆盖 DPlugin 延迟填充。分享能力由 Digikam_ShareExport 实现发送侧(选中图像拷出后经 TSFN 回 JSON,ArkTS 拉起系统分享面板),接收侧注册 harmonyShare.on('dataReceive') 与 sendData skills,分享来的文件落入相册库并触发后台扫描。
### 5. 按文件路径的重建流水线
改 digikam-master 源码后必须按文件路径 ninja 四个库(别名目标不可靠),四库以去版本单一名拷入 entry/libs/arm64-v8a/,llvm-strings 校验补丁标记真的进了库,再跑 deploy_fast.sh。改 qt5_source(例如 A36 的动画根治)后,增量重编 libQt5Widgets.so 并同样执行 fix_needed.py 重写 DT_NEEDED——这两条纪律分别来自踩坑 A22、A30、A35 与 A36 的真实事故。
▍五、适配中遇到的几个难点
### 难点一:嵌套事件循环锁死宿主泵
相册列表点击后永久卡死的现象,根因是 ScanController 的三处嵌套 eventLoop->exec() 在宿主泵互斥量临界区内无限期等待扫描完成。桌面 Qt 由 QPA 派发器驱动嵌套循环没有问题,而本移植的 RenderThread 抢不到互斥,事件投递与渲染全停。解法是 P9 补丁把嵌套 exec 改为分片等待:连接先于触发建立防止信号丢失,每 20ms processEvents 后主动让出宿主泵互斥再退避重取。配套的两个次生坑——未持锁时 unlock 破坏 libc++ 条件变量内部计数、让路后直接 lock 造成 Step 饿死——分别用标志守卫与 try_lock 退避解决。
### 难点二:跨线程信号静默丢弃
相册树点击后视图不切换,排查发现 signalAlbumCurrentChanged(QList<Album*>) 由 RenderThread 发射、宿主线程接收,而 QList<Album*> 未注册 metatype,Qt 跨线程排队投递直接静默丢弃,只留一条 QtWARN。Init 期三次选中正常是因为同线程直连,极具迷惑性。修复是 QApplication 创建后 qRegisterMetaType 补注册,同类隐患 QItemSelection 一并处理。这个案例的价值在于日志证据链:从 TREEHIT 命中、基类处理到信号零调用的每一步都有埋点实证。
### 难点三:窗口变化族的黑屏与黑边
最小化恢复、切后台回前台、熄屏唤醒后画面全黑,根因是 surface 销毁回调里停掉了 RenderThread 并触发引擎清理,恢复后新线程又因状态标志立即退出,再无人调用 sendWindowSystemEvents。修复后 surface 销毁不停车,重建时立即回注并武装 SURF-KICK 重绘爆发。随后的最大化黑边是互补根因:QPA 的 setGeometry 从未向 Qt 回报 handleGeometryChange,QResizeEvent 永不下发,内容钉死旧矩形。两处修复都在 MatePad Mini 与 MateBook Pro 上做了逐像素 diff 回归,恢复后与基线差异在噪声级。
### 难点四:PC 自由窗拖边缩放的线程亲和崩溃
MateBook Pro 上拖边缩放窗口触发 SIGSEGV,栈定位到 QWidgetAnimator 的动画对象跨线程销毁竞态:动画在 RenderThread 创建,属主却在宿主线程。第一层应用级 QProxyStyle 修复被 digiKam 启动期的 setStyle("Fusion") 覆盖而失效,最终下沉到 Qt 源码——QCommonStyle 的动画时长 hint 直接归零,qwidgetanimator.cpp 硬走一次性几何直通。修复后 18 次缩放(含 8 连发风暴)pid 稳定零 faultlog。同族的闪烁与黑帧问题分别由"尺寸变更不销毁 EGLSurface"与"flush 尺寸同步保留旧画面拉伸拷贝"解决,280 余次几何事件实测零 surface 重建。
### 难点五:musl 沙箱的库文件纪律
踩坑表里反复出现同一类事故:exiv2 安装产生的 libexiv2.so.27 符号链接在 musl 严格 NEEDED 语义下失效;digiKam 四库带版本名副本与单一名并存时,dlopen 命中孤儿副本导致 165 个 dplugin 全部加载失败;新编 Qt 库 DT_NEEDED 带版本号会让 QtHostThread 静默退出, ArkUI 壳照常显示但 XComponent 全黑。工程由此固化两条铁律:库文件只允许去版本单一名,任何新库入库前必跑 fix_needed.py。另外 QFile::copy 在 musl 加 CJK 路径组合下失败,全部拷贝逻辑替换为自研 ohosCopyFile(分块读写加尺寸校验)。
▍六、编译、安装与启动
Native 依赖链按顺序构建:build_kf5_new.sh 编 KF5 新增 7 模块,build_thirdparty.sh 编 exiv2 0.27.7、OpenCV 4.5.5、qtxmlpatterns 与 libqsqlite,build_digikam_ohos.sh 打补丁、配置、编译 libdigikam_core.so 与全部 dplugins 并收集部署,最后 hvigor 编薄桥与打包签名。
ArkTS 工程的标准构建命令:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap(工程档案记载该 HAP 约 186 MB,含 164 个 native 库与数据文件)。连接设备后安装并启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airkam.app
日常改源码后的快速部署走 deploy_fast.sh,它内含 fix_needed.py 重写、HAP 打包与卸载重装。工程档案对 PC 部署的记载是:devecocli run --device 3QC0225C09000179 --skip-build 一次成功,随后完成了拖动、贴边、最大化、还原的完整窗口变化验证矩阵。
▍七、当前功能边界
已覆盖的能力,均有真机验证记录支撑:
- 可安装 HAP、双层架构启动链、164 库拓扑序加载与 Qt 主窗呈现;
- 相册树点击、缩略图网格、滚动、单选、双击进预览、预览翻页;
- 图像编辑器(82 个 Editor 插件注册加载)、灯桌、维护与配置对话框;
- 导入全流程走系统 DocumentViewPicker,相册真实缩略图生成;
- 系统分享面板发送(发送与接收两侧均真机实证)与碰一碰接收注册;
- 强制暗黑主题(Qt 调色板 + kdeglobals + ArkTS 三通道)、全中文界面(5737 条 zh_CN);
- 物理键盘、双击合成、指针 hover 光标桥;
- PC 自由窗口拖动、贴边、最大化、拖边缩放与最小化恢复。
尚未覆盖或处于观察项的能力:全屏幻灯片放映在单表面模型下不可用(P10 已做菜单与工具栏清场);Export 菜单对应的 webservices 导出插件未编译且端口刻意断网,分享面板承担导出职责;相似度搜索依赖的指纹从未生成,相关提示已被 P12 抑制;图标视图条目高度的 A24 问题在部分设备上降级观察;自动化无法触发的拖边 resize 仍待手工复验。工程对库文件单一名、日志 .txt 后缀、按路径构建等纪律性约束同样构成交付边界的一部分。
因此,当前版本更准确的定位是"在鸿蒙侧真实运行的全栈原生 digiKam 照片管理基础版":照片管理主链路(导入、浏览、查看、编辑、分享)已经完整可用,桌面特有能力按清单如实缺省。
▍八、总结
AirKam 的价值在于验证了重型 KDE 应用的完整移植路径可以复用:Qt5 + ohos QPA 双层架构、幂等补丁系统、拓扑序 dlopen 与 Step 泵,这套组合在 Krita 上成立之后,在 digiKam 这种依赖更复杂的场景里依然收敛。38 项踩坑记录、12 个源码补丁与三台设备的验证矩阵,把"编译通过"与"可交付"之间的距离拆成了可执行的工序。
后续工作的重心在工程档案中同样明确:A24 图标视图布局与 libunwind 转储路径两个遗留项待收口,遮挡回前台不自动重绘的零事件族已立项待修,拖边 resize 等自动化盲区需要手工复验补齐。每一次修复的验收标准不变——可构建的 HAP、真机上的实际操作、以及逐像素级的画面证据。
■ 应用:AirKam(from digiKam)(AirKam)
■ 定位:digiKam 移植,照片库管理
■ 上游:digiKam | 路线:Qt5 5.15+KF5+QPA | 版本:1.3.0
■ 开源: https://gitcode.com/Andy88/Harmony-digiKam
■ 市场状态:已上架 AGC 应用页: https://appgallery.huawei.com/app/6917615090522471519
■ 官网(免费下载): https://www.shunidc.cn/
————————————————————
开源鸿蒙 PC 社区: https://harmonypc.csdn.net/
PC 社区项目申请: https://atomgit.com/OpenHarmonyPCDeveloper
适配开源地址:https://gitcode.com/Andy88/Harmony-digiKam
环境搭建文章:https://blog.csdn.net/weixin_52908342/article/details/161343743
▍一、为什么要适配 AirKam
digiKam 是 KDE 生态中专业级照片管理应用,覆盖相册管理、图像查看与编辑、RAW 解码、元数据维护和人脸识别等能力。对摄影工作流来说,它承担的是"导入、整理、检索、加工、输出"这条完整链路,这类重数据、重界面的桌面应用恰恰是检验鸿蒙 PC 生态成熟度的试金石。
AirKam 是 digiKam 6.3.0-git 向 HarmonyOS 的移植工程,目标是全栈原生交叉编译——不是重写、也不是 Web 壳——在鸿蒙侧跑通"导入照片、相册浏览、图像查看与编辑、元数据与导出"全流程,并且界面全中文。工程沿用 AirKrita 验证过的 Qt5 + ohos QPA 双层架构,把 Qt 5.15.16、KF5 框架、exiv2、OpenCV、libraw 等依赖全部交叉编译到 arm64-v8a。
当前鸿蒙工程包名为 com.airkam.app,版本 1.0.0,deviceTypes 声明 phone、tablet、2in1 三种形态。汉化直接采用 digiKam 6.3.0 官方 po 翻译,digikam.mo 含 5737 条 zh_CN 文案,与 KF5 全套 .mo/.qm 一起随 HAP 分发。工程在真机上完成过多轮系统级验证,包括 MatePad 11.5、MatePad Mini 与 MateBook Pro(HAD-W32)三台设备,PC 自由窗口连续缩放回归也在工程档案中有完整记录。
▍二、先划清边界:不把编译通过当成适配完成
digiKam 的体量决定了"编过"与"可用"之间隔着很长的距离。它的启动链依赖 KXmlGui 菜单框架、模态向导、SQLite 相册库、dplugin 插件体系,任何一环在鸿蒙的沙箱与单窗口环境下都可能断链。AirKam 的口径因此很明确:以真实可交互的用户流程为验收单位,编译产物只是起点。
层次 原项目实现 鸿蒙侧当前方案
应用入口 main() + 桌面 QApplication Stage 模型 EntryAbility + Index.ets XComponent
窗口承载 桌面窗口系统 + QWidget 主窗 单 XComponent SURFACE + 自研 libqohos QPA
文件选择 QFileDialog/KFileDialog 桌面对话框 Digikam_OhosPickFile 桥接系统 DocumentViewPicker
菜单系统 KXmlGui 原生菜单栏 Qt 菜单树 6 秒周期刷新 + ArkTS 标签栏呈现
相册数据库 Qt SQL + SQLite 直连 同引擎,BUSY_TIMEOUT 调整为 10000ms 补丁
图片导出 Export webservices 插件族 系统分享面板 Digikam_ShareExport(选中不超过 8 张)
全屏放映 幻灯片放映模式 单表面模型不可用,预览工具栏已清场(P10 补丁)
这张表里最值得说明的是文件选择与导出两行:digiKam 内部所有打开、保存、选目录的调用点都被收敛到系统选择器,导出则改走鸿蒙系统分享面板。边界因此清晰——照片管理的核心链路在鸿蒙侧真实运行,依赖桌面特权的部分(全屏放映、联网导出服务)明确标注为当前不可用,不伪装。
▍三、鸿蒙版的整体架构
整体是 AirKrita 验证过的双层架构:ArkTS 壳负责 Ability 生命周期、数据部署与系统桥接;Native 侧按拓扑序 dlopen 全部依赖后,在受控线程里创建 QApplication 与 DigikamApp 主窗口,由专门的 Step 泵驱动 Qt 事件循环。
text
EntryAbility(rawfile→沙箱部署 / stamp 幂等 / 分享接收)
└── Index.ets:XComponent SURFACE + splash + 选择器桥
│ NAPI:libdigikambridge.so(薄壳,只链系统库)
▼ initDigikam 按拓扑序 dlopen 全部依赖(RTLD_GLOBAL)
libdigikam_core.so(digikam_patches/digikam_ohos_entry.cpp)
├── QApplication + DigikamApp(Qt 主窗口,KXmlGui)
├── RenderThread:Digikam_Step(8ms 保底节拍 + 事件驱动唤醒)
├── 指针/键盘注入:QWindowSystemInterface(原生像素,Qt 内一次 ÷dpr)
├── 选择器桥:Digikam_OhosPickFile ← Digikam_CompletePick(ArkTS 回传)
└── digikamcore + digikamgui + digikamdatabase(官方库结构)
dplugins(Generic_/Editor_/Bqm_ 插件 .so 平铺 entry/libs/arm64-v8a)
仓库主要目录如下:
text
AirKam/
├── AppScope/ # 应用级配置(com.airkam.app / 1.0.0)
├── digikam-master/ # digiKam 6.3.0-git 上游源码(补丁锚点替换目标)
├── deploy_fast.sh # fix_needed + 打包 + 部署的快捷链
├── build-profile.json5 # 工程级构建与签名配置
├── hvigorfile.ts / hvigor/ # hvigor 构建系统
└── entry/
├── libs/arm64-v8a/ # 164 个 .so(digiKam 四库、dplugins、Qt5/KF5 依赖)
└── src/main/
├── module.json5 # 权限、sendData skills、backup 扩展
├── ets/pages/Index.ets # XComponent 宿主 + 菜单标签栏
├── resources/rawfile/digikam/ # share 数据树、breeze 图标、zh_CN locale
└── cpp/
├── napi_init.cpp # libdigikambridge.so 薄桥
├── digikam_patches/ # apply_ohos_patches.py + digikam_ohos_entry.cpp
├── build_kf5_new.sh / build_thirdparty.sh / build_digikam_ohos.sh
├── fix_needed.py # DT_NEEDED 版本名重写,部署前必跑
├── qt5_source/ # Qt 5.15.16 自建源码树
├── ohos_deps/ # 复用 AirKrita 的依赖与本项目新编库
└── qt_platform/ # libqohos QPA 插件源码
▍四、核心适配过程
### 1. 拓扑序 dlopen 与 Step 事件泵
napi_init.cpp 生成的 libdigikambridge.so 只链接系统库,真正的启动由 digikam_patches/digikam_ohos_entry.cpp 的 Digikam_Init 完成:按依赖拓扑序 dlopen 164 个 .so(RTLD_GLOBAL),创建 QApplication 与主窗口,再启动 RenderThread 执行 Digikam_Step——8ms 保底节拍加事件驱动唤醒,负责 sendWindowSystemEvents 与 processEvents。事件泵在 QApplication 创建后立即可用(踩坑 A12 的结论),否则 Init 期任何意外模态框都会因为拿不到输入而卡死启动链。
### 2. 幂等补丁系统与宏全局化
对 digiKam 上游源码的修改全部走 digikam_patches/apply_ohos_patches.py 的幂等锚点替换,P1 到 P12 共十余处:P1/P7 把 DFileDialog 的静态方法与实例式 exec 都短路到系统选择器;P2/P4 跳过 KXmlGui 的快捷键冲突检查;P3 把 SQLite BUSY_TIMEOUT 从 0 调到 10000ms;P5 修复相册树点击信号链;P9 把扫描控制器的嵌套 exec 改成分片等待;P12 抑制冷启动必弹的 No Fingerprints 信息框。工程曾因宏只定义在单个编译目标上导致 P1 静默失效,此后补丁系统在 add_subdirectory 之前全局注入 Q_OS_OHOS=1 OHOS_DIGIKAM_PORT=1,杜绝按目标漏定义的问题。
### 3. 窗口分层、输入路由与选择器桥
鸿蒙侧只有一块 XComponent 表面,Qt 的二级弹窗采用 overlay 矩形混合叠加:flush 按 g_windowRegistry 栈序合成 z-order,指针事件自顶向下做命中测试找到包含触点的可见顶层窗注入,Press 起隐式抓取整条序列,键盘投给最顶可见对话框。物理键盘经 OH_NativeXComponent_RegisterKeyEventCallback 在 NDK 层直收(ArkUI 焦点链不可靠),双击由 QPA 在 400ms/8px 窗口内合成。文件选择统一走 Digikam_OhosPickFile 嵌套循环等待 ArkTS 的 DocumentViewPicker 回传,保存路径由 native 守护线程轮询暂存文件、稳定 1.5 秒后触发回拷到用户选择的 URI。
### 4. 暗色主题、汉化与系统分享
applyForcedDarkTheme() 在 QApplication 创建后以 Fusion 风格套用 Breeze Dark 全角色配色,同时写 kdeglobals 的 ColorScheme 与 Icon 主题,ArkTS 壳三点钳制 COLOR_MODE_DARK,构成 Qt 与系统双通道暗黑。汉化方面,菜单标签经 fixupMenuLabel 修正个别条目并完成 digikam 到 AirKam 的去品牌,菜单树每 6 秒刷新一次以覆盖 DPlugin 延迟填充。分享能力由 Digikam_ShareExport 实现发送侧(选中图像拷出后经 TSFN 回 JSON,ArkTS 拉起系统分享面板),接收侧注册 harmonyShare.on('dataReceive') 与 sendData skills,分享来的文件落入相册库并触发后台扫描。
### 5. 按文件路径的重建流水线
改 digikam-master 源码后必须按文件路径 ninja 四个库(别名目标不可靠),四库以去版本单一名拷入 entry/libs/arm64-v8a/,llvm-strings 校验补丁标记真的进了库,再跑 deploy_fast.sh。改 qt5_source(例如 A36 的动画根治)后,增量重编 libQt5Widgets.so 并同样执行 fix_needed.py 重写 DT_NEEDED——这两条纪律分别来自踩坑 A22、A30、A35 与 A36 的真实事故。
▍五、适配中遇到的几个难点
### 难点一:嵌套事件循环锁死宿主泵
相册列表点击后永久卡死的现象,根因是 ScanController 的三处嵌套 eventLoop->exec() 在宿主泵互斥量临界区内无限期等待扫描完成。桌面 Qt 由 QPA 派发器驱动嵌套循环没有问题,而本移植的 RenderThread 抢不到互斥,事件投递与渲染全停。解法是 P9 补丁把嵌套 exec 改为分片等待:连接先于触发建立防止信号丢失,每 20ms processEvents 后主动让出宿主泵互斥再退避重取。配套的两个次生坑——未持锁时 unlock 破坏 libc++ 条件变量内部计数、让路后直接 lock 造成 Step 饿死——分别用标志守卫与 try_lock 退避解决。
### 难点二:跨线程信号静默丢弃
相册树点击后视图不切换,排查发现 signalAlbumCurrentChanged(QList<Album*>) 由 RenderThread 发射、宿主线程接收,而 QList<Album*> 未注册 metatype,Qt 跨线程排队投递直接静默丢弃,只留一条 QtWARN。Init 期三次选中正常是因为同线程直连,极具迷惑性。修复是 QApplication 创建后 qRegisterMetaType 补注册,同类隐患 QItemSelection 一并处理。这个案例的价值在于日志证据链:从 TREEHIT 命中、基类处理到信号零调用的每一步都有埋点实证。
### 难点三:窗口变化族的黑屏与黑边
最小化恢复、切后台回前台、熄屏唤醒后画面全黑,根因是 surface 销毁回调里停掉了 RenderThread 并触发引擎清理,恢复后新线程又因状态标志立即退出,再无人调用 sendWindowSystemEvents。修复后 surface 销毁不停车,重建时立即回注并武装 SURF-KICK 重绘爆发。随后的最大化黑边是互补根因:QPA 的 setGeometry 从未向 Qt 回报 handleGeometryChange,QResizeEvent 永不下发,内容钉死旧矩形。两处修复都在 MatePad Mini 与 MateBook Pro 上做了逐像素 diff 回归,恢复后与基线差异在噪声级。
### 难点四:PC 自由窗拖边缩放的线程亲和崩溃
MateBook Pro 上拖边缩放窗口触发 SIGSEGV,栈定位到 QWidgetAnimator 的动画对象跨线程销毁竞态:动画在 RenderThread 创建,属主却在宿主线程。第一层应用级 QProxyStyle 修复被 digiKam 启动期的 setStyle("Fusion") 覆盖而失效,最终下沉到 Qt 源码——QCommonStyle 的动画时长 hint 直接归零,qwidgetanimator.cpp 硬走一次性几何直通。修复后 18 次缩放(含 8 连发风暴)pid 稳定零 faultlog。同族的闪烁与黑帧问题分别由"尺寸变更不销毁 EGLSurface"与"flush 尺寸同步保留旧画面拉伸拷贝"解决,280 余次几何事件实测零 surface 重建。
### 难点五:musl 沙箱的库文件纪律
踩坑表里反复出现同一类事故:exiv2 安装产生的 libexiv2.so.27 符号链接在 musl 严格 NEEDED 语义下失效;digiKam 四库带版本名副本与单一名并存时,dlopen 命中孤儿副本导致 165 个 dplugin 全部加载失败;新编 Qt 库 DT_NEEDED 带版本号会让 QtHostThread 静默退出, ArkUI 壳照常显示但 XComponent 全黑。工程由此固化两条铁律:库文件只允许去版本单一名,任何新库入库前必跑 fix_needed.py。另外 QFile::copy 在 musl 加 CJK 路径组合下失败,全部拷贝逻辑替换为自研 ohosCopyFile(分块读写加尺寸校验)。
▍六、编译、安装与启动
Native 依赖链按顺序构建:build_kf5_new.sh 编 KF5 新增 7 模块,build_thirdparty.sh 编 exiv2 0.27.7、OpenCV 4.5.5、qtxmlpatterns 与 libqsqlite,build_digikam_ohos.sh 打补丁、配置、编译 libdigikam_core.so 与全部 dplugins 并收集部署,最后 hvigor 编薄桥与打包签名。
ArkTS 工程的标准构建命令:
bash
export JAVA_HOME=/Applications/DevEco-Studio.app/Contents/jbr/Contents/Home
export DEVECO_SDK_HOME=/Applications/DevEco-Studio.app/Contents/sdk
hvigorw --mode module -p module=entry assembleHap --no-daemon
签名产物位于 entry/build/default/outputs/default/entry-default-signed.hap(工程档案记载该 HAP 约 186 MB,含 164 个 native 库与数据文件)。连接设备后安装并启动:
bash
hdc install -r entry/build/default/outputs/default/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.airkam.app
日常改源码后的快速部署走 deploy_fast.sh,它内含 fix_needed.py 重写、HAP 打包与卸载重装。工程档案对 PC 部署的记载是:devecocli run --device 3QC0225C09000179 --skip-build 一次成功,随后完成了拖动、贴边、最大化、还原的完整窗口变化验证矩阵。
▍七、当前功能边界
已覆盖的能力,均有真机验证记录支撑:
- 可安装 HAP、双层架构启动链、164 库拓扑序加载与 Qt 主窗呈现;
- 相册树点击、缩略图网格、滚动、单选、双击进预览、预览翻页;
- 图像编辑器(82 个 Editor 插件注册加载)、灯桌、维护与配置对话框;
- 导入全流程走系统 DocumentViewPicker,相册真实缩略图生成;
- 系统分享面板发送(发送与接收两侧均真机实证)与碰一碰接收注册;
- 强制暗黑主题(Qt 调色板 + kdeglobals + ArkTS 三通道)、全中文界面(5737 条 zh_CN);
- 物理键盘、双击合成、指针 hover 光标桥;
- PC 自由窗口拖动、贴边、最大化、拖边缩放与最小化恢复。
尚未覆盖或处于观察项的能力:全屏幻灯片放映在单表面模型下不可用(P10 已做菜单与工具栏清场);Export 菜单对应的 webservices 导出插件未编译且端口刻意断网,分享面板承担导出职责;相似度搜索依赖的指纹从未生成,相关提示已被 P12 抑制;图标视图条目高度的 A24 问题在部分设备上降级观察;自动化无法触发的拖边 resize 仍待手工复验。工程对库文件单一名、日志 .txt 后缀、按路径构建等纪律性约束同样构成交付边界的一部分。
因此,当前版本更准确的定位是"在鸿蒙侧真实运行的全栈原生 digiKam 照片管理基础版":照片管理主链路(导入、浏览、查看、编辑、分享)已经完整可用,桌面特有能力按清单如实缺省。
▍八、总结
AirKam 的价值在于验证了重型 KDE 应用的完整移植路径可以复用:Qt5 + ohos QPA 双层架构、幂等补丁系统、拓扑序 dlopen 与 Step 泵,这套组合在 Krita 上成立之后,在 digiKam 这种依赖更复杂的场景里依然收敛。38 项踩坑记录、12 个源码补丁与三台设备的验证矩阵,把"编译通过"与"可交付"之间的距离拆成了可执行的工序。
后续工作的重心在工程档案中同样明确:A24 图标视图布局与 libunwind 转储路径两个遗留项待收口,遮挡回前台不自动重绘的零事件族已立项待修,拖边 resize 等自动化盲区需要手工复验补齐。每一次修复的验收标准不变——可构建的 HAP、真机上的实际操作、以及逐像素级的画面证据。
楼主
8 天前