
开头稍微有点长先把自己放进场景里如果你跟我一样既想给自家孩子做一款真正能用的日程管理工具又想借着鸿蒙这波浪潮练手那么“用命令行跑通一个鸿蒙 App 从开发到上架”这件事绝对值得认真做一遍。我这次做的「宝贝日程表」就是这样一个项目按家长视角拆需求用 DevEco CLI 从空目录初始化工程用 ArkTS 写界面和业务逻辑最后打包成 HAP 提交到华为应用市场。整个过程没有靠 IDE 的图形向导一步步点而是尽量用 hvigor、ohpm 这些命令行工具链完成好处是逻辑清晰、可复现后期接 CI 也顺手。这篇文章会把我从立项、开发、签名打包到送审上架踩过的坑和沉淀下来的方法完整展开适合正在学鸿蒙开发、或者准备做正式上架产品的人参考。1. 项目定位为什么是「宝贝日程表」为什么选 DevEco CLI1.1 需求拆解与产品边界「宝贝日程表」这个名字听起来很家常但背后是一个很典型的效率工具需求家长需要给孩子安排起床、吃饭、学习、运动、睡眠等固定事项同时记录实际完成情况慢慢帮孩子养成习惯。市面上的儿童日程 App 不少但普遍有两个问题一是广告和付费墙太多二是数据模型过于复杂孩子自己看不懂家长每次都得替孩子操作。所以我在拆需求时给自己定了三条硬约束。第一界面要足够大按钮要足够大核心操作必须能在 3 步以内完成因为使用者很可能是四五岁的小朋友。第二数据要本地化优先不强制注册账号不搞云同步避免个人信息合规上的麻烦。第三所有功能必须离线可用早教场景里经常没有稳定的网络。基于这些约束我把「宝贝日程表」的产品边界锁定为三个模块日程模板管理、每日打卡记录、完成情况看板不做社交、不做消息推送、不做付费订阅。模块一日程模板管理。家长可以按周一至周日分别设置时间块比如“7:30 起床”“8:00 吃早饭”“16:00 户外活动”每个时间块可以选一个 emoji 图标和颜色方便不识字的低龄儿童识别。模块二每日打卡记录。当天到了某个时间点孩子或者家长在首页点一下对应卡片就完成一次打卡后台会记录实际打卡时间戳。模块三完成情况看板。按周维度展示每个事项的完成次数和完成率用最简单的柱状图呈现家长能快速知道本周哪些习惯执行得好哪些需要调整。1.2 DevEco CLI 是什么我为什么没用纯 IDE 向导很多初学者接触鸿蒙开发第一反应是打开 DevEco Studio 点“New Project”然后一路 Next。这个流程没有错但如果你打算把工程交给 Git 管理、在 CI 上自动构建、甚至让团队成员用不同系统环境协作纯 IDE 操作就不够透明了。DevEco CLI 并不是指某一个单独的命令而是 DevEco Studio 配套命令行工具链的统称核心包括 hvigor构建引擎类似 Gradle、ohpm包管理器类似 npm 管理三方库、SDK Manager管理 API 版本和 SDK 组件以及签名、打包相关的工具。我用命令行方式最核心的原因是可以把整个构建过程写进脚本任何一步出错都能从日志里看到具体是哪个配置项的问题不用反复在 IDE 界面里寻找菜单。比如初始化空工程我只用一条命令创建目录结构然后手动维护build-profile.json5和module.json5这样我对每一个字段的作用都非常清楚。后面接自动化打包的时候只需要按顺序执行“更新版本号 - 编译 HAP - 签名 - 校验产物”这几条命令。而且 DevEco Studio 本身就是建立在同样的命令行工具之上所以最终产物质量和你用 IDE 构建是完全一致的。2. 从空目录到第一个 HAP环境搭建与脚手架2.1 工具链全景Studio、hvigor、ohpm 各自负责什么在开始之前先理清工具链的层级关系。DevEco Studio 是 IDE它的底层是 DevEco 命令行工具包包含 SDK、hvigor 和 ohpm。hvigor 是鸿蒙的构建工具读取build-profile.json5、oh-package.json5和模块里的module.json5负责编译资源、生成中间产物、最终打出 HAP。ohpm 则是三方库管理器类似 npm负责拉取类似于ohos/axios、路由库、UI 组件库等依赖并把它们链接到工程里。还有一个容易被忽略的是 SDK Manager鸿蒙 SDK 分为Default、HarmonyOS NEXT等多个版本你可以用命令行查看和管理已安装的 API 版本。这一层搞清楚以后构建的整个脉络就通了。你写的是 ArkTS 源码和资源文件ohpm 负责把依赖装进本地oh_moduleshvigor 拿着 SDK 的编译器做类型检查和编译最后输出 HAP。如果你还配了混淆hvigor 也会在编译阶段做代码压缩混淆。2.2 创建工程与最小可运行版本我用命令行从零创建工程的过程是这样的。先新建一个空目录比如baby-schedule在里面创建oh-package.json5作为工程级配置。这个文件类似根package.json里面声明依赖和工程信息比如modelVersion: 5.0.0表示使用 Stage 模型 5.0 版本。然后创建build-profile.json5指定app的signingConfigs、products以及模块列表。模块是我们真正写代码的地方。在entry/src/main下面我手动创建了几个关键文件module.json5模块配置声明入口 Ability、权限、支持的设备类型。ets/entryability/EntryAbility.ets应用入口 Ability负责加载页面并管理生命周期。ets/pages/Index.ets首页也就是日程列表页。resources/base/profile/main_pages.json页面路由表声明所有页面路径。resources/base/element/string.json字符串资源。这些文件建好之后执行ohpm install安装基础依赖再执行hvigorw assembleHap --mode module -p productdefault就能打出第一个 HAP。第一次跑构建的时候大概率会遇到几个小问题最常见的两个一是hvigorw找不到那是因为你还没把 DevEco Studio 里自带的hvigor脚本路径加入 PATH或者在工程根目录缺少hvigorfile.ts二是oh-package.json5里没有声明ohos/hypium之类的框架依赖测试相关模块编译不过。这些都属于环境问题按日志提示补全即可。注意鸿蒙工程的module.json5里deviceTypes不能乱写比如只列了phone却想在平板上运行就会出现安装失败。我建议一开始就填[phone, tablet, 2in1]这样三端都能装虽然要额外留意 UI 适配但从长期看省事。2.3 目录结构里的“雷区”没被 ide 自动生成的坑用命令行建工程有一件事和 IDE 向导差别很大IDE 会自动帮你生成EntryAbility的注册、路由表映射、还有各种资源引用但手动建工程时这些都要自己检查一遍。最常见的坑是你们在main_pages.json里写了某个页面路径但实际对应的.ets文件不存在编译会报“找不到页面”反过来你把页面文件放在pages/下面但没在main_pages.json注册编译不会报错但运行时跳转会黑屏。所以每新增一个页面我习惯立刻把它加进路由表并跑一次编译而不是攒在一起改。另一个容易忽略的是资源目录。ArkTS 里引用字符串资源用$r(app.string.xxx)如果你在string.json里删除了某个键但代码还在用这个不会在编译期报错而是运行到该页面时白屏或异常。为此我专门养成了一个习惯资源索引的增删一定和代码改动同步并且在提交前全局搜一遍$r(引用。做命令行开发没有 IDE 的实时错误提示这些自检动作必须养成肌肉记忆。3. 核心功能开发ArkTS 写界面与状态管理3.1 Stage 模型与 ArkUI 页面结构鸿蒙应用从 HarmonyOS NEXT 开始全面推行 Stage 模型和旧的 FA 模型相比最大的变化是模块化更清晰一个应用可以有多个 Module每个 Module 可以包含多个UIAbility和页面。我用 Stage 模型的思路组织「宝贝日程表」实际上就是让 UI 和业务逻辑解耦。首页Index.ets的结构我设计成上下两个区域。顶部是日期切换栏左右箭头切换当周中间显示“x月x日 周x”底部用一条分割线下方是日程卡片列表List组件里面嵌套ListItem每张卡片显示时间、图标、标题、完成状态以及一个大大的打卡按钮。为了让孩子能看懂我把整个页面背景色做成淡黄色卡片做成圆角白底未完成事项的按钮是灰色点击后变成绿色并把“打卡”换成“已完成”。在 ArkUI 里实现这整套布局大概只需要Column、Row、List这几个核心容器代码量不大但状态变化比较多。3.2 状态管理State、Prop、Link、AppStorage 怎么选ArkTS 基于 ArkUI 的状态管理机制理解这一块基本就理解了整个前端开发模型。我写了一个日程项组件ScheduleItem父页面持有当前选中星期的日期列表数据子组件负责展示单条日程。这里面用到了几个关键装饰器State父页面持有数组比如State scheduleList: ScheduleModel[]当数组内容变化时UI 自动刷新。Prop子组件接收父组件传入的单一值比如Prop item: ScheduleModel但要注意Prop是单向同步子组件修改它不会同步到父组件。Link如果子组件需要修改父组件的数据就要用Link做双向绑定。我最开始把打卡按钮的点击事件写在子组件里直接改Prop的字段结果发现父页面视图不刷新查了半天文档才想起来要改成Link。AppStorage跨页面共享的全局存储我在首页点击打卡后需要让统计页立刻感知数据变化就把当前累计打卡数放进了AppStorage统计页StorageProp接收后自动刷新图表。简单地说一个页面内部用自己的State父子之间用Prop单向传值、Link双向联动跨页面共享用AppStorage。这个选择模型在小型项目里完全够用不需要引入额外状态管理库代码也好维护。3.3 数据持久化Preferences 还是关系型数据库日程打卡类 App 对持久化的要求其实比想象中高。需要保存的数据有两类一类是“日程模板”数量少、结构固定比如每周每天的固定事项另一类是“打卡记录”会随着时间增长越来越多而且需要按日期、按事项维度做统计。我在第一个版本里只用了ohos.data.preferencesPreferences它是一个类似键值对的轻量存储存日程模板绰绰有余。但到了上统计功能的时候发现打卡记录用 Preferences 存非常痛苦要统计“这周星期一早上 8 点的事项完成了没有”我得把所有记录遍历一遍边遍历边解析 JSON。后来我改成了关系型数据库 RDBRelational Database用ohos.data.relationalStore建了两张表一张schedule_template一张checkin_record。建表语句我写在了一个独立的database.ets里用CREATE TABLE IF NOT EXISTS保证重复执行不报错。打卡时插入一条记录统计时用SELECT COUNT(*) WHERE schedule_id? AND date BETWEEN ? AND ?就能拿到完成数查询效率比遍历 Preferences 高太多。所以我的建议是固定配置、用户偏好用 Preferences凡是会增长、需要筛选统计的数据直接上 RDB不要图省事。注意RDB 的使用要处理好数据库实例的获取时机。在 Ability 的onCreate里初始化数据库连接然后用单例管理RdbStore避免每次页面访问都重新打开。还有所有数据库操作默认是异步接口如果直接await放在aboutToAppear里要注意页面可能先渲染再等数据最好做一个 Loading 状态。3.4 权限配置与隐私合规儿童类 App 要格外小心「宝贝日程表」功能简单理论上不需要太多权限。但我遇到过很多同行在这里翻车原因是觉得自己没申请敏感权限就不需要写权限说明结果上架审核时被要求补充“权限使用说明”。我的做法是在module.json5里最小化声明只保留了ohos.permission.INTERNET因为需要反馈页面报错日志和ohos.permission.STORE_PERSISTENT_DATA持久化存储相关。同时在 App 内设置页增加一个“隐私政策”入口把采集什么、不采集什么、数据存哪里写清楚。儿童类应用还有额外的要求不能诱导儿童点击广告、不能收集儿童个人信息、必须有家长控制的内容。我直接在设置页加了“家长锁”进入设置和统计数据查看前需要完成一个简单的两位数乘法验证这样既能保护儿童不误触也向审核人员展示了产品在儿童隐私上的谨慎态度。上架填写年龄分级时我选择了“儿童适宜”对应的资料要求也更严但通过了之后应用商店会给产品一个很好的信任背书。4. 打包与签名从 HAP 到可安装交付4.1 HAP、HSP、HAR 到底怎么选做鸿蒙开发工程产物有 HAP、HSP、HAR 三种很多新手容易搞混。简单区分一下HAPHarmonyOS Ability Package是应用最终安装包一个应用由一个或多个 HAP 组成入口模块的 HAP 是必须的。HARHarmonyOS Archive是静态共享包编译时会把代码和资源打包到 HAP 里类似 Android 的 AAR 或者前端里的本地依赖。HSPHarmonyOS Shared Package是动态共享包运行时由系统加载多个 HAP 可以共用减少重复代码体积。「宝贝日程表」早期是一个单 HAP 工程把工具函数、网络请求、数据库封装全放在entry里。后来为了结构清晰我把通用的日期处理、颜色主题、数据库 helper 抽成了一个独立的commonHAR 模块作为oh-package.json5里的本地依赖挂进工程。这样做的直接好处是后续如果我再做第二个 App可以直接复用这个 HAR不用复制粘贴代码。至于要不要做 HSP要看场景。如果你的应用有独立的大体积功能模块比如视频播放、AI 模型下载可以考虑拆成 HSP 并在需要时动态加载。对「宝贝日程表」这种轻量工具类应用单 HAP 一个公共 HAR 已经足够过度拆分反而会增加管理和调试成本。4.2 签名证书的完整链路p12、cer、p7b、profile 到底是谁签名是上架前最容易卡住的地方很多人分不清.p12、.cer、.p7b和.profile的关系。我用一句话总结.p12是你的私钥和公钥证书文件类似于你的“数字身份”里面包含私钥绝对不能泄露.cer是华为开发者证书证明你是合法的开发者由 AGC 签发.p7b是证书链文件是一个包裹多个证书的容器.profileProvisioning Profile则是一个授权文件里面声明了你这个应用包名能用哪些证书签名、能在哪些设备上安装。开发阶段可以用 DevEco Studio 的自动签名功能一键申请。但如果你像我一样主要用命令行可以在 AGC 后台手动创建证书和应用签名然后把signingConfigs写进build-profile.json5。命令行签名时本质上就是调用签名工具把私钥和 profile 绑定到 HAP 上生成签名后的包。我建议把.p12密码写进本地的环境变量而不是直接写死在build-profile.json5里否则工程一旦开源私钥泄露等于身份被盗用。我这里踩过一次大坑在 AGC 后台创建应用时包名填的是com.example.babyschedule但工程module.json5里的bundleName写成了com.example.babyscheduler结果签名工具一直报“signature verification failed”排查了很久才发现是包名不匹配。签名链路上一个字母都不能差。4.3 多设备适配与屏幕适配鸿蒙系统的设备形态很多手机、折叠屏、平板甚至车机、手表同一个 HAP 要尽量在目标设备上都有好的体验。「宝贝日程表」一开始主要在手机上跑后来我在折叠屏预览器上看了下发现页面被拉得很宽卡片间距也不协调。后来我加入了 GridRow/GridCol 响应式布局把内容区域在宽屏下限制为最大 600vp 居中两侧留白看起来就舒服很多。字体和图标也需要注意。华为手机默认字体大小可能被用户调得很大如果你的 UI 固定写死字号就会导致文字溢出。我给首页日期标题设置的是fp单位并且最小字号限制为 14fp最大 20fp配合maxLines和textOverflow做兜底确保极端字体下也不会乱掉。这套适配做得越早后面上架审核被退回“界面显示异常”的风险就越小。4.4 编译过了但运行时崩的三个典型原因命令行编译只要报“BUILD SUCCESSFUL”只能说明语法和资源引用没问题运行时崩溃往往源于配置或生命周期问题。我在这类小应用上遇到过三个典型原因。第一页面没有注册路由表。新增统计页Statistics.ets后忘记加入main_pages.json编译正常但点击入口时页面直接崩掉日志里会显示router.pushUrl找不到目标。第二aboutToAppear里异步操作没处理好。我在这个生命周期里去查数据库结果拿到数据时组件已经销毁了赋值给State就报错。后来我用if (this.isPageActive)这种标志位做保护。第三在子组件里直接修改Prop对象属性。前面说过Prop是单向的虽然编译期不会报错但行为不符合预期看起来像“数据改了 UI 没刷新”实际是数据只在子组件内部改了父组件没感知。这些崩溃在 IDE 里其实都有比较明确的日志但如果你只在命令行构建没有 DevEco Studio 的调试器就要学会在关键生命周期打日志用hilog.info输出关键变量暴力定位问题。5. 正式上架AGC 后台与送审清单5.1 上架前需要准备哪些材料当你的 HAP 在真机上跑得足够稳定把“上架”提上日程时首先要意识到上架不只是传一个包那么简单。我整理了一个材料清单照着准备就不会漏开发者账号在 AppGallery Connect 完成企业或个人实名认证。个人开发者可以上架但应用市场对个人应用会有一些额外的审核询问。隐私政策必须有一个可访问的 URL。我用的是腾讯云的一个静态页面里面详细写了这个 App 不采集个人信息、数据仅存储在本地、不含第三方广告 SDK。用户协议可选但强烈建议说明应用功能与使用规则。应用图标与宣传图要求 PNG/JPG 格式尺寸至少 512x512宣传图需要和真实 UI 尽量一致。版本说明简要描述新版本功能和更新点。测试账号如果应用有登录功能需要提供测试账号给审核人员「宝贝日程表」没有登录所以我额外强调了“无需账号即可体验全部功能”。儿童类 App 还要额外准备“儿童隐私保护声明”在年龄分级选择“儿童”之后系统会强制要求上传。我建议在开发期就把这个声明写好而不是等审核被拒再补。5.2 在 AGC 创建应用和上传构建包在 AGC 后台的流程其实很清晰创建项目 - 创建应用 - 填写包名和基础信息 - 配置签名证书 - 上传 HAP - 填写版本信息 - 提交审核。这里的核心问题是包名和签名必须和本地一致。包名就是你工程里的bundleNameAGC 创建应用时一旦生成不可修改。签名证书方面你可以回到“用户与访问”里创建一个应用签名证书证书指纹要和你本地.cer里的指纹一致。如果指纹不一致上传 HAP 时会出现“证书不匹配”的错误。上传 HAP 时AGC 会让你选择支持的设备类型这里建议和module.json5里的deviceTypes保持一致。然后填写版本说明和上架截图。截图这块我有个教训第一次送审我偷懒用了模拟器截图结果被以“界面截图与真实设备存在差异”退回。后来我改用真机截图宽度、状态栏、刘海屏适配都跟真实用户看到的一致一次就过。不要低估审核人员的严谨程度。5.3 审核被退回的常见理由和应对方式从我自己的经历和身边朋友的反馈鸿蒙应用市场审核被退回的高频理由大概是这几类隐私政策链接无效或内容与实际权限不符。权限申请理由不充分比如一个日历应用申请位置权限。应用内出现“测试”“内测”等字眼或存在明显的体验问题。截图与应用实机效果不一致。应用图标不符合设计规范比如背景透明导致图标一片黑。我正式提交之前做了一次完整的自测清单把 HAP 重新签名安装到一台完全没装过的手机上从桌面图标点进去走一遍所有页面确认无账号也能正常使用核心功能截图务必是这台真机在同一版本上的截图。然后才在 AGC 上提交。第一版审核用了大概两个工作日就通过了我自己写的一个小工具类应用能达到这个效率说明只要前期把合规和体验做扎实没必要怕审核。6. 避坑记录一套命令行开发者的常见问题速查6.1 构建与工具链问题速查我在整个开发过程中遇到的问题不少这里按类别整理成表格方便你直接检索。问题现象大概率原因解决办法hvigorw: command not foundhvigor 脚手架脚本未安装或不在 PATH在 DevEco Studio 的安装目录下找到 hvigor或执行本地脚本./hvigorwohpm install超时或失败仓库地址配置错误、网络受限、缓存损坏检查.npmrc的 registry 设置删除oh_modules后重试编译报错“router.pushUrl failed”页面没有在main_pages.json中注册打开main_pages.json补上对应页面路径HAP 安装到手机提示“签名不一致”手机的旧包签名与当前签名不同卸载旧应用重新安装新签名的 HAP运行后白屏但编译通过资源引用错误或页面生命周期里数据异常使用 hilog 输出日志检查$r()引用是否存在检查 aboutToAppear 里的变量初始化上架时提示证书指纹不匹配本地证书和 AGC 后台录入的不一致在 AGC 后台重新生成证书并同步本地签名配置6.2 开发过程中“改到怀疑人生”的几次经历这里再分享两个更细节的踩坑片段。第一个是数据库连接没有及时关闭。早期版本我在每次插入打卡记录时都重新getRdbStore结果频繁打开关闭导致偶现的“database is locked”错误。后来改成在应用启动时初始化一次用一个单例类持有RdbStore所有数据操作都走这个实例锁冲突问题再没出现过。第二个是关于状态管理的复杂数据流。我在统计页需要监听首页打卡后数据变化一开始用AppStorage存一个全局计数但发现统计页重新计算的时候要重新查数据库单纯监听计数不够。后来我调整了思路统计页每次onPageShow时主动刷新数据而不是依赖全局变量联动。在鸿蒙里onPageShow触发时机稳定数据一定是最新的处理统计页这种低频刷新场景反而是最简单可靠的方案。7. 一些实战心得「宝贝日程表」做完以后我最大的感受是鸿蒙开发工具链虽然还在快速迭代但命令行这套玩法已经非常实用了。你能看到每个配置、每个构建步骤背后发生了什么遇到问题更容易定位根因。而且用 DevEco CLI 结合脚本我可以在不改任何代码的情况下通过参数切换签名环境、版本号、构建类型这让后期交付省了很多时间。如果你也想跑一遍我建议不要一开始就做复杂的 App先拿一个单页面工具类应用练手。把“创建工程、写页面、接数据、打包、上真机、上架”这条链路彻底跑通一次积累的经验比看十篇文档都有用。未来如果再加入自动化测试、多模块拆分这套项目骨架也能平稳扩展。最重要的是把产品打磨到能上架、能通过审核这件事本身就是对开发者综合能力的一次全面检验。