ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

DevEco CLI实战:从命令行构建到上架鸿蒙宝贝日程表App

DevEco CLI实战:从命令行构建到上架鸿蒙宝贝日程表App 最近在给家里小朋友做一款鸿蒙原生的「宝贝日程表」App整个过程从命令行创建工程一路做到正式上架就是用 DevEco CLI 这套命令行工具链完成的。这个项目本身不复杂但正好把一个鸿蒙 App 从 0 到 1 的关键链路全跑了一遍工程初始化、ArkTS 页面开发、本地数据持久化、hvigor 打包、签名配置、AGC 上架审核。这篇文章把整个实战过程完整复盘一遍适合两类人看一类是刚接触鸿蒙开发、想找个小项目练手的同学另一类是已经在用 DevEco Studio 写界面、但还没搞明白命令行构建和上架流程的开发者。我会把实际踩过的坑也一并交代清楚尤其是签名和构建产物那部分官方文档里散落在各处这里一次说透。1. 需求拆解与整体方案设计1.1 做「宝贝日程表」要解决什么问题带娃的家庭应该都有画面感每天早上催起床、催吃饭、催写作业晚上还要盯着兴趣班时间全靠家长人肉闹钟。我最初的想法特别简单就是做一个让孩子自己也能看懂、能操作的小工具把每天要做什么事、几点做清清楚楚列出来。它的使用场景是放在平板或者智慧屏上孩子早晨起来看一眼就知道今天安排完成一项就自己点一下打卡。目标用户决定了产品形态。孩子用的 App第一个要求是字要大、按钮要大、操作路径短。第二个要求是不能有复杂的账密体系打开就能用。第三个要求是数据要本地保存不需要联网也不需要服务器。所以这个项目的定位很明确本地优先、单机可用、轻量快速。我没有做登录、云同步、分享这些功能甚至没有做多用户体系因为 MVP 阶段这些都属于过度设计。核心功能收敛成三块就够用了今日日程列表按时间顺序展示当天的安排已经完成的置灰或打勾。添加日程通过一个大按钮唤起添加弹窗填标题、选时间点保存就上屏。本地持久化应用杀掉再打开日程数据不能丢。做完这三块这个 App 就已经能在日常使用了。至于后续的周视图、农历提醒、家长密码锁都是可以迭代的扩展项不影响第一版上架。1.2 技术选型为什么坚持用 DevEco CLI这个项目技术栈选型其实没有太多悬念鸿蒙原生开发现在主流就是 ArkTS ArkUI声明式 UI 写法状态驱动界面刷新。但我在工程创建和构建方式上刻意选择了 DevEco CLI而不是全程依赖 DevEco Studio 的图形界面原因有两个。第一命令行工具更适合重复操作和自动化。日常开发中我经常要新建 demo 工程、跑构建、出包如果每次都在 IDE 里点来点去效率很低。CLI 方式下一条命令就能完成工程创建hvigor 一条命令就能打出 HAP这在做多模块项目、CI/CD 流水线时尤其重要。第二CLI 能让你更清楚构建链路的真相。IDE 图形界面把很多细节藏起来了换成命令行之后构建产物在哪、签名怎么配、依赖从哪拉全都要自己面对反而能更快理解鸿蒙项目的工程结构。当然我并不是说要彻底抛弃 IDE。实际开发中我还是会用 DevEco Studio 来跑模拟器、看布局效果、打断点调试因为 ArkUI 的实时预览和调试体验确实比纯命令行舒服太多。我的建议是工程管理和构建走 CLI界面编写和调试配合 IDE两者互补。1.3 功能范围与开发里程碑整个「宝贝日程表」从设计到上架前后大概用了两周的业余时间。开发节奏拆成四个阶段推进每个阶段都有明确的可验证产物阶段内容交付物第一阶段工程初始化 页面骨架可运行的空壳 App第二阶段日程数据模型 列表展示静态列表界面第三阶段添加弹窗 本地持久化完整可用的单机 App第四阶段打包签名 上架审核应用市场可下载的正式版本这个拆分方式有个好处每个阶段结束都能在真机上跑一版及时发现问题不用憋一个大版本到最后才验证。做个人项目最忌讳闷头写很久才拿出来跑试错成本会很高。2. 环境准备CLI 工具链从零配置2.1 安装 DevEco Studio 并找到命令行全家桶很多人在这一步会绕弯子。DevEco CLI 并不是一个单独下载的工具它是随着 DevEco Studio 一起安装的。安装完 DevEco Studio 之后命令行工具就已经在你的电脑上了只是默认没有配到 PATH 里需要自己找到路径。以我本机为例命令行工具主要集中在两个位置一个是 DevEco Studio 安装目录下的sdk目录里面有hdc设备调试工具和各类 SDK 组件另一个是工程目录下的hvigorw和ohpm这两个在初始化工程之后会出现。如果你是 macOS 或 Linux 环境建议把 DevEco Studio 的tools目录加到 shell 的 PATH 里方便全局调用。Windows 环境同理把对应目录加到系统环境变量即可。配置好之后打开终端先跑一条命令验证devecocli --help devecocli --version如果终端能正常输出版本信息说明命令行环境已经通了。这里有个小坑新版 DevEco Studio 的命令行工具名称在不同版本里可能不完全一样有的版本是devecocli有的版本把能力拆得更细。遇到无法识别命令的情况先去安装目录的bin或者tools目录下看看到底有哪些可执行文件不要硬记命令名。2.2 用 DevEco CLI 初始化工程模板环境就绪之后我用 CLI 创建了「宝贝日程表」的工程骨架。DevEco CLI 提供了初始化项目的子命令可以指定工程名称、包名、模板类型。比如下面这条命令就是创建一个基于 Empty Ability 模板的工程devecocli create -n BabySchedule -p com.example.babyschedule -t app参数含义不复杂-n是工程名-p是应用包名-t指定工程类型。创建完成后当前目录下会生成一个完整的鸿蒙工程结构大概是这样的BabySchedule/ ├── AppScope/ ├── entry/ │ ├── src/main/ │ │ ├── ets/ │ │ │ ├── entryability/ │ │ │ ├── pages/ │ │ │ └── models/ │ │ └── resources/ │ ├── build-profile.json5 │ └── hvigorfile.ts ├── build-profile.json5 ├── hvigorfile.ts └── oh-package.json5entry就是我们最终的 HAP 模块AppScope放应用级配置oh-package.json5是依赖管理文件build-profile.json5是构建配置文件。初次看到这个结构的同学不用慌日常开发 90% 的时间只会在entry/src/main/ets下面写业务代码其他文件偶尔动一下。2.3 必须掌握的 hvigor 与 ohpm 命令鸿蒙工程的构建工具叫 hvigor对应工程根目录下的hvigorw脚本依赖管理工具叫 ohpm类似前端的 npm。这两个工具的常用命令我整理了一份速查表场景命令安装依赖ohpm install单独安装某个依赖ohpm install ohos/axios清理构建产物./hvigorw clean构建 HAP 包./hvigorw assembleHap构建并执行测试./hvigorw test查看所有 task./hvigorw tasks --type build需要注意的是hvigorw在执行时会自动去拉取对应版本的 hvigor 依赖首次运行会比较慢这是正常现象耐心等即可。另外assembleHap产出的包默认是带 debug 签名的如果要在真机上安装还需要先配置签名。这部分我在后面专门展开讲。3. 宝贝日程表核心功能实现3.1 数据模型与页面骨架开发核心功能之前先把数据模型定下来。「宝贝日程表」的数据结构非常简单四个字段就够用标题、日期、时间、完成状态另外加一个唯一 id 用于列表渲染时做 key。export interface ScheduleItem { id: string; title: string; date: string; time: string; done: boolean; }界面我用了 ArkUI 的声明式写法。整个首页就是一个垂直布局顶部是当天日期的大标题中间是日程列表底部是一个大的“添加日程”按钮。ArkUI 的Column、List、Text、Button这些基础组件组合一下页面骨架很快就搭起来了。Entry Component struct Index { State scheduleList: ScheduleItem[] []; build() { Column({ space: 16 }) { Text(this.getToday()) .fontSize(28) .fontWeight(FontWeight.Bold) .margin({ top: 20 }) List({ space: 12 }) { ForEach(this.scheduleList, (item: ScheduleItem) { ListItem() { ScheduleRow({ item: item, onToggle: this.toggleDone.bind(this) }) } }, (item: ScheduleItem) item.id) } .layoutWeight(1) Button(添加日程) .width(80%) .height(48) .onClick(() { this.showAddDialog(); }) } .width(100%) .height(100%) } }这段代码里有几个细节值得注意List之所以要设置layoutWeight(1)是为了让它填满按钮和标题之间的剩余空间ForEach的第三个参数是 key 生成函数我用item.id来保证列表项被唯一标识这样增删改时 ArkUI 能精准复用节点不会出现列表刷新时的闪动问题。3.2 添加日程弹窗交互与时间选择添加日程的入口是底部的大按钮点击之后弹出一个自定义弹窗。我用了 ArkUI 的CustomDialogController在控制器里嵌入表单控件。这里有一个体验细节值得说说如果直接上标准输入框加时间选择器对孩子来说操作门槛还是偏高所以我简化成两个控件——标题输入框和按小时的快速时间选择。时间选择部分我用了TimePicker但是把它限制在整点和半点减少操作精度。这个交互优化对成人用户来说也能减少思考成本。核心代码大概是这样的CustomDialog struct AddScheduleDialog { controller: CustomDialogController; title: string ; selectedTime: string 08:00; onConfirm: (item: ScheduleItem) void; build() { Column({ space: 16 }) { TextInput({ placeholder: 今天要做什么, text: this.title }) .onChange((value: string) { this.title value; }) TimePicker({ selected: this.selectedTime }) .useMilitaryTime(true) .onChange((value: TimePickerResult) { this.selectedValue ${value.hour}:${value.minute}; }) Row({ space: 16 }) { Button(取消) .onClick(() { this.controller.close(); }) Button(保存) .onClick(() { const newItem: ScheduleItem { id: Date.now().toString(), title: this.title, date: this.getToday(), time: this.selectedValue, done: false }; this.onConfirm(newItem); this.controller.close(); }) } } } }注意这里的时间格式化TimePicker返回的hour和minute是数字类型两位数的时间需要补零否则会出现8:5这种不好看也不便于排序的格式。我在工具函数里做了padStart处理。3.3 列表渲染与完成状态切换日程列表的每一行展示两项核心信息标题和时间左边放一个自定义的完成状态按钮。孩子点击状态按钮之后这一条日程就在视觉上变为置灰并加删除线同时底部统计数字会更新形成正向反馈。Component struct ScheduleRow { item: ScheduleItem; onToggle: () void; build() { Row({ space: 12 }) { Button(this.item.done ? ✓ : 〇) .width(36) .height(36) .backgroundColor(this.item.done ? #4CAF50 : #E0E0E0) .onClick(() { this.onToggle(); }) Column() { Text(this.item.title) .fontSize(22) .decoration({ type: this.item.done ? TextDecorationType.LineThrough : TextDecorationType.None }) .fontColor(this.item.done ? #999999 : #333333) Text(${this.item.date} ${this.item.time}) .fontSize(14) .fontColor(#999999) } .alignItems(HorizontalAlign.Start) .layoutWeight(1) } .width(100%) .padding(12) .backgroundColor(#F7F7F7) .borderRadius(12) } }状态切换的逻辑放在父组件里通过toggleDone方法从前到后遍历列表找到对应 id 后把done字段取反。这样子组件不需要自己维护可变数据状态统一由父组件管理结构更清晰。实际测试中我发现当列表项变多时如果状态分散在子组件内部很容易出现“界面没刷新但数据变了”这种幽灵问题统一状态管理可以从源头上避免。3.4 本地持久化首选项与关系型数据库怎么选日程数据必须本地保存这涉及到一个选型问题鸿蒙提供了多种本地存储方案首选项Preferences和关系型数据库RDB是最常用的两种到底选哪个我的选择是首选项原因很直接宝贝日程表的数据量很小一个家庭一天的日程最多几十条用关系型数据库属于杀鸡用牛刀。首选项的特点是轻量、结构简单、适合 key-value 形态的少量数据我的做法是把整个日程数组序列化成 JSON 字符串存到一个 key 下面。读取时反序列化写回时整体覆盖逻辑非常简单。import { preferences } from kit.ArkData; const PREF_NAME schedule_store; const KEY_LIST schedule_list; async function loadList(context: Context): PromiseScheduleItem[] { const store await preferences.getPreferences(context, PREF_NAME); const json store.getSync(KEY_LIST, []) as string; return JSON.parse(json); } async function saveList(context: Context, list: ScheduleItem[]): Promisevoid { const store await preferences.getPreferences(context, PREF_NAME); await store.putSync(KEY_LIST, JSON.stringify(list)); await store.flush(); }这段代码几乎可以无脑复用。唯一要注意的是flush方法必须调用否则数据只停留在内存里应用异常退出就丢了。我第一版就漏了这一步导致每次杀进程之后数据全没排查了半天才反应过来。那什么时候应该用关系型数据库呢如果后续你要做历史日程按天查询、跨月统计、模糊搜索数据量上来之后JSON 整体读写的方案就不太合适了。那种场景建议切换到 RDB建一张日程表用 SQL 查询。但对于当前项目首选项是性价比最高的方案。4. 构建、签名与真机调试4.1 构建 HAP 产物hvigor 构建链路功能代码写完第一件要做的事是构建出可以在真机上安装的 HAP 包。HAP 是鸿蒙应用的分发物相当于 Android 的 APK。执行构建只需要一条命令./hvigorw assembleHap首次构建会比较慢因为要拉取配套的工具链依赖。构建完成后产物一般在entry/build/default/outputs/default/entry-default-unsigned.hap。注意这个文件名的unsigned标记它说明当前包还没有签名直接装到真机上会被系统拒绝。hvigor 的构建链路其实分几个阶段资源编译、ArkTS 编译到字节码、资源索引生成、打包、签名。其中任何一个环节出错都会中断整个流程。我项目里遇到的坑主要是在资源层面比如往resources目录里塞了一张命名字段不合法的图片构建直接报错退出排查方法是把新加的资源文件逐个移除做二分排查很快就能定位。4.2 签名配置与真机调试真机调试绕不开签名。鸿蒙应用签名涉及两套体系调试签名和发布签名。调试签名用于开发阶段在真机安装运行发布签名用于上架市场。先用调试签名把应用装到真机上跑通再把发布签名配好做上架包。签名配置分三步走生成密钥库文件.p12、生成证书请求.csr、申请调试 Profile.p7b。这些操作在 DevEco Studio 的 Project Structure 界面里可以半自动完成也可以在 AppGallery Connect 后台手动操作。我的建议是第一步在本地用keytool命令生成密钥库后续步骤在 AGC 后台完成。调试 Profile 申请好之后把它和密钥库文件放到工程的entry/signature目录下然后在build-profile.json5里注册签名信息{ modules: [ { name: entry, signingConfigs: [ { name: debug, material: { certpath: ./signature/debug.p7b, storePassword: 123456, keyAlias: debug, keyPassword: 123456, profile: ./signature/debug-profile.p7b, signAlg: SHA256withECDSA, storeFile: ./signature/debug.p12 } } ] } ] }配置完成后再跑一次assembleHap输出的产物名会从unsigned.hap变成正式的 HAP 包。这里提醒一句storePassword和keyPassword虽然是写在工程里的但千万不要把这个文件提交到公开仓库否则别人可以拿你的签名做应用分发。签名的安全性直接关系到应用的身份可信度。连真机调试还用到另一个命令工具hdc它是鸿蒙的设备连接工具类似 Android 的 adb。常用命令也不多hdc list targets hdc install entry-default-signed.hap hdc shell新增设备第一次连接时手机上会弹出授权确认框点击允许后hdc list targets才能看到设备。如果总是看不到设备优先检查开发者模式有没有打开、USB 调试有没有授权这两个环节最容易出问题。4.3 构建报错排查实录这个项目开发周期不长但构建报错遇到好几个挑三个有代表性的记录下来都是大家大概率会碰上的。第一个是签名相关error: verify signature failed。出现这个报错绝大多数情况是 Profile 证书和密钥库不是一套。我在调试阶段申请了好几个 Profile混乱之中配混了导致 hvigor 签名之后自校验不过。解决方法是把signature目录下的文件清理干净重新申请一套调试签名配置。第二个是依赖相关ohpm install超时或者拉取依赖失败。鸿蒙的 ohpm 仓库是分地区的网络不好的时候很容易失败。我的经验是设置镜像源在~/.ohpmrc里配置国内镜像地址速度会提升很多。另外仓库里部分三方库的版本兼容性参差不齐引入之前最好先查一下它支持的 API 版本和鸿蒙 SDK 版本。第三个是资源编译相关resource path is invalid。这种情况大多数是因为资源文件名称不规范或者放错了目录。ArkUI 资源文件要求小写命名不支持特殊字符即使是一个圆点也可能导致构建失败。发现问题后不需要改动代码把资源名改成ic_schedule_add.png这种规范格式重新构建即可。5. 上架全流程与避坑指南5.1 AppGallery Connect 后台配置功能稳定、本地测试通过之后就可以准备上架了。鸿蒙应用上架主要走 AppGallery Connect简称 AGC平台。登录 AGC 后台后第一步是创建应用填入应用名称、包名等基础信息。这里的包名必须和工程里AppScope下的配置保持一致否则后面签名和审核都会出问题。创建好应用之后需要在“开发服务”里申请发布证书和发布 Profile。这个流程和调试签名类似但注意不要把调试签名和发布签名搞混。发布证书申请时需要用到本地的 CSR 文件这个文件在 DevEco Studio 的 Project Structure 里可以生成。整套证书申请流程大约几分钟就能完成但审核可能需要等待所以建议提前准备。证书和 Profile 拿到后在工程里新增一个release的signingConfigs然后把构建命令改成生成发布包。hvigor 会根据当前构建类型自动选择合适的签名配置。5.2 隐私合规与权限声明现在应用市场上架对隐私合规要求相当严格鸿蒙这边也不例外。宝贝日程表功能简单不需要电话、存储、定位这类敏感权限这是先天优势。但即使不申请权限也需要在 AGC 后台提供一个隐私政策网址并且在应用详情页里如实填写 App 收集哪些数据。日程类应用如果完全没有网络功能数据仅存本地隐私政策可以写得很简单但如果不提供审核很大概率会被打回。另外有一个容易被忽视的点如果 App 里用到了网络能力却不上报隐私政策或者隐私政策声明的内容与实际功能不符审核人员可能会以“隐私合规风险”为由拒绝。我自己在初次提交时就因为隐私政策链接写成默认占位网址被打回一次改正确之后很快就通过了。权限声明这块我的建议是能不用权限就不用权限这是最稳的合规策略。日程列表要做到单机可用完全不需要任何联网类权限反而更符合轻量工具类应用的定位。5.3 提交审核与常见拒绝理由应用信息全部填好、发布包构建完成之后就可以在 AGC 后台上传了。上传 HAP 包之后后台会解析包信息校验签名、版本号、图标尺寸等基础项。如果这里就报错一般是包名不一致或签名 Profile 用错环境解决起来也直白重新确认签名配置重新构建。我整理了审核阶段最容易踩的几个坑常见问题表现解决办法隐私政策缺失后台提示未提供隐私政策补充可访问的隐私政策页面应用截图尺寸不对上传截图提示分辨率不符按后台要求重新截图功能描述与实际不符审核发现 App 内没有描述的功能保持描述简洁不夸大功能版本号不合法版本号小于已上架版本每次发版递增 versionCode审核周期一般来说是几天不等看提交时间段和排期情况。收到审核意见后按意见逐条修改再重新提交即可不用紧张。6. 项目总结与后续扩展思路6.1 从这次实战中学到什么做完这个项目一个很直接的感受是鸿蒙开发的上手门槛没有想象中高但构建和上架环节的细节确实比预期要多。DevEco CLI 的价值在这次项目中体现得很充分从建工程到出包几乎不用打开 IDE而且每个构建步骤都可追溯出了问题能快速定位到具体环节。对于想批量做工具类 App 或者搭自动化流水线的团队来说命令行这条路值得尽早投入。另外一个体会是关于“小项目也要认真对待流程”。宝贝日程表功能很简单但签名、隐私政策、版本管理这些环节一个都不能少。早早在工程里把调试签名和发布签名分开配置后面做版本迭代会非常省心不需要每次发版都临时去折腾证书。6.2 还能往哪些方向扩展这个 App 后续可以扩展的方向确实不少。如果能引入日历控件就能从“只看今天”升级为“随便翻哪天的日程”这对周计划和月计划的场景是刚需。再进一步如果要做多设备同步可以在鸿蒙的分布式数据管理能力上做文章让手机和平板之间共享同一份日程数据。还有家长控制功能比如设置一个简单的四位数密码防止孩子自己乱改日程这类功能在儿童用品的场景下会很受欢迎。不过这些都是后话。第一版先把基础体验做扎实孩子能每天打开列表看懂今天要做什么按完打卡有成就感这个项目的核心价值就已经到位了。
返回列表