尧图精选

OpenHarmony hdc启停应用:aa start与force-stop

🕒 发布时间:2026/10/1 23:30:02 📁 来源:尧图网络
手里只有一块开发板、一台装了 x86 模拟器的机器或者干脆就是一台连着 USB 的样机时调试 OpenHarmony 应用最省事的办法从来不是点界面而是敲命令。Openharmony hdc 启动应用、关闭应用这件事说白了就是用 hdc 这根数据线把aa start和aa force-stop两个动作精确地打到设备上。它看起来只是两行命令但真到项目里用起来你会发现坑全在细节里多设备时命令打错了板子、aa start返回成功界面却没动、force-stop 之后应用三四秒又自己爬起来、设备冷重启后应用死活不自启、渲染花屏时不知道该从应用层查还是从系统层查。这些问题靠 DevEco 点按钮是查不出来的只有命令行能给你确定的答案。这篇内容我按环境准备—启动—关闭—脚本化—排错—经验的顺序写适合刚接触 OpenHarmony 的应用开发者、做系统集成的工程师以及需要把启停动作塞进自动化测试流水线的人。整个过程不需要你有多深的系统底层功底能看懂命令行输出就够了。1. 从零把 hdc 这根线接上1.1 先搞清楚 hdc 在整套调试链路里的位置hdc 全称 HarmonyOS Device Connector是 OpenHarmony 体系里的设备连接器本质是一个客户端加一个服务端的组合你在终端敲的hdc xxx是客户端后台跑着的是 hdc server真正跟设备通信的是 server 和设备上的 hdcd 守护进程。理解这一点很重要因为后面绝大多数命令没反应设备列表是空的这类问题都出在 server 和设备之间的这一段而不是你的命令写错了。它跟安卓体系里的 adb 定位几乎一样hdc list targets对应adb deviceshdc shell对应adb shellhdc install对应adb install。如果你以前写过 adb 脚本迁移成本主要在于两条命令的名字不一样启动应用不再用am start而是aa start关闭应用也不是am force-stop而是aa force-stop。日常使用中hdc 承担三件事一是看见设备二是往设备里塞东西或者从设备里掏东西安装 hap、拉日志、拉截图三是在设备上执行一条命令启停应用、清数据、改系统参数。启停应用属于第三类走的是hdc shell通道最终落到设备上的 Ability Manager 服务去执行。清楚这个链路之后遇到问题就能分层判断设备都没连上谈启停毫无意义shell 能进但 aa 命令报错那就是应用配置或 Ability 名称的问题aa 命令成功但界面没变化那就是应用自己的启动模式或生命周期逻辑在作怪。1.2 Linux 下把 hdc 拿下来并配好Linux 版 hdc 不需要单独去找安装包它就在你下载的 SDK 里路径通常是toolchains/hdc有的版本是toolchains/linux/hdc。把它拷到一个你习惯的目录加执行权限再挂进 PATH 就完事了。这里有个很常见的低级问题从压缩包里解出来的二进制默认没有 x 权限直接敲hdc list targets会提示没有权限或者 command not found很多人第一反应是去重装 SDK其实是白折腾。执行chmod x hdc然后./hdc -h能打出帮助信息就说明二进制本身是好的。第二类问题是 USB 访问权限。Linux 下非 root 用户默认没有直接访问 USB 设备的权限表现是hdc list targets输出[Empty]但设备明明插着。解决办法是写一条 udev 规则把设备节点的属主或权限放开然后重新加载规则、重新插拔一次设备。这一步做完设备列表立刻就出来了。这里要注意udev 规则里的 vendor id 和 product id 要从lsusb的实际输出里抄别照着别人的博客抄不同板子的 id 经常不一样抄错了规则写了也白写。第三件事是环境变量。HDC_SERVER_PORT值得单独说一句因为当你要同时接多台设备、或者 CI 上要并发跑多个任务的默认端口只有一个 server多个进程会互相抢。给每个任务分配一个独立端口就能互不干扰启动前 export 一下端口号再hdc start两台设备两条流水线各跑各的实测下来很稳。至于 server 的启停hdc kill是关掉 serverhdc start是拉起来hdc start -r是重启。遇到设备状态显示异常、明明连着却报 offline 的时候先hdc kill再hdc start比在那反复插拔线快得多。1.3 一个容易忽略的前提应用得先装进去启动应用之前应用得先在设备上。命令行安装的写法是先把 hap 推到设备上再用设备端的包管理命令安装而不是像 adb 那样一步到位。常见流程是hdc file send xxx.hap /data/local/tmp/然后hdc shell bm install -p /data/local/tmp/xxx.hap覆盖安装加-r。装完之后用hdc shell bm dump -a能看到已安装的所有包名用hdc shell bm dump -n 你的包名能看到这个包的详细信息包括模块名和 Ability 名——这两个名字正是后面启停命令要填的参数很多人启动报错就是因为包名对了但模块名或 Ability 名填错了。先把这两个名字从bm dump里抄下来后面能省掉大量试错时间。多设备的时候还有个细节设备列表里的 connectKey 就是设备的身份证。hdc list targets -v能看到更详细的信息包括设备类型和连接方式。如果你的机器上同时插着一块开发板和一台模拟器任何一条不带-s的命令都可能打到你不想要的那台上去调试到一半发现日志对不上就是这个问题。2. 启动应用aa start 的参数该怎么填2.1 最小可用命令长什么样启动应用的核心命令是hdc shell aa start最少要给它两个信息Ability 名和包名。典型写法是hdc shell aa start -a EntryAbility -b com.example.demo。这里-a是 Ability 的名字注意是代码里module.json5里声明的那个名字不是类名加上一串路径-b是包名也就是 bundle name。这两个参数缺一个都不行只给包名会报输入参数不合法只给 Ability 名会找不到目标。如果这个应用有多个模块比如一个 entry 加若干个 feature 模块而你要启动的 Ability 不在 entry 模块里那就还得补上模块名-m。完整一点的形式是hdc shell aa start -a XxxAbility -b com.example.demo -m feature。我在项目里踩过这个坑feature 模块里的页面用手动点能正常打开用命令启动一直报找不到 Ability查了半小时才发现是少了-m。所以当你确认包名和 Ability 名都没写错、命令仍然报错的时候第一件事就是补-m。至于-U这个参数是用来指定用户的。设备上默认的普通用户 ID 通常是 100多用户场景下如果应用只给某个用户安装过那启动时就得指定对应的用户 ID否则会出现命令执行成功但界面没反应。这个坑在小设备上不容易碰到在支持多用户的平板上很常见。我的习惯是单用户设备不加一旦涉及多用户测试就把-U显式写出来反正写出来也不会错。2.2 带调试模式、带启动参数的进阶写法调试阶段强烈建议加上-D它会把 Ability 以调试模式启动。这个模式的价值在于你从命令行启动的应用可以被调试器正常附加日志级别和生命周期回调的行为也更接近 IDE 里点调试按钮的效果。如果你在排查启动阶段就崩溃的问题没有-D经常会看到应用起来一瞬间就没了加上之后才能在日志里拿到完整的调用栈。需要给 Ability 传自定义参数的时候用--pi传整型、--pb传布尔值这一套参数。比如hdc shell aa start -a EntryAbility -b com.example.demo --pi index 3应用侧在onCreate里就能读到这个 index 是 3。这个能力在做自动化测试时特别好用你想直接跳到某个深层页面与其在脚本里模拟一连串点击不如给 Ability 传个参数让它自己跳过去稳定性高出一个档次。要注意的是参数类型必须匹配给--pi传了字符串行为是未定义的我遇到过直接启动失败的情况。启动模式也值得在这里提一句。Ability 的启动模式决定了重复执行同一条aa start会有什么结果单实例模式下重复启动通常只是把已有实例拉到前台多实例模式下则会再起一个。如果你发现反复执行启动命令之后任务栈里堆了一排同样的页面那不是命令的问题是配置的问题。先用hdc shell aa dump -l看一下当前的任务栈再回头调module.json5比瞎猜快。2.3 一次启动到底发生了什么把一条aa start敲下去设备上大概经历这么几步客户端把命令通过 server 发到设备上的 hdcdhdcd 再转给系统的 Ability Manager 服务Ability Manager 先去校验包名和 Ability 名是否存在不存在就直接返回错误码这时候应用进程根本不会被创建校验通过后它会检查目标应用进程在不在不在就先通过应用孵化机制把进程拉起来进程起来之后加载 Ability 的代码走onCreate、onWindowStageCreate这一串回调最后界面才显示出来。理解这个顺序排错的时候就有了抓手。命令返回得快、错误码是ability 不存在问题在第一步命令返回慢、日志里能看到进程被拉起的记录但界面迟迟不出现问题在后面几步要去看onWindowStageCreate里的日志。很多人一看到启动失败就去翻应用代码其实错误码已经告诉你是名字写错了。2.4 启动报错的常见码与含义不同 SDK 版本的错误码号段会有些调整所以下面这些只做方向性参考具体以你本地版本为准。最常见的一类是输入参数不合法通常就是包名或 Ability 名拼错了、-m没给第二类是目标不存在说明系统里确实没有这个包或者包装在了别的用户下第三类是权限类错误某些 Ability 需要特定权限才能被外部拉起第四类是内部错误这种一般是系统服务状态异常先重启设备或者重启一下 server 再看。这里有个经验报错信息里如果出现了数字错误码把整条命令和报错原封不动贴到日志工具里搜往往比搜中文描述更准。因为错误码在系统源码和文档里是固定字符串而中文描述在不同版本里被翻译得五花八门。另外hdc shell aa help会打出当前版本支持的全部参数这份输出永远比任何一篇博客都权威参数拿不准的时候直接看它。3. 关闭应用force-stop 和 kill 不是一回事3.1 aa force-stop 做了什么关闭应用的正规命令是hdc shell aa force-stop 你的包名注意这里只要包名不要 Ability 名也不需要加-b。它的行为是把整个应用进程连同它的任务栈一起收掉等价于用户在多任务界面把应用划掉——但比划掉更彻底因为它不会走正常的退场动画属于强制终止。有一个容易被误解的点force-stop 只是把运行中的进程和任务清掉不碰应用的数据和缓存。你登录过的账号、写进沙箱的文件都还在。想让应用恢复到刚装完的状态得用清理数据的能力一般是通过设备的包管理命令清数据形式和参数各版本略有差别用bm help确认一下当前支持的选项。我见过不少人以为 force-stop 会把数据清掉写自动化脚本时指望它来做重置环境结果前后两个用例互相污染查了半天才发现数据一直在。这两件事要严格分开进程生命周期归 aa数据生命周期归 bm。3.2 想更狠一点直接杀进程有时候 force-stop 不够用比如应用起了常驻的服务进程或者你要模拟极端场景下的进程被杀。这时候可以绕过 aa直接用系统的杀进程命令。流程分两步先用hdc shell pidof 你的包名拿到进程号如果这个命令在你手上的版本里没有就退回到hdc shell ps -ef加上 grep 去筛拿到进程号之后执行hdc shell kill -9 进程号。需要提前说明这条路不是所有版本都通。有些版本对 shell 用户杀应用进程做了限制会直接返回权限不足或者操作不允许这属于正常的机制设计不是你环境有问题。碰到这种情况就老老实实回退到 force-stop它已经能满足绝大多数调试需求。另外一个应用往往不止一个进程尤其是有独立进程配置的模块pidof只会给你主进程号剩下的要自己从 ps 列表里看。杀主进程不一定能把兄弟进程一起带走这一点在排查明明杀干净了应用还能响应的时候很关键。3.3 关了又自己起来问题出在哪我刚 force-stop两三秒后应用又回来了是我被问得最多的问题之一。原因通常有几种一是应用里注册了某些系统事件的监听比如网络变化、屏幕状态变化事件一来就自己拉起了二是有常驻任务或者定时任务在跑任务被触发时会拉起承载它的进程三是这个应用配置了开机自启设备重启后由系统拉起如果你同时在做重启测试看起来就像是杀不死。排查手段是把时间线拉出来。先用hdc shell hilog把日志持续输出到文件再执行 force-stop观察这几秒里到底是谁把进程拉起来的。日志里会出现拉起进程的记录看到发起方是谁基本就定位了。这一步比在应用代码里大海捞针高效得多。3.4 设备重启后应用不自启这件事有不少人会用重启设备来验证应用的自启能力然后发现设备起来之后应用并没有跟着起来尤其是设备重启后停在锁屏、用户还没解锁的情况下。这个现象多数时候是机制使然不是配置写错了。应用要做到开机自启前提是它声明了对开机完成事件的监听并且被系统允许在启动阶段被拉起而在用户未解锁的状态下部分数据区域是不可用的应用进程即使被调度也起不完整表现出来就是没起来或者起来又退出。所以做自启验证的时候测试步骤要把用户解锁这一步显式写进去别把两种状态下的表现混在一起判断。我的习惯是先把设备重启到完全可用状态登录进去再用hdc shell aa start手动确认应用能正常启动确认没问题之后再单独验证自启逻辑。两件事分开测出问题的时候你才能确定到底是自启没生效还是应用本身启动就有问题。4. 把这套动作封装成脚本4.1 脚本化的收益和设计思路单次敲命令没什么技术含量真正的效率提升来自把启停动作脚本化。脚本能带来三个直接收益一是把包名、Ability 名、模块名这些容易写错的常量集中在一处维护二是把停止—启动—抓日志这种固定组合变成一条命令三是在 CI 里可以被稳定调用不依赖人的记忆。设计上我一般分四层参数解析包名、Ability、设备 connectKey、是否带调试、设备检查没设备就早退别让后续步骤报一堆莫名其妙的错、动作执行start、stop、restart、辅助功能抓日志、截图、看任务栈。分层的好处是哪一层出问题一眼就能看出来。4.2 一份可以直接抄的脚本下面这份脚本我用了很久改一改常量就能上项目。核心是把 hdc 的调用统一收口到一个变量里这样多设备场景下只要传一个 connectKey 就能把命令定向到指定设备。#!/bin/bash # oh-app-ctl.sh 用法: ./oh-app-ctl.sh start|stop|restart|log|shot [connectKey] set -u BUNDLEcom.example.demo ABILITYEntryAbility MODULEentry USER_ID100 CMDhdc if [ -n ${2:-} ]; then CMDhdc -s $2 fi ensure_device() { local n n$($CMD list targets 2/dev/null | grep -v ^\[Empty\]$ | grep -c . || true) if [ $n -eq 0 ]; then echo 没有可用设备先检查线和授权状态 exit 1 fi } do_stop() { ensure_device $CMD shell aa force-stop $BUNDLE sleep 1 } do_start() { ensure_device $CMD shell aa start -a $ABILITY -b $BUNDLE -m $MODULE -U $USER_ID -D } case ${1:-} in start) do_start ;; stop) do_stop ;; restart) do_stop; sleep 1; do_start ;; log) ensure_device; $CMD hilog hilog-$(date %H%M%S).log ;; shot) ensure_device $CMD shell snapshot_display -f /data/local/tmp/shot.jpeg $CMD file recv /data/local/tmp/shot.jpeg ./shot.jpeg ;; *) echo 用法: $0 start|stop|restart|log|shot [connectKey] ;; esac几个细节解释一下。ensure_device那一段用 grep 过滤[Empty]是因为某些版本的 hdc 在没设备时也会输出这个占位行直接判空会误判。restart里 stop 之后睡了 1 秒是给系统回收进程留时间不睡的话有时候新的启动请求会撞在旧的回收流程上出现启动失败。-D默认打开因为日常调试基本都需要。日志重定向用的文件名带时间戳避免多次抓取互相覆盖。4.3 把日志和截图串进调试闭环脚本里那两个辅助功能看着不起眼实际是排查问题的关键。当你遇到启动命令成功但界面白屏这种问题光看命令行输出什么都看不出来必须同时拿到日志和画面。日志告诉你应用走到了哪一步、有没有报错截图告诉你设备上实际显示了什么。两者放在一起判断范围立刻缩小。这里有个小技巧抓日志之前先清一次日志缓冲能让后面的日志文件干净很多不然你翻半天前面全是别的模块的输出。清缓存、执行动作、停止抓取这个顺序固定下来形成肌肉记忆。截图文件我习惯先放在设备的临时目录再用拉文件的命令取回来比让 hdc 直接落到本地要稳因为有些版本不支持直接输出到本地路径。4.4 系统层的辅助命令有些时候应用本身没问题是系统的状态需要干预。比如设备息屏了启动命令执行了但屏幕上没东西这时候先把屏幕唤醒再启动结果就完全不一样。再比如你要确认目标 Ability 到底有没有被系统识别用hdc shell aa dump -a把 Ability 相关的信息 dump 出来看要确认当前任务栈里堆了什么用hdc shell aa dump -l。这两条命令的价值在于它给你的是系统的真实视图而不是你的预期。还有一种情况是应用启动后界面渲染不正常画面错乱或者有大片色块。这类现象不要一上来就怀疑应用代码先做两步force-stop 之后重新冷启动一次看能不能复现能复现再抓日志和截图。很多所谓的渲染问题本质上是进程被异常终止之后 Surface 没被正确重建重启一次就正常了。把重启一次看是否还复现作为排查渲染问题的第一步能过滤掉相当一部分误报。5. 高频问题速查5.1 设备连接类问题对照表现象大概率原因处理方式hdc list targets输出[Empty]线材、接口、驱动或 Linux 权限换线换口Linux 下补 udev 规则后重新插拔设备显示未授权设备端没确认调试授权在设备上确认必要时重启 server 后重连设备显示 offlineserver 与设备状态不同步先hdc kill再hdc start然后重连命令打到了错误的设备多设备未指定 connectKey所有命令统一加-s脚本里做成参数一台机器跑两条流水线互相抢共用了同一个 server 端口给每条流水线分配独立的HDC_SERVER_PORT这张表里的每一条我都实际遇到过。其中打到错误的设备最隐蔽因为命令不会报错只是结果不对然后你就会去怀疑应用白白浪费半天。多设备环境下养成统一带-s的习惯能彻底杜绝这一类问题。5.2 启停动作类问题对照表现象大概率原因处理方式aa start报目标不存在包名、Ability 名或模块名有误用bm dump -n 包名核对真实名字补-m命令成功但界面没变化目标 Ability 已在栈顶或被其他应用挡住先 force-stop 再启动用aa dump -l看栈多用户设备上启动无反应未指定用户 ID命令里显式加-U确认应用装在该用户下force-stop 后进程立刻复活事件监听、常驻任务或自启机制拉起抓 hilog 看拉起方从源头改配置kill -9提示无权限当前版本限制了 shell 杀进程回退使用 force-stop清数据后配置丢失导致启动异常数据被清但首次启动逻辑未覆盖启动前确认初始化流程或改用缓存清理重启设备后应用不自启未解锁状态下数据区不可用解锁后再验证自启与手动启动分开测5.3 几个我踩过的坑第一个坑是把-a和-b写反。这两个参数一个名字一个包名看起来差别很大但赶时间的时候真的会写错而且报错信息不会直接告诉你你写反了只会说输入不合法。我的对策是在脚本里把这两个值定义成清晰的常量名命令里直接引用变量不再手敲。第二个坑是以为重启 server 会断开设备。其实重启之后设备通常还会重新出现在列表里不需要你重新插拔。以前我不知道这一点每次都拔线重插折腾了很长一段时间。第三个坑是在自动化流程里没有等待时间。force-stop 之后立刻 start偶尔会失败加上一秒的间隔之后就没再出现过。这种偶发问题在单次手动操作时几乎碰不到一进 CI 就暴露而且因为没有规律很容易被当成环境不稳定糊过去。凡是偶尔失败的动作都要怀疑是不是缺少必要的等待。6. 长期使用后的一些体会真正让这套东西用起来顺手的不是把命令背下来而是把变量和流程固化下来。包名、Ability 名、模块名、目标用户这四个值在任何一次会话里都是固定的把它们写进脚本顶部的常量区后面所有命令都引用它们能避免绝大部分拼写错误。这件事听起来很基础但我在不止一个团队里见过有人每次手敲包名然后花时间排查一个根本不存在的系统问题。另一个体会是关于验证顺序的。遇到问题的时候我的固定顺序是先确认设备在不在、对不对再确认包装没装、名字对不对然后才是启动动作本身最后才是看界面和日志。这个顺序之所以有效是因为它从外到内逐层收敛每一层都能给出确定的结论不会让你在应用代码里找一个其实出在连接层的毛病。反过来一上来就怀疑应用代码是最容易迷路的方向。最后一个小习惯分享给你把hdc shell aa dump -a和hdc shell bm dump -a这两条命令的输出各存一份到本地文件改完应用之后对比一下差异很多感觉不对劲的地方立刻就能看出来。设备上的状态是会变的而你脑子里的记忆不会用文件做基准比用脑子做基准可靠得多。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →