决定用 DevEco CLI 这条命令行路线把一款鸿蒙 App 完整做出来,并且最终推到应用市场上架,这个想法最初来自一个很普通的场景:我家孩子每天放学后要完成的几件小事,刷牙、整理书包、看绘本、早睡,总得追着提醒,催多了孩子烦,我也累。于是我想不如干脆自己写一个日程打卡类的鸿蒙小应用,名字就叫「宝贝日程表」,让孩子自己对着手机完成任务打卡。真正动手之后才发现,在纯血鸿蒙的工程体系下,从命令行建工程、写 ArkTS 页面、真机调式、打包签名到提交审核上架,整个过程踩的坑比预想的多得多。这篇文章就把我完整复刻这条开发链路的过程记录下来,包括每一步的选型理由、命令细节、报错排查和上架前必须准备的材料,给正在做鸿蒙应用的朋友一个可以直接参考的实战样本。
1. 为什么一个日程表项目最终选择用 DevEco CLI 硬啃到底
这个项目最开始的计划很简单,在 DevEco Studio 里用可视化向导新建一个工程,拖拖拽拽写几个页面,打包出来安装到手机上就完事。但做到一半我发现,用图形界面建工程有两个问题让后续开发非常难受:一个是工程参数一旦生成后再去改包名、改签名配置,要通过好几层界面操作,容易漏;另一个是我习惯在多个设备间切换工作,甚至偶尔想直接扔到 CI 服务器上做自动化打包,这时候图形界面完全帮不上忙。所以这个项目起手的位置就不是 IDE,而是 DevEco CLI——也就是鸿蒙官方提供的命令行工具链。它能把工程初始化、依赖安装、编译构建、签名打包、安装调试这一整条链路都用命令驱动起来,整个过程可脚本化、可记录、可复现,也能和其他自动化流程串起来。
1.1 图形界面能做的事,命令行真正做到多少
先说结论,DevEco CLI 的能力覆盖度比大部分人的第一印象要强。它可以做这样几件事:创建完整工程、添加模块、安装 ohpm 依赖包、执行构建任务、管理签名信息,以及通过 hdc 工具安装应用到模拟器或真机。我这次实际用下来,发现日常开发里 90% 的工作都不需要打开 DevEco Studio 就能完成,真正需要回到 IDE 的场景只剩下两个:写 ArkTS 页面代码时想要实时的语法检查和 UI 预览,以及查看崩溃日志里比较复杂的调用栈。CLI 对这两块的体验确实不如 IDE,但项目代码本来就需要一个顺手趁手的编辑器来写,我用的是 VS Code 加 ArkTS 语言插件,勉强够用。
1.2 从空目录到 hap 产物的真实命令序列
DevEco CLI 在安装 DevEco Studio 之后就已经可以用了,命令行入口一般位于安装目录的 bin 目录下。新建一个项目的最简命令是devecocli create,后面跟上模板类型、包名和项目名称。我实际执行的是类似这样的命令:
devecocli create -t app -p com.example.babyschedule BabySchedule这里-t app表示创建一个标准的 Stage 模型应用工程,-p指定包名,最后是项目名。执行完之后目录结构会是 entry 模块加 AppScope 的标准形态。如果你是第一次用,可能会卡在模板选项上,因为 CLI 支持的模板比较多,常见的有app、library和server,分别对应应用、HAR 静态共享库和 Hsp 动态共享库。我的项目里主工程就是一个 app 模板,因为没打算拆成多包复用,后面也没有新增 library 模块的必要。
2. 宝贝日程表的工程骨架与数据模型设计
工程创建好之后,先别急着写页面,我建议先用十分钟把工程目录结构和配置文件的含义弄清楚,免得后面模块配置改来改去都不知道自己在改什么。Stage 模型下最重要的几个位置是 AppScope/app.json5、entry/src/main/module.json5,以及 entry/build-profile.json5,它们分别对应全局应用配置、模块级声明和构建签名相关配置。像应用名称、图标、版本号这类信息,如果直接在 IDE 里操作就是在 app.json5 和 module.json5 里来回改,用命令行的时候就只能手动编辑这些 JSON 文件了。
2.1 ArkTS 状态管理:项目里用到的 @State、@Prop、@Link、@Observed
宝贝日程表主要的功能是展示每日任务列表、记录打卡状态、切换日期查看历史记录。在 ArkTS 里最核心的就是状态驱动 UI 更新这套机制。我在项目里用到了四个装饰器:页面内部状态用的@State,父组件向子组件传值用@Prop,子组件反向操作父组件数据用@Link,跨组件共享那一份会被修改的对象则用@Observed配合@ObjectLink。实际经验是用@State的优先级最高,能用它解决的就别引入更复杂的联动关系,否则一个小页面传参链会把你绕晕。
代码里的任务模型大概是这样的:
@Observed export class TaskItem { id: number = 0; title: string = ''; isDone: boolean = false; targetTime: string = '20:00'; constructor(id: number, title: string, targetTime: string) { this.id = id; this.title = title; this.targetTime = targetTime; } }日期页签和任务列表组件之间的传值,就是典型的@State加@Prop组合。首页持有当前选中日期字符串,传给子组件做过滤展示,子组件只读不改。打卡按钮状态翻转则用@Link把父组件里的任务数组引用传下去,子组件里直接修改task.isDone,界面就会同步刷新。这套组合在 ArkTS 的 Stage 模型下是最高频的写法之一,理解了这四种装饰器,很多页面逻辑都能往这个模式上套。
2.2 日历核心算法:不依赖第三方库的纯本地实现
因为宝贝日程表本身不联网,也没接任何后端服务,日历数据完全由本地计算。月份天数、当月第一天是星期几、闰年判断这些逻辑都是自己写工具函数实现的。这类通用算法其实并不难,真正要注意的是跨月切换时状态的一致性。我封装了一个 CalendarUtil 工具类,输入年月返回当月日期网格二维数组,同时提供getTodayString()返回yyyy-MM-dd格式的字符串用于高亮今天。
最开始我踩过一个逻辑坑:把数组索引当成星期值用,导致日期偏移了一天。原因是Date.getDay()返回的星期日是 0,而我习惯把星期一排在第 0 列,两者对不上。后来统一在 CalendarUtil 里做了一次转换,把周日当成每列的最后一天处理,问题就解决了。类似这种边界值问题在写工具类的时候特别容易埋雷,建议写完立刻写测试用例验证,别偷懒。
2.3 数据持久化方案:Preferences 与关系型数据库的选择
任务清单和打卡记录都需要在 App 杀掉之后保留下来,所以必须做本地持久化。鸿蒙提供的数据持久化方案有两类比较常用:首选项 Preferences 适合存键值对,关系型数据库 RDB 适合存结构化表格数据。宝贝日程表里任务列表适合用 RDB,打卡记录也可以按日期存成一张表。不过我的需求实在不算复杂,总共就二三十条任务和每天对应的打卡状态,最后我选择了更轻量的一种方式:用 Preferences 存整个 JSON 字符串,日期字符串作为 key,对应当天的任务打卡状态数组就序列化存进去。
这样做的优势在于实现简单,启动时一次性读出来,内存里自己维护一份全量数据,写完就序列化回写,不用担心数据库版本迁移和 SQL 语句问题。缺点是数据量大了以后读写效率会变差,但日程表这种轻量工具类应用完全在承受范围内。如果你做的是数据量较大的应用,我建议还是要认真用 RDB,把表和索引的设计做在前面。
3. 真机调试与模拟器体验:从 hdc 到 DevEco Studio 的拉扯
开发阶段最影响幸福感的环节就是调试。鸿蒙提供的调试方式有三种:本地模拟器、远程模拟器和真机。这个项目里我三种都试过,踩了不少坑,尤其是模拟器在启动速度和架构一致性上的表现会直接影响你排查问题的效率。
3.1 模拟器与真机差异:手势、定位、后台行为
模拟器最大的优势是启动方便,尤其是看 UI 布局的时候。但宝贝日程表有这么几个问题在模拟器上很难暴露:第一是通知权限弹窗时机,模拟器里系统服务不太一样,弹窗表现和真机有偏差;第二是电池优化和后台运行限制,真机上如果用户把 App 加入了省电白名单或者限制后台,定时提醒这类功能可能就直接失效了,这个问题在模拟器上基本复现不出来;第三是触摸反馈,儿童用的 App 按钮一般都做得比较大,但手指在真机上的滑动摩擦感和模拟器鼠标操作完全是两回事。所以我的结论是:模拟器适合看 UI,真机才是排查功能和性能问题的唯一标准。
3.2 USB 调试授权那个“坑”和 hdc 常用命令
真机调试之前必须在开发者选项里打开 USB 调试,这个大家应该都知道了。但有一个坑可能第一次接触的人都会遇到:手机插上数据线后,执行hdc list targets能看到设备,可一旦执行hdc install却提示 fail,而且不报任何具体原因。排查到最后发现是手机上的调试授权弹窗没有点掉,USB 连接后系统会弹出“允许 USB 调试吗”的确认框,不点允许或者那次误点了“仅充电”模式,后续所有调试命令都会失败。
hdc 的常用命令我列一下,方便直接抄:
hdc list targets # 查看当前连接的设备 hdc install path/to/app.hap # 安装应用 hdc uninstall com.example.babyschedule # 卸载应用 hdc shell aa start -b com.example.babyschedule -a EntryAbility # 启动应用 hdc file send local remote # 推送文件到设备 hdc hilog # 查看设备日志3.3 日志排查:hilog 里我最后悔没早做的事
应用崩溃或者逻辑不对的时候,我习惯先在 hilog 里过滤关键词来找线索。最开始我是直接hdc hilog全量输出,结果日志刷屏刷得根本没法看。后来才学会先hdc shell hilog -x清除旧日志,再带| grep去过滤自己应用的 tag。鸿蒙的日志分级和 Android 不太一样,常见的有 DEBUG、INFO、WARN、ERROR 和 FATAL。我自己的排查习惯是先看 FATAL 再往上翻 ERROR,并且优先处理带ArkTS前缀的报错,因为 ArkTS 层抛出的异常往往直接指向代码里出错的那一行。
4. 打包与签名:hap、hsp、har 三种产物到底怎么选
开发调试阶段用的是自动签名,也就是 DevEco Studio 自动帮你生成调试证书,安装到测试机上没问题。但准备上架时必须手动处理正式签名,还要理解鸿蒙工程构建出来的几种产物格式,项目本身越复杂,这一步越绕不过去。
4.1 hap、hsp、har 的区别和适用场景
先说最核心的概念。HAP 是应用安装包,也就是最终要装到用户设备上的东西,一个 App 可以包含一个或多个 HAP 模块。HAR 是静态共享库,可以理解成传统意义上的"库",里面的代码和资源会被打包进引用它的 HAP 中,一般用来抽取公共组件和工具方法。HSP 是动态共享库,它不会打进 HAP 里,而是等 App 运行到需要时再加载,适合做按需加载的模块或独立大功能包。
我这个项目的规模不大,结构上就只有一个 entry 模块,所以最终产物就是一个单独的 HAP。但如果你在做一个多模块应用,比如首页、商城、设置各自想分开构建,那就得考虑 HAP 分包或者 HSP 的动态加载了。我建议这样选择:纯工具类代码抽成 HAR,业务量较大且需要独立迭代的模块用 HSP,主入口保持轻量,目录清晰,可维护性也高。
4.2 从调试签名到正式签名:证书、Profile、密钥库三件套
正式签名需要三样东西:证书文件(.cer)、Profile 文件(.p7b)和密钥库文件(.p12)。这三样需要在鸿蒙应用生态平台申请和生成。证书的逻辑是先用 KeyStore Explorer 或者命令行生成密钥对,上传公钥信息获取证书,然后把证书和 App 信息关联生成 Profile,Profile 里记录了这个包能使用的权限、能安装的设备范围等等。
拿到三件套之后,要在 entry/build-profile.json5 里的 signingConfigs 节点里配好。这里有一个非常容易忽略的点:签名信息的name必须和证书文件里的信息保持一致,否则构建时直接报签名校验失败。我在测试流程里就因为签名 name 写错了,反反复复去检查 keystore 密码和别名,浪费了两个小时。后来学乖了,每次从命令行打包之前统一跑一遍构建脚本,签名的流程完全由脚本接管,基本不会再出现手滑填错配置的情况。
4.3 自动化构建脚本:让 hap 生成只在一行命令之间
日常开发时可以在 IDE 里点按钮构建,但到了准备 Release 包的时候我强烈推荐用命令行。鸿蒙工程的命令行构建入口是项目根目录下的 hvigorw 脚本,配合 Deveco CLI 一起用,只需要执行:
./hvigorw assembleHap --mode module -p product=default -p buildMode=release第一次跑这个命令之前要先装好依赖,ohpm install需要在工程根目录执行一次,对应的是 HarmonyOS 的包管理工具。构建成功后 hap 会输出到entry/build/default/outputs/default/下。我写了一个简单的 shell 脚本把这些命令串起来,每次提交版本只需要改版本号然后执行一次,既省时间又保证每次都走同样的构建逻辑。这里建议大家把生成的签名参数从命令行传入,而不要平铺写在配置里,这样可以把密钥文件单独保护起来,不会因为工程提交到仓库导致签名信息泄露。
5. 上架前的最后准备:版本审核自查清单与可能要踩的坑
应用开发到功能完整了,不代表就能顺利上架。上架前有大量以"审核视角"来审视应用的琐碎工作。审核过程很在意权限申请的合理性、隐私政策的完整性以及应用本身是否包含违规内容。宝贝日程表这个项目因为不涉及用户账号体系,不采集任何个人信息,隐私风险相对较低,但反而有一个坑比较隐蔽:儿童类应用的年龄分级和说明。
5.1 年龄分级与隐私政策:儿童应用躲不开的合规话题
鸿蒙应用市场要求开发者在上架时提供详细的年龄分级信息,如果应用是面向儿童的,审核会特别关注是否包含广告、是否有外部链接、是否包含社交功能、是否含有内购。宝贝日程表是做儿童习惯养成的工具,我老老实实选择了"全年龄"级别,同时在隐私政策里明确写了"应用不收集任何个人信息、不需要网络权限"。这一步看似无关紧要,但审核人员真的会逐条核对,你在权限声明里多申请了一个用不上的权限,就多一分被拒的风险。
配套材料方面还需要准备应用图标、不同尺寸的宣传图、4 到 8 张功能截图、一句话简介和较长的应用描述。截图要真实反映界面,Android 上架的开发者都懂那种"截图和实际进入后长得不一样"会被驳回的痛,鸿蒙这边审核尺度只会更严,所以我没有做任何虚化或拼接操作,直接截真机画面提交。
5.2 版本与权限声明最容易忽略的三个细节
第一是版本号不能乱填,主版本号、次版本号和修订号的递增规则要符合平台规范,不能出现上个版本是 1.0.0,下个版本直接跳到 1.2.0 这种不合理跳变。第二是权限列表需要和代码实际申请保持一致,比如我一开始在 module.json5 里声明了位置权限,但代码根本没用,后来排查排掉了。第三是应用市场里填写的"应用所支持的设备范围",要和自己测试过的设备型号对照填写,别图省事直接全选,万一用户设备上跑不起来,投诉率高了会影响账号信誉。
5.3 从测试版到正式上架的整体时间线参考
如果准备顺利,提交审核到最后通过通常需要一到三个工作日。我第一次提交的时候因为隐私政策链接填写不规范被打回了一次,改完之后重新提审又等了大半天。所以我的建议是把上架准备细化成一份清单,提前检查齐全再点提交,不要抱有"先提交试试、被打回再改"的心态,一次过对你账号后续的权限有好处。
6. 后续优化方向与对这个技术栈的几点真实感受
宝贝日程表上架之后,我后续计划做三个方向的小迭代:一个是把通知提醒真正做好,鸿蒙系统里的通知渠道需要在通知管理里注册并让用户授权,这块的体验直接决定这类工具应用的用户留存;另一个是桌面卡片,鸿蒙的桌面卡片框架可以把今天的任务列表直接展现在桌面上,长按就能打卡,这种“免打开”的轻交互非常契合宝贝日程表的使用场景;第三个是给打卡数据做周统计和月统计,画个简单的趋势图,让家长能够在每周日晚上清晰看到孩子这一周的执行情况。
最后聊一点个人真实感受。鸿蒙的开发工具链这两年进步确实非常明显,但从命令行角度去用它的生态还不算特别完善,很多资料分散在官方文档和论坛里,碰到报错想搜一个现成的答案并不容易。但换个角度看,正因为如此,把这个技术栈吃透之后能建立很深的护城河——市面上真正能从devecocli create一路写到hvigorw assembleHap再完成上架的人还不多,而这套全链路能力恰恰是跨团队做工程化建设最需要的。做宝贝日程表这半个多月,我的收获不只是一款应用,更是把自己从 IDE 图形化操作的舒适区里拽出来,重新理解了鸿蒙工程从源码到交付的每一个环节。