<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>鸿蒙 on Trevor's Blog</title><link>http://mings.in/tags/%E9%B8%BF%E8%92%99/</link><description>Recent content in 鸿蒙 on Trevor's Blog</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Tue, 13 Oct 2026 09:12:18 +0800</lastBuildDate><atom:link href="http://mings.in/tags/%E9%B8%BF%E8%92%99/index.xml" rel="self" type="application/rss+xml"/><item><title>把任意 AI Agent 打造成鸿蒙开发工具台</title><link>http://mings.in/posts/harmony-ai/</link><pubDate>Tue, 13 Oct 2026 09:12:18 +0800</pubDate><guid>http://mings.in/posts/harmony-ai/</guid><description>通用 agent 写 ArkTS 会翻车，原因就三个洞。它不认识 ArkTS 的装饰器和构建命令；它手里没有一份能查的官方文档，只能凭印象编 API；就算代码写对了，它也不知道怎么把包构建出来、装进设备、送到应用市场。</description><content:encoded><![CDATA[<h2 id="换一个-agent-解决不了的问题">换一个 agent 解决不了的问题</h2>
<p>通用 agent 写 ArkTS 会翻车，原因就三个洞。它不认识 ArkTS 的装饰器和构建命令；它手里没有一份能查的官方文档，只能凭印象编 API；就算代码写对了，它也不知道怎么把包构建出来、装进设备、送到应用市场。</p>
<p>洞可以一个个堵。华为和社区把堵洞的东西做成了可以外挂的工具，从零装到能用是八步。这篇按顺序走一遍，每一步给命令，也给该看到的输出。最后拿 DeepSeek Harness 把三样接进一个真 agent，看它到底认不认。</p>
<h2 id="要装的是四件东西">要装的是四件东西</h2>
<p>DevEco Studio 或者它的命令行工具是地基，构建、设备、签名这些真活都在它身上。@deveco/deveco-cli 是工具集，命令名 devecocli，负责把能力装进别的 agent。</p>
<p>两个 MCP 分工不一样。远程的开发者知识 MCP 管查文档，本地的 deveco-mcp 管 .ets 和 C、C++ 的语法检查。最后一件是 agc-cli，社区写的上架命令行，把 AGC 后台的操作搬回终端。</p>
<p>另外先备一个华为开发者账号，装地基和生成签名都要用。</p>
<h2 id="第一步装地基">第一步：装地基</h2>
<p>去开发者联盟的下载中心（developer.huawei.com/consumer/cn/download/），登录后选 macOS 版。Apple Silicon 的机器要的是 macOS ARM 那一档。</p>
<p>装完记一下路径，默认是 /Applications/DevEco-Studio.app。这一步不能跳，devecocli 后面所有的真活都要它。</p>
<p>不想装整个 IDE 的话，另一条路是同一页面上的命令行工具。体积小得多，但官方专门提了一句：</p>
<blockquote>
<p>Set <code>DEVECO_CLI_CLT_PATH</code> to the Command Line Tools root when DevEco Studio is not installed; CLT is not discovered automatically from PATH or default installation directories.</p>
</blockquote>
<p>它不会自己从 PATH 或者默认目录里被发现，必须手写环境变量指过去：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 装了 IDE</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">DEVECO_CLI_STUDIO_PATH</span><span class="o">=</span>/Applications/DevEco-Studio.app
</span></span><span class="line"><span class="cl"><span class="c1"># 只装命令行工具</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">DEVECO_CLI_CLT_PATH</span><span class="o">=</span><span class="nv">$HOME</span>/command-line-tools
</span></span></code></pre></div><p>写进 ~/.zshrc 才会一直生效。Studio 首次启动会引导下载 HarmonyOS SDK，这一步要联网，也慢，别在没网的地方干等。</p>
<h2 id="第二步装-devecocli-工具集">第二步：装 devecocli 工具集</h2>
<p>两个 npm 包一起装：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install -g @deveco/deveco-cli @deveco/deveco-code
</span></span></code></pre></div><p>装出来两个命令。deveco 是 Agent 本体，基于 OpenCode 定制；devecocli 是工具集。两个包 255 个依赖、267 MB。</p>
<p>装完先验一下，别急着往下走：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli --help
</span></span><span class="line"><span class="cl">devecocli device list
</span></span></code></pre></div><p>第二条会列出连着的设备。如果它报 DevEco Studio 找不到，问题不在这一条命令上，回头查第一步那个环境变量。</p>
<h2 id="第三步把技能包装进你的-agent">第三步：把技能包装进你的 agent</h2>
<p>这是「任意 agent」那一半的答案，一条命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli init --skill --agent dsh,cursor,codex
</span></span></code></pre></div><p>不给 &ndash;agent 就是装到所有能识别的宿主。想装到指定目录用 &ndash;path，要覆盖已有配置加 -f。</p>
<p>装进去的东西长这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">node_modules/@deveco/deveco-cli-common/SKILL.md
</span></span></code></pre></div><p>24,690 字节，开头的触发条件写得很直白：</p>
<blockquote>
<p>Use &lsquo;devecocli&rsquo; for HarmonyOS apps (scaffold, build, run, debug, devices, emulators, logs, docs, UI inspection, 鸿蒙开发文档, skills). Use it if workspace has build-profile.json5 or oh-package.json5, or user mentions HarmonyOS, 鸿蒙, DevEco, ArkTS, ArkUI &hellip;</p>
</blockquote>
<p>agent 读到它之后，碰到鸿蒙工程或者提到鸿蒙，就会自己想到用 devecocli。同一个包还带着一份离线文档语料，docs.zip 有 40.5 MB，解开是 31,477 个文件，另加 15.2 MB 的检索索引。<code>devecocli docs search</code> 查的就是它，不联网也能用。</p>
<p>支持的宿主是官方给的名单：atomcode、codebuddy、claude-code、codex、cursor、dsh、deveco、opencode、pi、qoder、trae-cn，十一个。名单外的宿主也有条土路，技能包说到底就是一个 Markdown 文件加一份文档语料，把它放进那个宿主的 skill 目录就是同一回事。</p>
<p>想批量装或者查已经装了什么，走 skills 子命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli skills list
</span></span><span class="line"><span class="cl">devecocli skills add --all
</span></span></code></pre></div><h2 id="第四步装本地那个语法检查-mcp">第四步：装本地那个语法检查 MCP</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli init --mcp
</span></span></code></pre></div><p>它配的是 deveco-mcp，一个 stdio MCP，只暴露一个 check 工具，专门给 .ets、C、C++ 做语法检查。接进 agent 之后，它每改完一轮代码先自查一遍，不用等整包构建才报错。官方在说明书里还叮嘱了一句，一轮编辑跑一次就行，不要每次编辑都跑。</p>
<p>官方另有一句限定：所有支持的宿主都能配全局和项目级 MCP，只有 qoder 不支持项目级。</p>
<p>这个服务能手动起，调试的时候用得上：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli serve mcp
</span></span></code></pre></div><p>它认几个环境变量，PROJECT_PATH、DEVECO_PATH，还有调 Node 内存上限的 NODE_MAX_OLD_SPACE_SIZE。</p>
<h2 id="第五步接官方知识-mcp">第五步：接官方知识 MCP</h2>
<p>这一件最通用，任何支持 MCP 的客户端都是往配置里塞一段：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;mcpServers&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;harmonyos_developer_knowledge&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://connect-api.cloud.huawei.com/api/developerknowledge/mcp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;http&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>DevEco Code 自己带 MCP 管理命令，一行就够：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">deveco mcp add harmonyos_developer_knowledge <span class="se">\
</span></span></span><span class="line"><span class="cl">  --url https://connect-api.cloud.huawei.com/api/developerknowledge/mcp
</span></span></code></pre></div><p>跑完它会自己报状态：</p>
<pre tabindex="0"><code>●  ✓ harmonyos_developer_knowledge connected
</code></pre><p>它只有两个工具。searchDocuments 按关键词搜，返回匹配的文本块和文档标识；getDocumentsById 拿标识换全文，一次最多十篇。成本要有数：搜一次三千字符左右，拉一篇全文三万五千字符，别让它一次拉十篇。官方说这份语料跟官网准实时同步，延迟不超过 30 分钟，这是这条路跟「让模型凭记忆答题」最要紧的差别。</p>
<h2 id="以-deepseek-harness-为例三件东西怎么落到一个真-agent-上">以 DeepSeek Harness 为例：三件东西怎么落到一个真 agent 上</h2>
<p>前面几步讲的是做什么，这一节拿 dsh 走一遍落到哪。它正好在官方那份宿主名单里，所以三件东西都能自动接。</p>
<p>技能包一条命令，落到 dsh 自己的技能目录：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli init --skill --agent dsh
</span></span><span class="line"><span class="cl">ls ~/.dsh/skills
</span></span></code></pre></div><p>目录结构跟它已有的技能一模一样，一个目录一份 SKILL.md，文件开头就两行 frontmatter，写 name 和 description。这台机器的 ~/.dsh/skills 里原本已经躺着七个 memorix- 开头的技能，deveco-cli 进来之后跟它们并列。</p>
<p>MCP 要写进 dsh 的配置文件 cordis.patch.yml。它的配置是一层层的 profile patch，MCP 走它自己的 mcp-client 插件，一个服务一条记录。知识 MCP 写进去是这样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">insert</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">mcp-harmonyos-knowledge</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;@deepseek-ai/dsh-mcp-client&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">serverName</span><span class="p">:</span><span class="w"> </span><span class="l">harmonyos_developer_knowledge</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">transport</span><span class="p">:</span><span class="w"> </span><span class="l">streamable-http</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">https://connect-api.cloud.huawei.com/api/developerknowledge/mcp</span><span class="w">
</span></span></span></code></pre></div><p>本地那个语法检查 MCP 只换 transport 和启动命令：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">mcp-deveco-syntax</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;@deepseek-ai/dsh-mcp-client&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">serverName</span><span class="p">:</span><span class="w"> </span><span class="l">deveco</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">transport</span><span class="p">:</span><span class="w"> </span><span class="l">stdio</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l">devecocli</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">serve, mcp]</span><span class="w">
</span></span></span></code></pre></div><p>它只认两种 transport，stdio 是本地起的进程，streamable-http 是服务端。几个默认值值得知道：单次工具调用超时 60 秒，服务端下发的指令上限 32,768 字节，连接断了会自己重连，最多连试十次。</p>
<p>改完不用重启去猜，dsh 有 dump 命令，能把合成后的配置整个打出来：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dsh --profile &lt;你的 profile&gt; --patch ./hw-mcp.yml --dump-config
</span></span></code></pre></div><p>配置合成进 profile 树之后，工具名就真的出现在它的工具表里了，我拿一个隔离的 DSH_HOME 跑过一遍：</p>
<pre tabindex="0"><code>mcp__harmonyos_developer_knowledge__searchDocuments
mcp__harmonyos_developer_knowledge__getDocumentsById
</code></pre><p>命名规则是 mcp 加两个下划线加服务名加工具名，所以同一个 agent 里挂好几个 MCP 也不会撞名。到这一步，你在 dsh 里问一句鸿蒙的接口怎么写，它会先去查官方文档，而不是凭印象编。</p>
<p><img loading="lazy" src="/images/harmony-ai/fig-dsh.png"></p>
<h2 id="第六步登录与签名">第六步：登录与签名</h2>
<p>要真机跑，这一步绕不过去。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli auth login
</span></span></code></pre></div><p>它会开浏览器走华为开发者账号的 OAuth。登录状态用 <code>devecocli auth status</code> 看，加入的团队账号用 <code>devecocli auth team list</code> 列。然后生成签名：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli signature generate --product default
</span></span></code></pre></div><p>顺序官方给得很清楚，auth login、signature generate、build、run，一步都不能少。漏了哪步都有对应的报错：没登录会说 <code>Not logged in. Run devecocli auth login first</code>，令牌过期说 <code>Invalid AccessToken. Sign in and try again</code>，签名换了说 <code>error:install sign info inconsistent</code>，这时候 run 加 &ndash;uninstall，或者签名重新生成时加 &ndash;force。</p>
<h2 id="第七步装-agc-cli">第七步：装 agc-cli</h2>
<p>上架走 AGC，以前只能在浏览器里一页页点。这个开源命令行把接口整理成一张可检索的注册表，实测 159 个接口、13 个家族，每条都带方法、路径、必填参数，还有一条指回华为官方文档的 sourceUrl。</p>
<p>Homebrew 那条路要先信任它的 tap：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brew tap createitv/tap
</span></span><span class="line"><span class="cl">brew trust createitv/tap
</span></span><span class="line"><span class="cl">brew install agc-cli
</span></span></code></pre></div><p>或者直接下官方 Release 的预编译二进制，有 darwin_arm64 的包，核完哈希再用：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">shasum -a <span class="m">256</span> agc-cli_0.2.0_darwin_arm64.tar.gz
</span></span><span class="line"><span class="cl">./agc version
</span></span></code></pre></div><p>凭据有三种配法，走一种就行：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">agc auth login --client-id &lt;id&gt; --client-key &lt;key&gt;
</span></span><span class="line"><span class="cl">agc auth login --api-client-file client.json
</span></span><span class="line"><span class="cl">agc auth login --service-account-file sa.json
</span></span></code></pre></div><p><code>agc auth check</code> 看当前生效的是哪一套，<code>agc auth list</code> 列已存的所有档，<code>agc auth token</code> 给当前这档签一个临时令牌。</p>
<p>它默认只预演，真发之前你会先看到请求长什么样：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">agc publishing app-info-query --invoke --query <span class="nv">appId</span><span class="o">=</span><span class="m">123456789</span>
</span></span><span class="line"><span class="cl"><span class="o">{</span><span class="s2">&#34;data&#34;</span>:<span class="o">{</span><span class="s2">&#34;method&#34;</span>:<span class="s2">&#34;GET&#34;</span>,<span class="s2">&#34;url&#34;</span>:<span class="s2">&#34;https://connect-api.cloud.huawei.com/api/publish/v2/app-info?appId=123456789&amp;lang=zh-CN&#34;</span>,<span class="s2">&#34;dryRun&#34;</span>:true<span class="o">}}</span>
</span></span></code></pre></div><p>确认没问题，再显式加 &ndash;dry-run=false 才真发。每个接口的返回里还带一段 affordances，写着下一步能跑哪条命令。agent 不用背命令树，顺着返回往下走就行。</p>
<h2 id="第八步跑一遍看它自己验货">第八步：跑一遍，看它自己验货</h2>
<p>装完这些，从工程到设备是一条直链：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli build
</span></span><span class="line"><span class="cl">devecocli emulator list
</span></span><span class="line"><span class="cl">devecocli emulator start <span class="s2">&#34;Phone&#34;</span>
</span></span><span class="line"><span class="cl">devecocli run
</span></span></code></pre></div><p>模拟器第一次用要先接受协议，交互式是 <code>devecocli emulator license</code>，要 TTY，脚本里用 <code>devecocli emulator license accept</code>。镜像下载失败别连着重试，官方说得很直白，这条交给用户手动跑。</p>
<p>跑完看工程里多出来的两个目录。<code>.verify</code> 放校验产物，<code>docs/screenshots</code> 放截图，后面这个是自动跑出来的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docs/screenshots
</span></span><span class="line"><span class="cl">  01-home.png                          首页
</span></span><span class="line"><span class="cl">  03-quick-entry.png                   快捷记账
</span></span><span class="line"><span class="cl">  05-stats-mpchart.png                 统计图表
</span></span><span class="line"><span class="cl">  06-stats-retry.png                   统计失败后重试
</span></span><span class="line"><span class="cl">  08-trip-edit.png                     行程编辑
</span></span><span class="line"><span class="cl">  09-home-empty.png                    空态
</span></span><span class="line"><span class="cl">  10-real-device-home.png              真机
</span></span><span class="line"><span class="cl">  11-real-device-trip-detail-finish.png
</span></span><span class="line"><span class="cl">  12-light-home.png                    浅色
</span></span><span class="line"><span class="cl">  15-dark-home.png                     深色
</span></span><span class="line"><span class="cl">  19-dark-to-system-fixed.png
</span></span></code></pre></div><p>编号从 01 排到 19。里面有两张真机截图，说明它把包装进设备真跑过；浅色、深色各一套，说明主题切换也在它的流程里；还有空态和统计失败后的重试态，说明它不满足于走顺路，会把边角的状态一个个摆出来。这些都不是手截的。</p>
<p>这一步的意义在闭环。以前 agent 写完代码交给你，好不好看得你自己去看；现在它能自己看到结果，截图落成文件，下一轮改什么就有依据。这也是我更愿意把 devecocli 归到「工具」而不是「客户端」的原因：它不是替你写代码的那只手，它是让 agent 能自己验货的那只眼。</p>
<p><img loading="lazy" src="/images/harmony-ai/fig-workbench.png"></p>
<h2 id="每步一条验证命令">每步一条验证命令</h2>
<p>装完别靠感觉，一条条过：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli --help                  <span class="c1"># 工具集在</span>
</span></span><span class="line"><span class="cl">devecocli device list             <span class="c1"># 找得到设备</span>
</span></span><span class="line"><span class="cl">devecocli docs search ArkTS       <span class="c1"># 离线文档在</span>
</span></span><span class="line"><span class="cl">devecocli skills list             <span class="c1"># 技能包装上了</span>
</span></span><span class="line"><span class="cl">deveco mcp list                   <span class="c1"># 知识 MCP 连上了</span>
</span></span><span class="line"><span class="cl">devecocli auth status             <span class="c1"># 登录状态</span>
</span></span><span class="line"><span class="cl">agc auth check                    <span class="c1"># AGC 凭据配好了</span>
</span></span><span class="line"><span class="cl">agc endpoints                     <span class="c1"># 注册表读得到</span>
</span></span></code></pre></div><p>哪条不对，问题就在它对应的那一步，不用到处翻。</p>
<h2 id="一点判断">一点判断</h2>
<p>四件东西里我会先装哪件？先装地基，因为它卡着后面全部。但如果你只想花二十分钟试水，那就先接远程的知识 MCP，一个 URL、不用设备、不用账号，堵的又是最致命的那个洞。</p>
<p>顺序别倒过来。技能包和本地 MCP 都挂在 devecocli 上，devecocli 的真活又挂在 DevEco Studio 上，地基不牢，后面每一步都会撞同一句话。</p>
<p>你现在让自己常用的 agent 写鸿蒙代码吗？它最常在哪一步翻车？评论区聊聊。</p>
]]></content:encoded></item><item><title>Kotlin 2.4 来了，鸿蒙开始适配 2.4：KMP 的两条战线</title><link>http://mings.in/posts/kmp-24-ohos/</link><pubDate>Fri, 18 Sep 2026 12:34:38 +0800</pubDate><guid>http://mings.in/posts/kmp-24-ohos/</guid><description>前段时间我写过一篇鸿蒙 KMP 的文章，当时查到一个关键信息：社区发布的正式版，是基于 KMP 2.2.21 / CMP 1.9.2 做的鸿蒙适配。</description><content:encoded><![CDATA[<h2 id="一条分支名暴露了正在发生的事">一条分支名，暴露了正在发生的事</h2>
<p>前段时间我写过一篇鸿蒙 KMP 的文章，当时查到一个关键信息：社区发布的正式版，是基于 <strong>KMP 2.2.21 / CMP 1.9.2</strong> 做的鸿蒙适配。</p>
<p>前两天再去翻那个仓库，发现它的分支列表里多了一行字：</p>
<blockquote>
<p><strong>develop-2.4</strong>
框架 KMP <strong>2.4.21</strong> _ CMP <strong>1.12.0</strong> 版本对应的文档集合</p>
</blockquote>
<p>而且这个分支<strong>21 小时前刚更新过</strong>。</p>
<p>另外还有一条：</p>
<blockquote>
<p><strong>main-2.2</strong>（默认分支）
框架 KMP <strong>2.2.21</strong> _ CMP <strong>1.9.2</strong> 版本对应的文档集合</p>
</blockquote>
<p>一边是已经发出去的正式版，一边是正在推进的新基线。</p>
<p><strong>KMP 在鸿蒙这条线上，开始跟上游的最新版本对齐了。</strong></p>
<p>而几乎同一时间，上游 JetBrains 刚发布 Kotlin 2.4.20。</p>
<p>两条线，撞在了一个时间点上。</p>
<h2 id="先把上游这条线讲清楚kotlin-2420-改了什么">先把上游这条线讲清楚：Kotlin 2.4.20 改了什么</h2>
<p>如果你只扫一眼更新日志，可能会觉得&quot;哦，又是个修修补补的版本&quot;——两百多条变更里，大半是 bugfix。</p>
<p>但里面有几个点，对<strong>写 KMP 的人</strong>来说很实在。</p>
<p><strong>第一，Swift 互操作终于&quot;能打了&quot;。</strong></p>
<p>这次 Swift export 支持把 Kotlin 的 <code>sealed class</code> 直接映射成 Swift 的 <code>enum</code>：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">// 不用再写 default 了</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">name</span> <span class="p">=</span> <span class="k">switch</span> <span class="n">shape</span><span class="p">.</span><span class="n">sealedType</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="p">.</span><span class="n">circle</span><span class="p">(</span><span class="kd">let</span> <span class="nv">t</span><span class="p">):</span>    <span class="s">&#34;圆&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="p">.</span><span class="n">rectangle</span><span class="p">(</span><span class="kd">let</span> <span class="nv">t</span><span class="p">):</span> <span class="s">&#34;方&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>   <span class="c1">// ← 穷尽检查</span>
</span></span></code></pre></div><p>以前处理这种多态类型，Swift 侧要么写一堆 <code>if let</code>，要么被迫加一个永远走不到的 <code>default</code> 分支——<strong>穷尽性检查直接失效</strong>。现在编译器能帮你查漏了。</p>
<p>还有<strong>跨语言继承</strong>：Kotlin 类型可以继承 Swift 的类/协议（含纯 Swift 库），不用再包一层。</p>
<p>这两件事指向同一个方向：<strong>KMP 在 iOS 侧一直被人诟病的&quot;互操作摩擦&quot;，正在被一点点磨平。</strong></p>
<p><strong>第二，klib 增量编译进 Beta。</strong></p>
<pre tabindex="0"><code>kotlin.incremental.native=true
</code></pre><p>改一行 Kotlin 代码，不用再等整个 native 模块重编。这个体验改善，做过 Kotlin/Native 的人应该懂。</p>
<p><strong>第三，Kotlin/JS 的测试工具链从 Karma 换成了 Playwright</strong>——算是跟上时代。</p>
<p><strong>所以上游这条线的主旋律是：把跨语言的接缝处理得更顺。</strong></p>
<h2 id="再看鸿蒙这条线它现在走到哪了">再看鸿蒙这条线：它现在走到哪了</h2>
<p>要理解鸿蒙这边，得先看它的<strong>版本号规则</strong>——这是昨天刚发布的正式版公告里写的：</p>
<blockquote>
<p>版本号采用「<strong>上游基线 + 鸿蒙适配序号</strong>」：2.2.21-1.0.0 / 1.9.2-1.0.0 表示基于上游 KMP 2.2.21、CMP 1.9.2 的鸿蒙适配正式版。</p>
</blockquote>
<p>这个规则很关键。它说明了一件事：</p>
<blockquote>
<p><strong>鸿蒙适配不是另起炉灶，而是在追上游的版本。</strong></p>
</blockquote>
<p>所以你会看到 <code>2.2.21-1.0.0</code> 这样的编号——前面的 <code>2.2.21</code> 是它对应的上游 Kotlin 版本，后面的 <code>1.0.0</code> 是鸿蒙适配自己的迭代号。</p>
<p>已经发布并进了 Maven 仓的有：</p>
<table>
	<thead>
			<tr>
					<th>组件</th>
					<th>版本</th>
					<th>状态</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>kotlin</td>
					<td>2.2.21-1.0.0</td>
					<td>Maven 已发布</td>
			</tr>
			<tr>
					<td>compose-multiplatform</td>
					<td>1.9.2-1.0.0</td>
					<td>Maven 已发布</td>
			</tr>
			<tr>
					<td>compose-multiplatform-core</td>
					<td>1.9.2-1.0.0</td>
					<td>Maven 已发布</td>
			</tr>
			<tr>
					<td>skiko</td>
					<td>0.9.22.2-1.0.0</td>
					<td>Maven 已发布</td>
			</tr>
			<tr>
					<td>akinterop</td>
					<td>1.0.1</td>
					<td>Maven 已发布</td>
			</tr>
			<tr>
					<td>KMP OHOS Support</td>
					<td>1.0.0</td>
					<td>插件市场已发布</td>
			</tr>
	</tbody>
</table>
<p>而 <code>develop-2.4</code> 这条新分支，目标基线就是 <strong>KMP 2.4.21 / CMP 1.12.0</strong>。</p>
<p>开发节奏公告里也写了：</p>
<blockquote>
<p><strong>develop</strong> 分支：用于每月开发态代码合入，<strong>每月准出后合入 main 分支</strong>。</p>
</blockquote>
<p>翻译一下：<strong>每月推进一版，成熟了才合进正式线。</strong> 不是拍脑袋发版，而是有节奏的滚动。</p>
<h2 id="这条线上最近在忙什么">这条线上最近在忙什么</h2>
<p>翻了 <code>develop-2.4</code> 的提交记录，最近的动作挺有意思。</p>
<p>最新一条是把 <strong>CAPI 用户手册适配到 API 26</strong>——把 OHOS CAPI 版本更新到 API26，并且同步更新了「模块、最低 API 与典型依赖库」，还专门加了一节说明**「CAPI 升级至 API 26 后导致的特殊变更」**。</p>
<p>另一条是更新 <code>akinterop</code> 到 1.0.1、<code>sqldelight</code> 到 2.2.1-1.1.0。</p>
<p><strong>这两个方向，恰好对应鸿蒙跨平台落地的两个真实卡点：</strong></p>
<ul>
<li><strong>CAPI 跟着鸿蒙系统版本走</strong>——系统升级，桥接层就得跟着升，这是&quot;不断档&quot;的问题</li>
<li><strong>三方库的适配版本号</strong>——生态库能不能跟上，决定了项目敢不敢用</li>
</ul>
<h2 id="顺带一提cmp-112-上游给的亮点">顺带一提：CMP 1.12 上游给的亮点</h2>
<p>既然 <code>develop-2.4</code> 这条线要跟到 CMP 1.12.0，那 1.12 上游改了什么，也值得看一眼。</p>
<p>CMP 1.12.0 是 8 月 25 日发布的，最亮的一条在 Desktop 侧：</p>
<blockquote>
<p>MCP server was introduced for Compose Hot Reload that enables <strong>AI agents to interact with a running Compose application in real time</strong></p>
</blockquote>
<p>翻译一下：<strong>给 Compose 热重载加了个 MCP server，让 AI agent 能实时和一个正在运行的 Compose 应用交互。</strong></p>
<p>这条挺微妙的——Compose 和 AI 的结合点，正在从&quot;用 AI 写 Compose 代码&quot;，走向&quot;<strong>让 AI 直接操作跑着的应用</strong>&quot;。</p>
<p>当然它也带了一批破坏性调整，迁移时要留心：</p>
<ul>
<li><code>NativeCanvas</code>、<code>NativePaint</code> 这些 typealias 的弃用级别被提到了 <strong>ERROR</strong></li>
<li><code>SwingPanel</code> 的 <code>background</code> 参数弃用</li>
</ul>
<p><strong>从 1.9.2 跳到 1.12.0，不只是版本号变了——迁移成本也是要算进去的。</strong></p>
<h2 id="两件事凑在一起说明了什么">两件事凑在一起，说明了什么</h2>
<p>把这两条线放在一起看，我看到的是一个挺清晰的图景：</p>
<p><strong>KMP 的&quot;一码多端&quot;，正在多条战线上同时推进。</strong></p>
<ul>
<li><strong>上游</strong>在打磨跨语言互操作的体验（Swift export、增量编译）</li>
<li><strong>鸿蒙这条社区线</strong>在追上游版本（2.2 → 2.4）、补系统适配（CAPI → API 26）、扩三方库生态</li>
</ul>
<p>它们不是同一个团队在做，但走的是<strong>同一个方向</strong>——让&quot;写一套 Kotlin，跑更多平台&quot;这件事，摩擦更小一点。</p>
<p>对开发者来说，判断标准其实很简单：</p>
<blockquote>
<p><strong>当你评估一个新平台时，问的不再是&quot;支持不支持&quot;，而是&quot;上游跟到哪个版本了、库够不够用、性能顶不顶得住&quot;。</strong></p>
</blockquote>
<p><strong>从&quot;能不能跑&quot;到&quot;跟得上吗&quot;，这本身就是个阶段性的变化。</strong></p>
<h2 id="但社区的真实声音比公告更值得听">但社区的真实声音，比公告更值得听</h2>
<p>公告总是漂亮的。我特意去翻了社区讨论区，那才是这条线真正的体温。</p>
<p><strong>最热的一帖，是问&quot;能不能尽快合并到 JetBrains 官方分支&quot;</strong>（30 天前发起，7 条回复）。</p>
<p>发起人的原话挺直接的：</p>
<blockquote>
<p>我现有的纯 KMP&amp;CMP 应用项目添加鸿蒙平台<strong>存在大量库不支持问题</strong>，编译过程还出现几个已适配的库存在内部依赖冲突，希望尽快合并上游方便整个 KMP 社区的库开发者新增 ohos 的库版本，<strong>CPF 社区对库支持的进度过于慢了</strong>。</p>
</blockquote>
<p>他具体点了三个库：<code>androidx-datastore</code>、<code>androidx-room3</code>、<code>androidx-sqlite</code>——&ldquo;这三个基本就是大家普遍使用的持久化方式的基础依赖库了&rdquo;。</p>
<p>然后是最扎心的部分：</p>
<blockquote>
<p>它们都没有 ohos <strong>x86</strong> 的支持。开发者在 win 平台几乎无法直接开发测试一个正常功能的应用，起码你得一直接一台鸿蒙手机充当测试平台……</p>
</blockquote>
<p>他还举了个具体的坑：从某个镜像仓库拉到的 <code>room3</code> alpha 版本，生成代码会报 <code>androidx.sqlite</code> 缺少实现；而用上游的 <code>room3 3.0.1</code> 就没问题。</p>
<p><strong>官方回复了，态度挺实在：</strong></p>
<blockquote>
<p>您好，<strong>关于 ohos x86 的支持，已在规划中</strong>，感谢您的关注。</p>
</blockquote>
<p>其他帖子也各有各的真实：</p>
<ul>
<li><strong>&ldquo;skia 渲染模式下页面帧生产被拖至约 3 秒一帧，疑似 VsyncOnDemand 延迟注销机制异常&rdquo;</strong>——性能问题</li>
<li><strong>&ldquo;material3 导致移植已有项目阻塞&rdquo;</strong>——组件库的坑</li>
<li><strong>&ldquo;当前 webView 有支持吗？&rdquo;</strong>——能力覆盖</li>
<li><strong>&ldquo;大家有项目真实上线吗？有遇到什么坑吗&rdquo;</strong>——底下有人回了四个字：<strong>&ldquo;正在做语音助手 KMP 版本适配&rdquo;</strong></li>
</ul>
<p><strong>这几条加起来，才是这条线的真实状态：能跑通、有人在用、但生态和性能还在补课。</strong></p>
<h2 id="为什么跟上上游是件慢活">为什么&quot;跟上上游&quot;是件慢活</h2>
<p>看到社区那句&quot;进度过于慢了&quot;，可能会有人觉得是投入不够。</p>
<p>但这里有个技术现实值得说清楚：<strong>鸿蒙适配改的是编译器后端，不是写个插件。</strong></p>
<p>翻 CPF-KMP-CMP 的仓库列表，你会看到这些名字：</p>
<p><code>kotlin</code>、<code>compose-multiplatform</code>、<code>compose-multiplatform-core</code>、<code>skiko</code>、<code>akinterop</code>、<code>cicd-toolkit</code>……</p>
<p><strong>它们是 Kotlin 编译器、Compose 框架、图形渲染层的鸿蒙分支。</strong></p>
<p>这意味着这份工作至少包括：</p>
<ul>
<li>给 Kotlin/Native 加一个新的<strong>编译目标</strong>（target）</li>
<li>把 Compose 的渲染管线接到鸿蒙的 <strong>Render Service</strong></li>
<li>让 Skiko 封装的 Skia 图形库能在鸿蒙上跑起来</li>
</ul>
<p><strong>而每次上游发新版本，这些改动都要重新对齐一遍。</strong></p>
<p>这也是&quot;上游基线 + 适配序号&quot;这个版本号规则真正的含义——<strong>它精确描述了这份工作的性质：持续的追踪，而不是一次性的移植。</strong></p>
<p>再看那句&quot;每月开发态代码合入，每月准出后合入 main&quot;，感觉就不一样了：</p>
<p><strong>这不是慢，这是在用一个能持续的速度，做一件很重的事。</strong></p>
<h2 id="我的几点看法">我的几点看法</h2>
<p><strong>第一，&ldquo;适配&quot;这件事的本质是&quot;跟上游&rdquo;，不是&quot;造轮子&quot;。</strong></p>
<p>从版本号规则（上游基线 + 适配序号）就能看出来——鸿蒙这条线的核心工作是把上游的编译器/UI 框架搬到鸿蒙上跑通，而不是自己另搞一套 API。</p>
<p><strong>这意味着它的天花板和上游绑在一起</strong>：上游把 Swift 互操作做顺了，那是 iOS 的红利；上游把编译器改进了，鸿蒙这边跟进才能吃到。</p>
<p><strong>第二，社区抱怨的那三条，其实指向同一个问题。</strong></p>
<p>x86 支持、三方库版本、性能——表面看是三件事，本质都是**&ldquo;生态成熟度&rdquo;**。</p>
<p>而生态成熟度有个残酷的特点：<strong>它不能靠热情解决，只能靠时间和真实项目打磨。</strong> 官方回&quot;已在规划中&quot;，是实话，也是实话里透出的无奈。</p>
<p><strong>第三，如果你在评估要不要上这条线，我给个判断框架。</strong></p>
<p>别问&quot;支持鸿蒙吗&quot;——<strong>问这三个问题</strong>：</p>
<ol>
<li><strong>上游跟到哪个版本了？</strong>（现在是 2.2.21 稳定、2.4.21 开发中）</li>
<li><strong>我依赖的库适配了吗？</strong>（尤其是持久化、网络这类基础库——<code>room3</code>、<code>datastore</code> 是公认痛点）</li>
<li><strong>我的开发机是什么？</strong>（如果需要 x86 模拟器，现在还得等；Mac M 系列 + 真机的组合比较稳）</li>
</ol>
<p><strong>三个都过了，再动手；有一个卡住，就先把方案记在备忘录里。</strong></p>
<h2 id="最后">最后</h2>
<p>Kotlin 2.4 和鸿蒙 2.4，看起来是两件不相干的事。</p>
<p>但它们其实在回答同一个问题：</p>
<blockquote>
<p><strong>一套 Kotlin 代码，能跑多少地方、跑得多好？</strong></p>
</blockquote>
<p>上游在把跨语言的接缝磨平，鸿蒙这条线在把版图铺开。</p>
<p><strong>这两件事，都在让那个数字变大。</strong></p>
<hr>
<p>想问问你：</p>
<p><strong>你手上的 KMP 项目，会因为&quot;鸿蒙适配跟到 2.4 了&quot;而重新考虑吗？</strong></p>
<p><strong>或者你已经踩过这条线的坑了？</strong> 评论区聊聊，我挺想知道真实项目的反馈。</p>
<hr>
<p><em>参考资料：CPF-KMP-CMP 社区仓库（AtomGit/GitCode）分支与 Release 公告、社区讨论区公开帖、JetBrains Kotlin 2.4.20 发布说明。</em></p>
]]></content:encoded></item><item><title>KMP 上鸿蒙了：一套 Compose 代码，现在能跑四个平台</title><link>http://mings.in/posts/kmp-harmony/</link><pubDate>Wed, 16 Sep 2026 21:26:40 +0800</pubDate><guid>http://mings.in/posts/kmp-harmony/</guid><description>先说个数字：四。</description><content:encoded><![CDATA[<h2 id="一件悄悄发生的版图扩张">一件悄悄发生的&quot;版图扩张&quot;</h2>
<p>先说个数字：<strong>四</strong>。</p>
<p>Android、iOS、桌面 —— 这是 Kotlin Multiplatform 这几年稳定覆盖的主力阵地（Web / Wasm 也在推进中）。写一套共享逻辑，加一套 Compose UI，几个平台一起跑。</p>
<p>而昨天，这个名单要加上第四个成员了：<strong>鸿蒙</strong>。</p>
<p>而且这次不是&quot;理论上能跑&quot;，是<strong>正式版本发布</strong>——组件进了 Maven 仓，Android Studio 插件市场上能搜到工具链插件，文档在 AtomGit 上。</p>
<p>最让我意外的是发布方那句话：</p>
<blockquote>
<p>已经在其他平台使用 CMP 的开发团队，界面业务代码可直接复用同一套 Compose 写法，不必为鸿蒙完全重新写一套 ArkUI。</p>
</blockquote>
<p><strong>不用重写 ArkUI。</strong> 这五个字对做跨端的人来说，分量挺重的。</p>
<h2 id="先把事实说清楚">先把事实说清楚</h2>
<p>这次发布的是 <strong>KMP&amp;CMP 鸿蒙首版本</strong>，2026 年 9 月 15 日发布。</p>
<p>几个关键信息：</p>
<ul>
<li><strong>谁做的</strong>：CPF-KMP-CMP SIG 社区伙伴共建（注意，是社区 SIG，不是 JetBrains 官方，后面我会细说这个区别）</li>
<li><strong>基于什么</strong>：源社区 <strong>KMP 2.2.21 / CMP 1.9.2</strong></li>
<li><strong>目标</strong>：把&quot;逻辑跨端共享、UI 原生渲染&quot;这套开发范式带到鸿蒙上</li>
<li><strong>获取方式</strong>：组件已发布至 Maven 仓；工具链插件 <strong>KMP OHOS Support</strong> 在 Android Studio 插件市场搜索安装</li>
</ul>
<p>官方对适用人群的定位也很清楚：</p>
<blockquote>
<p>对已经使用 KMP/CMP 做跨端业务的团队，该版本有助于降低迁到鸿蒙的改造成本。鸿蒙开发团队也可借助这套能力，做 Kotlin 与 ArkTS 混合开发，拓展技术选型。</p>
</blockquote>
<p>翻译一下就是：<strong>KMP 团队想上鸿蒙，现在成本低了；鸿蒙团队想用 Kotlin，现在有路子了。</strong></p>
<p>顺便说个背景——这事其实铺垫了三个月。今年 6 月社区版就开了 Beta，8 月 InfoQ 还报道过一版实测数据（渲染内存降 95%、GC 卡顿率降 90%）。现在算是正式落地。</p>
<h2 id="亮点一compose-直接跑鸿蒙不用重写-ui">亮点一：Compose 直接跑鸿蒙，不用重写 UI</h2>
<p>这是我觉得最有价值的一条。</p>
<p>在鸿蒙上开发，官方路线是 <strong>ArkTS + ArkUI</strong>。对已经在做 KMP/CMP 的团队来说，这意味着鸿蒙端要<strong>单独维护一套 UI</strong>——业务逻辑能共享，但界面得重写一遍。</p>
<p>这次发布把这条路铺平了：</p>
<blockquote>
<p>CPF-KMP-CMP 在鸿蒙侧提供了 compose.ui、compose.foundation、compose.material、compose.material3 的平台实现，可延续熟悉的声明式 UI 习惯开发鸿蒙端界面。</p>
</blockquote>
<p><strong>compose.material3 都在。</strong> 也就是说，你写惯了的那套 Compose 代码，在鸿蒙上是能认的。</p>
<p>对一个已经在 Android/iOS 上跑 CMP 的项目来说，这意味着<strong>鸿蒙端的接入成本，从&quot;重写一套 UI&quot;降到&quot;加一个 target&quot;</strong>。</p>
<p>差别有多大？做过跨端的人应该懂。</p>
<h2 id="亮点二kotlin--arkts-双向互操作">亮点二：Kotlin ↔ ArkTS 双向互操作</h2>
<p>第二条对存量项目很关键。</p>
<p>现实情况是：很多鸿蒙项目<strong>已经用 ArkTS 写了一堆东西</strong>（系统能力、业务模块），不可能整体推翻重写成 Kotlin。</p>
<p>这次的解法是 <strong>akinterop</strong>：</p>
<blockquote>
<p>共享逻辑可继续写 Kotlin，系统侧或存量模块仍用 ArkTS 时，用 akinterop 做双向互操作：注解 / 装饰器声明意图，工具链生成桥接，少写 C 胶水。基础类型、容器与异常可跨侧传递，存量工程可渐进接入，不必为互通推倒重来。</p>
</blockquote>
<p><strong>&ldquo;渐进接入，不必推倒重来&rdquo;</strong> —— 这句话是给存量项目的定心丸。</p>
<p>注解声明意图、工具链生成桥接——比手写 C 胶水的体验好太多，写过 JNI 的人都懂那种痛。</p>
<h2 id="亮点三统一渲染这条是技术核心">亮点三：统一渲染（这条是技术核心）</h2>
<p>这条比较硬核，但我觉得是整件事里最有技术含量的一环。</p>
<p>先说问题：<strong>传统的跨平台 UI 方案，通常要自建一套渲染管线。</strong></p>
<p>自建有什么坏处？报道里说得很直接：</p>
<blockquote>
<p>传统跨平台 UI 方案常自建渲染管线，这容易带来额外初始化与合成开销，也容易出现启动慢、动画和系统表现不一致等问题。</p>
</blockquote>
<p>自渲染意味着你的 UI 不走系统那条路——启动要多一步初始化，动画节奏跟系统对不齐，看起来就&quot;不像原生&quot;。</p>
<p>这次的方案是<strong>反过来的</strong>：</p>
<blockquote>
<p>CPF-KMP-CMP 将 Compose 绘制能力对接鸿蒙 Render Service / RenderNode 系统渲染能力，由鸿蒙系统统一完成合成与刷新调度（统一渲染需 API 19 及以上）。</p>
</blockquote>
<p><strong>把绘制能力交给鸿蒙系统自己去合成</strong>，收益是连锁的：</p>
<ul>
<li><strong>更快首帧</strong>：少了自渲染的初始化等待，启动和跳转更利落</li>
<li><strong>精细化局部刷新</strong>：状态变了才更新，还支持局部录制——<strong>只重绘变化的区域</strong></li>
<li><strong>原生组件友好混排</strong>：Compose 页面能和 ArkUI、C-API 混排，地图、视频这类原生能力更容易嵌进来</li>
<li><strong>对齐系统动画</strong>：帧节奏走系统的 DisplaySync，动画体验跟鸿蒙原生应用一致</li>
</ul>
<p>最后一条尤其重要。跨平台应用&quot;一眼假&quot;的原因，往往不是布局不对，而是<strong>动画的节奏感不对</strong>——帧率调度跟系统不在一个节拍上。</p>
<blockquote>
<p>⚠️ 注意一个限制：<strong>统一渲染需要 API 19 及以上</strong>。老设备上会退化。</p>
</blockquote>
<h2 id="性能账省了-20-编译时间降了-30-堆内存">性能账：省了 20% 编译时间，降了 30% 堆内存</h2>
<p>这次给了几个挺具体的数字，不像很多&quot;性能优化&quot;的模糊说法：</p>
<p><strong>编译侧（并行编译）：</strong></p>
<blockquote>
<p>将 LLVM IR 切分后多线程跑局部优化再合并，缩短大规模工程的编译等待；典型场景下约可节省 20%+ 耗时。</p>
</blockquote>
<p><strong>内存侧（CommonGC）：</strong></p>
<blockquote>
<p>采用并发标记—复制回收算法，降低内存碎片、减少 GC 停顿时间，典型场景下 KN 堆占用约可下降 30%。</p>
</blockquote>
<p>另外还有两个工程化的优化很实用：</p>
<ul>
<li><strong>模块化拆分</strong>：单一 KMP 工程可配置成多个 <code>.so</code>，配合分布式编译进一步缩短构建</li>
<li><strong>Debug 增量编译</strong>：macOS / Linux 上支持基于静态库缓存的增量编译——<strong>只重编改动的部分，不必每次全量重链</strong></li>
</ul>
<p>（做 KMP 的人看到&quot;全量重链&quot;四个字应该会有点应激反应）</p>
<p>还有个细节我挺喜欢——<strong>后台不空转</strong>：</p>
<blockquote>
<p>统一渲染路径支持按需申请 VSync 信号，当页面没有内容变化时，主动抑制刷新绘制；同时支持 LTPO 硬件特性，应用退到后台之后自动停止无效绘制循环……该行为为框架默认行为，不需要业务额外编写代码。</p>
</blockquote>
<p><strong>默认行为，不需要业务写代码</strong>。这种&quot;框架替你操心&quot;的设计，比让你手动去 <code>onPause</code> 里关动画友好多了。</p>
<h2 id="上手三步跑起来">上手：三步跑起来</h2>
<p>流程比我想象的简单：</p>
<blockquote>
<p>(1) DevEco Studio：用 6.0.0+ 下载 HarmonyOS SDK API 17+，打开工程里的 harmonyApp 配好调试签名。
(2) Android Studio：日常写 KMP&amp;CMP 代码。
(3) Android Studio：选设备和 harmonyApp 点 Run（推包时保持 DevEco 运行）。</p>
</blockquote>
<p><strong>注意第 3 步那个括号</strong>——推包时要保持 DevEco Studio 开着。这类&quot;两个 IDE 配合&quot;的细节最容易踩坑，官方提前说了。</p>
<p>文档在 <strong>AtomGit</strong>（搜 <code>CPF-KMP-CMP/docs</code>），想深入看的可以去翻。</p>
<h2 id="为什么鸿蒙这块拼图特别重要">为什么鸿蒙这块拼图特别重要</h2>
<p>说句实在话：对国内的 KMP 开发者，鸿蒙这块拼图可能比 Web/Wasm 重要得多。</p>
<p>原因很现实——<strong>在国内做移动端，鸿蒙已经不太是&quot;要不要考虑&quot;的问题了，而是&quot;什么时候必须做&quot;的问题。</strong> 尤其在政企、金融、车机、IoT 这些领域，推进速度比很多人预期的快。</p>
<p>而过去 KMP 团队面对鸿蒙的选择挺尴尬的：</p>
<ul>
<li><strong>共享逻辑能复用，但 UI 得重写</strong>（ArkUI）→ 相当于鸿蒙端要额外养一个人</li>
<li>或者<strong>干脆放弃 KMP</strong>，鸿蒙端单独起个工程 → 共享逻辑的优势全丢了</li>
</ul>
<p>这次发布的意义就在这儿：<strong>它把&quot;鸿蒙端 UI 也要重写&quot;这个成本项，从等式里拿掉了一大半。</strong></p>
<p>对一个小团队来说，这可能就是从&quot;做不起&quot;到&quot;做得起&quot;的差别。</p>
<h2 id="说点冷静的这事该怎么看">说点冷静的：这事该怎么看</h2>
<p>热点归热点，我得把几个边界说清楚，免得你判断失误。</p>
<p><strong>第一，这是社区 SIG 项目，不是 JetBrains 官方。</strong></p>
<p>发布方是 <strong>CPF-KMP-CMP SIG</strong>，不是 JetBrains 也不是华为官方发布。这不代表它不好——恰恰相反，这种&quot;生态底座&quot;通常就是靠社区 SIG 一点点啃出来的——但<strong>你心里的预期要摆正</strong>：</p>
<ul>
<li>长期维护性取决于社区活跃度，不像官方 API 那样有明确承诺</li>
<li>遇到问题，答案在社区（Issue/Discussion），不在官方文档里</li>
</ul>
<p><strong>第二，API 19+ 才能用统一渲染。</strong></p>
<p>如果目标设备里有大量低版本鸿蒙，那这项最大的性能红利你吃不到，得评估一下自己的设备分布。</p>
<p><strong>第三，但它解决的是个真问题。</strong></p>
<p>跨平台上鸿蒙，过去的选项基本是两个：</p>
<ul>
<li>要么<strong>鸿蒙端单独写 ArkUI</strong>，维护两套 UI</li>
<li>要么<strong>放弃鸿蒙</strong>，等官方支持</li>
</ul>
<p>现在多了第三条：<strong>共享逻辑照写，Compose UI 复用，ArkTS 存量用 akinterop 桥接</strong>。</p>
<p>对一个已经投了 KMP 的团队来说，这个第三条路的性价比，是很难忽视的。</p>
<h2 id="放回这几天的脉络里看">放回这几天的脉络里看</h2>
<p>有意思的是，这事跟咱们前两天聊的两个话题，正好串成了一条线。</p>
<p><strong>Jake Wharton 那篇</strong>里，他说得很清楚：</p>
<blockquote>
<p>会 Kotlin 已经不是职业优势了……但掌握 Kotlin Multiplatform 这类相对进阶的能力，仍然可能构成差异化优势。</p>
</blockquote>
<p><strong>Shopify 那篇</strong>的结论是：AI 把&quot;重复实现&quot;变便宜之后，&ldquo;共享 UI 派&quot;受冲击最大，而&quot;共享逻辑 + 原生 UI&quot;这条路更抗打。</p>
<p>然后是今天：<strong>KMP 的版图，又扩了一块——而且是块很大的版图。</strong></p>
<p>三件事指向同一个方向：<strong>跨平台的价值，正在从&quot;省人力&quot;转向&quot;架构治理 + 生态覆盖&rdquo;。</strong></p>
<p>一个平台一个平台地啃下来，本来就是 KMP 这条路线的核心叙事。鸿蒙这块拼图补上，对国内开发者尤其是——<strong>它可能是很多人第一次真正有理由认真看 KMP。</strong></p>
<hr>
<p>最后留个问题给大家：</p>
<p><strong>如果鸿蒙能用 Compose 写 UI，你会为它单独维护一套 ArkUI，还是直接把 KMP 项目扩到鸿蒙？</strong></p>
<p>还有——你手上的项目，鸿蒙是&quot;必须做&quot;还是&quot;再看看&quot;？</p>
<p>评论区聊聊，我挺想知道大家的真实处境。</p>
<hr>
<p><em>参考资料：</em></p>
<ul>
<li><em><a href="https://news.mydrivers.com/1/1151/1151178.htm">KMP&amp;CMP 鸿蒙首版本正式发布（快科技/驱动之家，2026-09-15）</a></em></li>
<li><em>文档集合：AtomGit 搜索 <code>CPF-KMP-CMP/docs</code></em></li>
</ul>
]]></content:encoded></item></channel></rss>