HarmonyOS 7 / API 26 项目中,调用 startChildProcess() 后拿到了 PID,日志也打印“success”,主进程却一直等不到图片解析、日志归档或 Native 计算的结果。这里最容易判断错的一点是:PID 只证明子进程创建成功,不代表业务已经完成,更不代表启动接口会把结果送回主进程。

华为在 2026 年 9 月 1 日更新的 ArkTS 子进程开发指导里,把三种创建方式的边界写得很明确:基础 ArkTS 子进程、支持传参的 ArkTS 子进程、支持传参的 Native 子进程,参数能力、异步能力和退出方式都不同;而且三种启动方式本身都没有提供父子进程之间的 IPC 通道。

这篇文章不从概念堆砌开始,直接用两个能复现的故障说明:为什么“启动成功”仍然收不到结果,以及怎样在写代码前就选对接口。

先看结论:PID 只回答“进程有没有起来”

创建方式参数与文件句柄适合的任务退出方式启动接口自带结果通道
startChildProcess不支持无需传参的轻量同步 ArkTS 任务onStart 执行完自动退出没有
startArkChildProcess支持 entryParams 和 fd需要传参、异步 I/O 或较长时间运行的 ArkTS 任务子进程主动调用 process.abort没有
startNativeChildProcess支持 entryParams 和 fdC/C++ 图像处理、编解码、计算密集任务Native 入口函数返回后退出没有

这里还有一个容易混淆的词:官方表格中的“支持 Binder IPC”,说的是子进程运行环境具备 Binder 能力,不等于 startArkChildProcess 或 startNativeChildProcess 已经替应用建立了结果回传通道。如果业务确实需要父子进程间的 Binder IPC,应使用官方给出的 Native 子进程 IPC 接口 OH_Ability_CreateNativeChildProcessWithConfigs,而不是等待启动 Promise 返回业务数据。

三种子进程生命周期与结果通道边界

案例一:日志归档拿到 PID,却一直等“归档完成”

复现条件

主进程启动一个基础 ArkTS 子进程,将当天日志压缩到本地文件。调用代码把 startChildProcess 返回的数字当作“任务结果”,随后等待一个并不存在的完成回调。

主进程会看到类似日志:

startChildProcess success, pid: 24831

但看不到“归档结果返回主进程”。这不是子进程一定失败了,而是我们误解了返回值。

正确的基础写法

import { ChildProcess } from '@kit.AbilityKit';

export default class LogArchiveProcess extends ChildProcess {
  onStart(): void {
    console.info('LogArchiveProcess onStart');
    // 这里只安排无需传参、同步完成的轻量任务。
    // onStart 返回后,基础 ArkTS 子进程自动退出。
    archiveTodayLogsSync();
  }
}

function archiveTodayLogsSync(): void {
  console.info('archiveTodayLogsSync finished');
}

主进程:

import { childProcessManager } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import LogArchiveProcess from '../process/LogArchiveProcess';

export async function startLogArchive(): Promise<number> {
  // 必须显式引用,避免子进程入口文件被构建工具优化掉。
  LogArchiveProcess.toString();

  try {
    const pid = await childProcessManager.startChildProcess(
      './ets/process/LogArchiveProcess.ets',
      childProcessManager.StartMode.SELF_FORK
    );
    console.info('child process created, pid=' + pid);
    return pid;
  } catch (error) {
    const err = error as BusinessError;
    console.error('startChildProcess failed, code=' + err.code);
    throw err;
  }
}

如何判断它真的执行了

不要只搜索“startChildProcess success”。还要在 HiLog 中检查子进程入口日志 LogArchiveProcess onStart,并验证归档文件是否实际生成。

这两个信号回答的是不同问题:

  1. 有 PID:进程创建成功。
  2. 有 onStart 日志:入口已经执行。
  3. 有目标文件且内容可读:业务动作完成。

如果任务需要异步文件 I/O、需要把文件句柄传进去,或者不能在一次同步 onStart 中结束,就不应继续使用基础 startChildProcess。

案例二:图片解析需要 fd 和异步 I/O,却用了基础子进程

问题是怎样发生的

图片解析通常需要三个条件:

  • 主进程把文件句柄交给子进程;
  • 子进程执行异步读取或解码;
  • 子进程在任务结束后明确退出。

基础 startChildProcess 不支持传参,并且官方明确限制为同步 ArkTS API。为了“先跑起来”而把异步解析硬塞进 onStart,常见结果是:PID 正常、入口日志正常,但异步任务尚未完成,基础子进程已经随着 onStart 返回而退出。

改用 startArkChildProcess

主进程先做能力检查,再把字符串参数和 fd 放进 ChildProcessArgs:

import {
  childProcessManager,
  ChildProcessArgs,
  ChildProcessOptions
} from '@kit.AbilityKit';

export async function startImageParse(fd: number): Promise<number> {
  if (!childProcessManager.isArkChildProcessSupported()) {
    throw new Error('当前设备不支持 ArkTS 子进程');
  }

  const args: ChildProcessArgs = {
    entryParams: JSON.stringify({
      taskId: 'image-parse-001',
      maxEdge: 2048
    }),
    fds: {
      sourceImage: fd
    }
  };

  const options: ChildProcessOptions = {
    isolationMode: false
  };

  return await childProcessManager.startArkChildProcess(
    'entry/ets/process/ImageParseProcess.ets',
    args,
    options
  );
}

子进程接收参数,执行异步任务,并在 finally 中保证退出:

import { ChildProcess, ChildProcessArgs } from '@kit.AbilityKit';
import process from '@ohos.process';

interface ParseTask {
  taskId: string;
  maxEdge: number;
}

export default class ImageParseProcess extends ChildProcess {
  onStart(args?: ChildProcessArgs): void {
    const task = JSON.parse(args?.entryParams ?? '{}') as ParseTask;
    const fd = args?.fds?.sourceImage;

    if (fd === undefined) {
      console.error('sourceImage fd is missing');
      process.abort();
      return;
    }

    this.parse(fd, task)
      .catch((error: Error) => {
        console.error('image parse failed: ' + error.message);
      })
      .finally(() => {
        // startArkChildProcess 创建的子进程不会自动退出。
        process.abort();
      });
  }

  private async parse(fd: number, task: ParseTask): Promise<void> {
    console.info('parse start, taskId=' + task.taskId);
    // 在这里接入实际的异步读取、解码与落盘逻辑。
    // fd 只在约定的任务边界内使用,完成后由工程统一管理关闭策略。
  }
}

这段代码解决的是“参数、fd、异步任务和退出时机”四个边界,仍然没有凭空制造结果回调。若主进程需要知道解析进度或拿到结构化结果,必须另行设计明确的数据通路。

需要结果回传时,先选数据通路,再写启动代码

根据任务性质,可以把结果需求分成三类:

结果需求建议
只需确认是否成功创建子进程使用启动接口返回的 PID
一次性生成文件,主进程稍后读取约定独立输出文件、任务 ID、原子写入和校验字段
需要实时进度、取消、双向请求响应使用明确的 IPC 方案;Native 父子进程可按官方文档使用 OH_Ability_CreateNativeChildProcessWithConfigs

“写文件再读取”不是所有场景都适用。它适合低频、一次性、允许最终一致的结果;不适合实时进度、高频消息或强交互。需要持续通信时,应从一开始就按 IPC 生命周期设计,而不是等拿到 PID 后再补一个轮询。

还要记住:子进程会随父进程退出而退出,不能把它当成脱离应用长期存在的独立服务。

三种方式怎么选:把条件写成可测试规则

下面这个选择器不依赖设备 API,可以放进工程的策略层做单元测试。它只负责阻止明显错误的选型,不负责启动进程。

type Runtime = 'ARKTS' | 'NATIVE';
type ResultMode = 'NONE' | 'FILE' | 'REALTIME_IPC';

interface ChildTaskPlan {
  runtime: Runtime;
  needsArgs: boolean;
  needsAsyncApi: boolean;
  resultMode: ResultMode;
}

type ChildProcessChoice =
  | 'START_CHILD_PROCESS'
  | 'START_ARK_CHILD_PROCESS'
  | 'START_NATIVE_CHILD_PROCESS'
  | 'BLOCK_NO_BUILTIN_IPC';

export function chooseChildProcess(plan: ChildTaskPlan): ChildProcessChoice {
  if (plan.resultMode === 'REALTIME_IPC') {
    return 'BLOCK_NO_BUILTIN_IPC';
  }

  if (plan.runtime === 'NATIVE') {
    return 'START_NATIVE_CHILD_PROCESS';
  }

  if (plan.needsArgs || plan.needsAsyncApi) {
    return 'START_ARK_CHILD_PROCESS';
  }

  return 'START_CHILD_PROCESS';
}

测试两个正确场景,再加两个边界场景:

const cases: Array<[string, ChildTaskPlan, ChildProcessChoice]> = [
  [
    'log-archive',
    { runtime: 'ARKTS', needsArgs: false, needsAsyncApi: false, resultMode: 'FILE' },
    'START_CHILD_PROCESS'
  ],
  [
    'image-parse',
    { runtime: 'ARKTS', needsArgs: true, needsAsyncApi: true, resultMode: 'FILE' },
    'START_ARK_CHILD_PROCESS'
  ],
  [
    'native-codec',
    { runtime: 'NATIVE', needsArgs: true, needsAsyncApi: false, resultMode: 'FILE' },
    'START_NATIVE_CHILD_PROCESS'
  ],
  [
    'request-result',
    { runtime: 'ARKTS', needsArgs: true, needsAsyncApi: true, resultMode: 'REALTIME_IPC' },
    'BLOCK_NO_BUILTIN_IPC'
  ]
];

for (const [name, plan, expected] of cases) {
  const actual = chooseChildProcess(plan);
  if (actual !== expected) {
    throw new Error(name + ': expected ' + expected + ', got ' + actual);
  }
  console.info(name + ' -> ' + actual);
}

本地 Node.js 对这四条纯策略用例的运行结果为:

log-archive -> START_CHILD_PROCESS
image-parse -> START_ARK_CHILD_PROCESS
native-codec -> START_NATIVE_CHILD_PROCESS
request-result -> BLOCK_NO_BUILTIN_IPC

这只能证明选型规则按预期工作,不能替代 HarmonyOS SDK 编译、设备能力检查和真机子进程验证。

上线前检查这 8 项

  1. 先调用 isArkChildProcessSupported 或 isNativeChildProcessSupported 检查设备能力。
  2. 基础 ArkTS 子进程只放无需参数、同步完成的轻量任务。
  3. 需要 entryParams、fd 或异步 API 时,改用 startArkChildProcess。
  4. Native 计算先确认动态库名称、入口函数和 libchild_process.so 依赖。
  5. 在主进程代码中显式引用 ArkTS 子进程类,避免入口文件被构建优化。
  6. 分清 SELF_FORK 与 APP_SPAWN_FORK 的资源继承和 Binder 能力边界。
  7. 给 startArkChildProcess 的子进程准备可靠的 process.abort 退出路径。
  8. 验收时分别检查“创建成功、入口执行、业务产物、退出行为”,不要用一个 PID 代替全部结果。

版本与验证范围

本文针对 HarmonyOS 7 / API 26 工程中的子进程选型问题。需要说明的是,startChildProcess、startArkChildProcess 和 startNativeChildProcess 并非都从 API 26 才出现;官方文档分别标注了既有版本边界。本文采用的是 2026 年 9 月 1 日更新后的官方开发指导。

当前已完成官方接口边界核对和选型策略的本地运行测试;尚未在 API 26 SDK 与真机上完成图片解析示例的编译、安装和跨进程链路验证,因此没有把示例描述成“拿来即可运行的完整工程”。

官方资料

子进程问题最值得先问的不是“为什么没有回调”,而是:这次任务需要的是创建结果、最终文件,还是持续 IPC? 先把结果类型说清楚,三种启动方式就不会再选错。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐