多线程处理图片?色彩管理器也要能"共享"

上一篇我们聊了 colorSpaceManager,它能帮你创建和管理色彩空间。但有一个问题:如果你的照片编辑 APP 需要在后台线程处理大量图片(比如批量滤镜、批量导出),普通的 colorSpaceManager 创建出来的对象是不能跨线程传递的。

为什么呢?因为在 ArkTS 的并发模型里,不同线程之间的对象默认是隔离的,你不能直接把一个线程里创建的对象传给另一个线程用。如果你尝试这么做,运行时会报错。

HarmonyOS 提供了 sendableColorSpaceManager 这个模块来解决这个问题。它创建出来的色彩管理器实现了 ISendable 接口,可以在主线程、TaskPool、Worker 等并发实例之间传递,而且传递的方式是引用传递——也就是说,多个线程共享同一个对象,修改会互相影响。

导入模块

import { colorSpaceManager, sendableColorSpaceManager } from '@kit.ArkGraphics2D';

注意这里同时导入了两个模块。因为 sendableColorSpaceManager 的参数类型(比如 ColorSpace 枚举、ColorSpacePrimaries 接口)都定义在 colorSpaceManager 里,所以两个都要导入。

普通版与可共享版对比

下面是两种色彩管理器的使用场景对比:

单线程

多线程

需要色彩管理器

使用场景?

colorSpaceManager

sendableColorSpaceManager

照片编辑预览

滤镜实时预览

色彩空间显示

TaskPool 批量处理

Worker 后台导出

多线程色彩转换

普通 Array 返回

ISendable 接口

collections.Array 返回

创建标准可共享色彩空间

跟普通版本的用法几乎一样,只是把 colorSpaceManager.create 换成了 sendableColorSpaceManager.create

import { colorSpaceManager, sendableColorSpaceManager } from '@kit.ArkGraphics2D';
let colorSpace: sendableColorSpaceManager.ColorSpaceManager;
colorSpace = sendableColorSpaceManager.create(colorSpaceManager.ColorSpace.SRGB);

这里有几个细节值得注意:

  1. 返回类型不同:普通版返回的是 colorSpaceManager.ColorSpaceManager,而这里返回的是 sendableColorSpaceManager.ColorSpaceManager。虽然名字一样,但它们是不同的类型。可共享版本实现了 ISendable 接口,所以能跨线程传递。

  2. 参数来源相同:色彩空间的枚举值 ColorSpace 还是来自 colorSpaceManager,可共享版本并没有重新定义一套枚举。

  3. UNKNOWN 和 CUSTOM 同样不能直接创建:跟普通版一样,这两个枚举值不能传给 create 方法。

创建自定义可共享色彩空间

如果你需要处理自定义色彩空间的照片,也可以创建自定义的可共享色彩管理器:

import { colorSpaceManager, sendableColorSpaceManager } from '@kit.ArkGraphics2D';
let colorSpace: sendableColorSpaceManager.ColorSpaceManager;
let primaries: colorSpaceManager.ColorSpacePrimaries = {
  redX: 0.1,
  redY: 0.1,
  greenX: 0.2,
  greenY: 0.2,
  blueX: 0.3,
  blueY: 0.3,
  whitePointX: 0.4,
  whitePointY: 0.4
};
let gamma: number = 2.2;
colorSpace = sendableColorSpaceManager.create(primaries, gamma);

跟普通版完全一样的参数,只是调用的是 sendableColorSpaceManager.create。创建出来的对象同样继承了 ISendable,可以在多线程间共享。

查询属性:跟普通版一样的方法

可共享版本的 ColorSpaceManager 实例提供了跟普通版一样的属性查询方法,但 API 签名稍有不同。

获取色彩空间类型:

let spaceName: colorSpaceManager.ColorSpace = colorSpace.getColorSpaceName();

注意返回类型直接用了 colorSpaceManager.ColorSpace,没有 try-catch 的示例。在实际使用中,建议你还是加上错误处理。

获取白点值:

import { collections } from '@kit.ArkTS';
let point: collections.Array<number> = colorSpace.getWhitePoint();

这里有个小区别:可共享版本返回的是 collections.Array<number>,而不是普通的 Array<number>collections.Array 是 ArkTS 提供的可共享数组类型,同样能在多线程间传递。使用时你需要额外导入 collections 模块。

获取 gamma 值:

let gamma: number = colorSpace.getGamma();

这个跟普通版完全一样,返回一个浮点数。

多线程批量处理流程

下面是使用 sendableColorSpaceManager 进行批量图片处理的典型流程:

主线程

创建可共享色彩空间

传递给 TaskPool

工作线程1

工作线程2

工作线程3

处理图片批次1

处理图片批次2

处理图片批次3

色彩空间转换

导出处理结果

实际场景:后台批量处理图片的色彩管理

想象一下这个场景:你的照片编辑 APP 有一个"批量导出"功能,用户选了 100 张照片,要全部转换成 SRGB 色彩空间后导出。这个过程如果在主线程做,UI 会卡死。所以你要用 TaskPool 来做。

使用 sendableColorSpaceManager 的流程是这样的:

在主线程创建可共享的色彩空间对象:

import { colorSpaceManager, sendableColorSpaceManager } from '@kit.ArkGraphics2D';

// 创建一个 SRGB 色彩空间,用于批量导出
let targetColorSpace = sendableColorSpaceManager.create(colorSpaceManager.ColorSpace.SRGB);

把对象传给 TaskPool 的工作线程:

因为 targetColorSpace 实现了 ISendable 接口,你可以直接把它作为参数传给 TaskPool 的任务。在工作线程里,你可以直接使用这个对象来做色彩空间转换,不需要重新创建。

在工作线程里使用:

工作线程拿到这个对象后,可以调用 getColorSpaceName() 来确认目标色彩空间类型,然后对每张图片做转换。

什么时候该用可共享版本?

简单说,如果你的色彩管理器只在主线程用,就用普通的 colorSpaceManager;如果需要在多个线程间共享,就用 sendableColorSpaceManager

具体来说:

  • 单线程场景:普通的照片编辑、滤镜预览、色彩空间显示等,用 colorSpaceManager 就够了。
  • 多线程场景:批量图片处理、后台导出、TaskPool/Worker 中的色彩转换等,必须用 sendableColorSpaceManager

性能方面,可共享版本因为要支持跨线程引用传递,可能会有轻微的额外开销。但对于色彩管理这种操作频率不高的场景,这个开销完全可以忽略。

小结

sendableColorSpaceManager 本质上就是 colorSpaceManager 的多线程安全版本。它的 API 设计几乎一模一样,区别就是创建出来的对象可以跨线程共享。如果你的 APP 有多线程处理图片的需求,记得用它来替代普通的色彩管理器。

最后提醒一下:因为是引用传递,多个线程共享同一个对象,所以如果你在一个线程里修改了对象的状态(虽然目前的 API 都是只读的),其他线程会立即看到变化。在设计并发逻辑时要注意这一点。

Logo

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

更多推荐