前几天在安卓上对小组件的开发遇到一些阻碍,突然想到之前华为鸿蒙号称其类似的技术——称作卡片服务的——非常强大,就想看一看了解一下。本次对鸿蒙系统的开发实践,完全是为了验证一个技术点的可行性,并不是系统性地上手的开始。因此本文仅是一个备忘的记录,并不是向读者特意说明什么。
一开始是在老夫那台老旧的 MacBook Pro 2015 上进行的。首先是去官网下载 DevEco Studio 开发环境。体验很差,下载下来的是一个 zip 包,3GB 出头。双击解压,释放出来了 dmg 安装映像,装载映像后再把应用拖放至系统的应用程序文件夹中,这一来二去,对磁盘剩余空间的要求可不低。dmg 本身就有校验机制,完全不知道 zip 那一层是什么目的。安装后在官网的开发文档下寻找卡片开发的示例代码,找到了这个,由于是托管在 Gitee 上,花了一些时间回忆老夫在上面的账号信息。成功下载示例,用 IDE 打开,计划在模拟器里跑一下看,结果令人崩溃的来了:没有模拟器可用。倒不是说鸿蒙开发本身完全不支持模拟器,而是老夫的 MacBook Pro 不符合要求。对于一台 x64 架构的 MacBook Pro,华为给出的可选项是,要么运行 Windows 系统,要么是 ARM 架构的 macOS。无奈,只好换到另一台 Windows 老笔记本上重头再来。
使用 IDE 的向导打开示例工程看了看,感觉它演示的内容比想象的复杂,精简的话,面对根本不了解整个工程结构以及 TypeScript 语法的老夫来说,不是首选方向。于是选择了 DeepSeek,希望它能引导着从 0 实现一个最简单的卡片。最简单的目标有二:一是能把卡片创建到桌面上,二是点击之后能执行进入相应的处理代码中。之所以没有敢从国外敌对势力的 AI 进行,完全是怕它们蠢笨到根本不了解伟大的鸿蒙最新的革命性进展,难免令老夫误入歧途。
然而极可能由于鸿蒙系统过于先进的原因,导致相关信息的保密性也大大提升,所以 DeepSeek 虽然兢兢业业尽心尽责,却在沟通过程中一而再再而三地给出过失的项目组织结构,或者 API 调用的示例代码,复制到 IDE 里就会引发群起的红色警告。还好几个回合下来,老夫渐渐摸到了一些门道,TypeScript 的语法跟 Java 也堪堪可以比对起来稍有理解了,最后还是能构建成功了。悲催的是,人家官方示例的应用图标长按后,关联菜单上可以显示出有“卡片”的入口,而老夫的则不行。后来了解到,IDE 本身有向导可以引导一个卡片的创建过程,在工程树中右键,从关联菜单里选择“New->Service Widget”,其下有静态和动态两种 widget 可选。出于“最简”的原则,显然先弄个静态的试试。这样一来,工程里其实就拥有了老夫手工打造和 IDE 辅助打造的两个 widget。再次运行,发现 IDE 后加的 widget 出现在点选“卡片”入口后的待选列表里,老夫的手工打造的那个依旧无影踪。反复对比,最后发现了一个“只可能是它”的差异点:老夫在设置里指定的 widget 大小为 1×1,而不管是官方示例工程,还是 IDE 向导辅助创建成功的 widget,它们的布局大小都超过了这个尺寸。为了验证,当把 IDE 辅助创建的 widget 的大小设置为 1×1 后,果然它也消失了。这个怪异的限制,没有在任何文档中体现,只不过在向导界面里有所暗示,因为它让你可以选择的布局大小中,没有 1×1 这个选项。
前文提到,DeepSeek 给出的代码一般都不能正常通过编译。在老夫看来,它其实并不是有意的,只不过它所掌握的知识过于陈旧了而已。新加一个 widget 的话,首先是要在工程中增加两个配置项,一处是 src\main\resources\base\profile\form_config.json
,在其中的 forms
节点下增加 widget 的基本描述信息,另一处是在 src\main\module.json5
中的 extensionAbilities
节点下增加对应的用于处理 widget 事件的 ability(应该派生于 FormExtensionAbility
)的信息,这里会与前面的 form_config.json
关联起来。
此外当然就是代码。widget 自身的代码在 src\main\ets
下,看 IDE 的安排,是要先创建一个以 widget 名字命名的目录,其下再创建 pages
目录,再在其中创建 form_config.json
中指定文件名的 ets 源代码文件。在此源码文件中的同名类(但关键字使用的是添加了 @Entry
和 @Component
注解过的 struct
而不是 class
)中,实现 build()
函数。
在这个函数中,执行声明式布局的组合。向其中的控件上增加常规的 onClick
事件处理器是无效行为(别问我怎么知道的),而似乎只能执行 postCardAction
。究其原因,想必跟 Android 相似,这些布局最终生效是在远程,而非本 app 的进程内。除了为每一个要执行工作的控件一一指定执行 postCardAction
的 onClick
事件处理器之外,如果有一组相似的控件,例如按钮,还可以借助 FormLink
来完成,FormLink
的参数是一个如下结构:
abilityName
指定了接收卡片上产生的事件的 ability 的名字,message
可以用来区分事件的名称。用一个 message
来区分事件名称看上去比较 low,但官方示例工程里就是这么干的。事实上,postCardAction
的第二个参数就是一个 FormLink
。
那么,指定的 ability 如何才能处理卡片发来的动作请求?那就需要处理 onNewWant
了。例如,
老夫上述代码是想打开系统的设置界面,在模拟器上运行时并未出错,也输出了成功的日志,但是系统的设置界面并未打开。不知道真机上的效果会是如何。
另外,鸿蒙系统对应的 adb 工具叫 hdc(鸿蒙设备连接器),对应 adb 的 dumpsys
命令的命令是 hidumper
,管理 ability 的命令是 aa
。
实践得草率,总结得仓促,就这么着吧。