方向四「基于官方工具的模拟验证与开发提效」实践。全程使用华为官方 Command Line Tools + 鸿蒙云手机,不装 IDE、不用真机、不配签名,把「改代码 → 看到界面」的循环压到 24 秒。

环境与版本

项目版本 / 规格
云手机8 vCPU / 12 GB / 1260×2720
设备型号ALN-AL00(Mate 60 Pro)
系统版本HarmonyOS 5.0.1.130(SP2C00E1R4P1log)
设备 API17
Command Line Tools6.0.1.251(hvigor 6.21.1 / ohpm 6.0.1)
编译 SDKHarmonyOS 6.0.1 (API 21)
JDK17.0.21
hdspace CLI2.3.3
宿主系统Windows 11 x64

说明:本文验证的是工具链与流程,云手机当前为 HarmonyOS 5.0.1 / API 17,编译 SDK 为 API 21。流程对 API 26 同样适用,只需把 compatibleSdkVersion 提高。

目录

一、这件事为什么值得写

搜「鸿蒙云手机 命令行」,能搜到的内容基本都是「打开 DevEco Studio,点这个按钮」。但有两种场景 IDE 帮不上忙:

  1. CI/CD:Jenkins/GitLab Runner 是纯命令行的,装不了图形界面
  2. 远程开发:服务器只有 SSH,本地带宽还紧

我这次就是第二种——目标是一台 8c12g 的云手机,手边只有命令行。整个过程踩了 5 个坑,每个都卡了不短时间。

二、工具链从哪来

官方下载页 developer.huawei.com/consumer/cn/download/command-line-tools-for-hmos 必须登录华为账号才能下。CI 环境里没人能给你扫码。

三个绕法:

方案可行性说明
登录后手动下载,缓存到 CI可行最稳,但要预先准备
HuggingFace 镜像可行实测 commandline-tools-windows-x64-6.0.1.251.zip 完整
从 DevEco Studio 安装目录拷可行sdk/ + hvigor/ + tool/ 三块拷出来即可

我是第二种。包体 2.36 GB,单线程只有 0.45 MB/s(约 90 分钟),16 线程分段后 5.8 MB/s(约 7 分钟)。

# 分段下载 + 断点续传核心逻辑
chunk = size // THREADS + 1
def worker(i):
    start, end = i * chunk, min(size - 1, i * chunk + chunk - 1)
    have = os.path.getsize(part_file) if exists else 0
    while have < end - start + 1:
        headers = {"Range": f"bytes={start + have}-{end}"}
        with requests.get(final_url, headers=headers, stream=True) as r:
            with open(part_file, "ab") as f:
                for blk in r.iter_content(262144):
                    f.write(blk); have += len(blk)

下载完先校验 ZIP 完整性(zipfile.ZipFile(path).namelist()),我第一版就是包被截断了却以为下完,解压时才报错。

三、环境变量:三个,一个都不能少

export JAVA_HOME=/d/harmony/jdk-17.0.21+9
export DEVECO_SDK_HOME=/d/harmony/clt/command-line-tools/sdk
export PATH=$JAVA_HOME/bin:$PATH

Command Line Tools 不带 JRE。不配 JAVA_HOME 时 hvigorw 会报一个跟 Java 毫无关系的错误,很容易误判成 SDK 问题。

JDK 17 就够,不用 21。

四、工程骨架:五个文件决定能不能编

一个能 assembleHap 的最小 stage 模型工程,必须有这五处:

HarmonyShadow/
├── AppScope/
│   ├── app.json5                    # bundleName / versionName / icon
│   └── resources/base/
│       ├── element/string.json      # app_name
│       └── media/app_icon.png       # 图标必须真实存在
├── entry/
│   ├── src/main/
│   │   ├── module.json5             # abilities + permissions
│   │   ├── ets/entryability/EntryAbility.ets
│   │   ├── ets/pages/Index.ets
│   │   └── resources/base/
│   │       ├── element/string.json
│   │       ├── element/color.json
│   │       ├── profile/main_pages.json   # 页面路由,缺了白屏
│   │       └── media/app_icon.png
│   ├── build-profile.json5
│   ├── hvigorfile.ts
│   └── oh-package.json5
├── build-profile.json5
├── hvigorfile.ts
├── oh-package.json5
└── hvigor/hvigor-config.json5       # 最容易漏

坑 1:app_icon.png 缺失会直接编译失败

app.json5 和 module.json5 都引用了 $media:app_icon。工程是手搓的,没有图。

用 Python 生成一个纯色 PNG 就行(不依赖 PIL):

import zlib, struct
def png(path, w=192, h=192, rgb=(26,115,232)):
    raw = b''.join(b'\x00' + bytes(rgb) * w for _ in range(h))
    def chunk(t, d):
        c = t + d
        return struct.pack('>I', len(d)) + c + struct.pack('>I', zlib.crc32(c) & 0xffffffff)
    data = (b'\x89PNG\r\n\x1a\n'
            + chunk(b'IHDR', struct.pack('>IIBBBBB', w, h, 8, 2, 0, 0, 0))
            + chunk(b'IDAT', zlib.compress(raw))
            + chunk(b'IEND', b''))
    open(path, 'wb').write(data)

547 字节,够用。

坑 2:「工程结构需要升级」其实缺 modelVersion

ERROR: 00303024 Configuration Error
The project structure and configuration need to be upgraded before use.
Try the following:
> Use the migration tool to update the project structure and configuration with Deveco Studio

命令行里没有 Studio,这条建议等于没有。翻 hvigor 源码 validate-util.js 才看到真实校验:

const i = HvigorConfigLoader.getInstance().getHvigorConfig()?.modelVersion;   // hvigor-config.json5
const r = parseJsonFile(oh-package.json5).modelVersion;                      // 根 oh-package.json5
i && r ? (i === r ? ... : printErrorExit("INCONSISTENT_MODEL_VERSION"))
       : printErrorExit("PROJECT_STRUCTURE_AND_CONFIGURATION");

结论:

  • 两边都得有 modelVersion
  • 两个值必须完全相等
  • 版本号必须在 hvigor 支持的区间内

只改一边,报错一模一样,看不出区别。

坑 3:hdc 缓存死连接,隧道活了也连不上

云手机隧道断线后重连,hdc tconn 一直 Connect failed,但 hdspace 日志显示 connect to tunnel server success。

原因:隧道服务端活着,但本地 hdc 客户端还握着上一个死 socket。

def reconnect():
    subprocess.run([HDC, "kill"])      # 关键:先杀 hdc 服务
    time.sleep(2)
    for _ in range(6):
        r = subprocess.run([HDC, "tconn", "127.0.0.1:8712"], capture_output=True, text=True)
        if "Connect OK" in (r.stdout + r.stderr):
            return True
        time.sleep(2)
    return False

hdc kill 这一步不能省。少了它,重连十次全是 Connect failed。

坑 4:未签名 HAP 也能装

编译完会看到:

hvigor WARN: No signingConfig found for product default

第一次看到这个我以为是致命错误,去配签名证书,结果要实名、要 CSR、要签名 Profile,流程很长。

实测:云手机直接装未签名 HAP 成功。

hdc -t 127.0.0.1:8712 install entry-default-unsigned.hap
# [Info]... msg:install bundle successfully.

开发调试阶段完全不用碰签名。

坑 5:aa start 报错但应用其实起来了

hdc shell "aa start -a EntryAbility -b com.example.harmonyshadow"
# error: failed to start ability.

但 hilog 里能看到:

C03F00/com.example.harmonyshadow/ArkCompiler: [gc] SmartGC: app cold start just finished

应用在跑,aa start 的返回码不可信。判断标准应该是截图,不是命令返回值。

hdc shell "snapshot_display -f /data/local/tmp/s1.jpeg"
hdc file recv /data/local/tmp/s1.jpeg ./s1.jpeg

五、一键脚本

把上面所有坑封成一个流程,改完代码到看到界面:

import subprocess, time

HDC = "path/to/hdc.exe"
KEY = "127.0.0.1:8712"

def sh(cmd):
    subprocess.run(cmd, shell=True, check=True)

def hdc(*args):
    subprocess.run([HDC, "kill"], capture_output=True)
    time.sleep(2)
    subprocess.run([HDC, "tconn", KEY], capture_output=True)
    time.sleep(1)
    r = subprocess.run([HDC, "-t", KEY] + list(args), capture_output=True, text=True)
    return (r.stdout or "") + (r.stderr or "")

# 1. 编译
sh("hvigorw --mode module -p product=default -p module=entry@default assembleHap --no-daemon")

# 2. 安装
hap = "entry/build/default/outputs/default/entry-default-unsigned.hap"
print(hdc("install", hap))

# 3. 启动
print(hdc("shell", "aa start -a EntryAbility -b com.example.harmonyshadow"))
time.sleep(5)

# 4. 截图
hdc("shell", "snapshot_display -f /data/local/tmp/shot.jpeg")
hdc("file", "recv", "/data/local/tmp/shot.jpeg", "./shot.jpeg")

耗时实测:

步骤耗时
hvigor 增量编译18.3 s
install HAP~3 s
aa start~1 s
截图 + 回传~1.5 s
合计约 24 s

改一行 ArkTS,24 秒后在本地看到云手机截图。

提效对比:三种开发方式

方式首次准备单次改动反馈能否进 CI多人共享
DevEco Studio + 本地模拟器装 IDE + 下载模拟器镜像(数 GB)热重载约 3–8 s否否
DevEco Studio + 真机IDE + 数据线 + 签名约 15–30 s否否
官方 CLI + 云手机工具链 2.36 GB + 云手机开箱即用约 24 s(全自动)是是(同一隧道多端复用)

CLI 方案的单次反馈不是最快的(热重载更快),但它有两个别人没有的属性:

  1. 可进 CI:整条链路都是命令,能塞进 Jenkins / GitLab Runner
  2. 可共享:云手机在云端,团队成员不用各自准备设备

这就是方向四「开发提效」真正的落点:不是让单人更快,而是让流程可自动化、可复制。

实际收益

我这次给「多设备任务接续」做端侧验证,一共改了 7 版 ArkTS(接 WebSocket、加序号、加缺口检测、加重连、修 RTT、改 UI、修日志截断)。

  • 按真机方式:要等设备、插线、签名,每版至少 5 分钟起
  • 按 CLI + 云手机:7 × 24 s ≈ 3 分钟全部跑完

改版次数越多,这个差距越明显。

六、云手机真机信息

型号:ALN-AL00(Mate 60 Pro)
系统:HarmonyOS 5.0.1.130(SP2C00E1R4P1log)
API:17
分辨率:1260 × 2720
规格:8 vCPU / 12 GB

用 hdc shell param get 一次性读全:

hdc shell "param get const.product.software.version; \
           param get const.ohos.apiversion; \
           param get const.product.model; \
           param get const.product.devicetype"
5.0.1.130(SP2C00E1R4P1log)
17
ALN-AL00
phone

注意 -t 127.0.0.1:8712 必须带。不带的话报:

[Fail]ExecuteCommand need connect-key? please confirm a device by help info

因为本地可能同时连着多台,hdc 不知道你指哪台。

七、效果验证:应用真的跑起来了

编译产物 entry-default-unsigned.hap 只有 48 KB(一个页面 + 一个 Ability + 网络权限),安装后启动,云手机实时抓屏如下:

应用启动界面,1260x2720 竖屏

界面元素全部正常渲染:标题、输入框、按钮、统计栏、日志列表。没有任何白屏、错位、字体缺失。

点「连接」后,应用通过 WebSocket 连上公网的 Node 服务,收到对端消息:

连接成功,收到对端消息

说明整条链路都通了:

hvigorw 编译 → hdc install → aa start → 应用进程 → ohos.net.webSocket → 公网服务端

从 assembleHap 到看到这张截图,总耗时约 24 秒。

八、这套流程适合谁

适合:

  • CI 里跑鸿蒙自动化测试(有 hdc 就能装包、截图、点按)
  • 服务器上做远程开发
  • 需要批量在多台云手机上验证兼容性

不适合:

  • 需要 UI 预览、实时热重载 —— 老老实实用 DevEco Studio
  • 需要调试 ArkTS 断点 —— CLI 工具链不支持,只能看 hilog

九、总结

纯命令行跑通鸿蒙云手机,关键就三件事:

  1. 工具链:hvigorw(编译)+ hdc(部署/调试)+ JDK 17,SDK 自带 hdc 不用另外下
  2. 工程配置:modelVersion 两边一致、app_icon.png 真实存在、main_pages.json 别漏
  3. 连接稳定性:hdc kill → tconn 是唯一可靠的复位方式

五个坑里,三个是错误信息骗人(说是升级问题其实是配置缺失、说是启动失败其实已启动、说是签名问题其实能装),两个是状态残留(hdc 死连接、ZIP 断包)。

这些都不在官方文档里,只能靠踩。希望这篇能让你少踩几个。

下一篇我会用这套流程把「多设备任务接续」的完整客户端跑起来,包括 WebSocket 长连接、消息序号和断线影子恢复的真实数据。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐