• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 在Worker线程中使用相机(ArkTS)
2<!--Kit: Camera Kit-->
3<!--Subsystem: Multimedia-->
4<!--Owner: @qano-->
5<!--Designer: @leo_ysl-->
6<!--Tester: @xchaosioda-->
7<!--Adviser: @zengyawen-->
8
9[Worker](../../arkts-utils/worker-introduction.md)主要作用是为应用程序提供一个多线程的运行环境,可满足应用程序在执行过程中与主线程分离,在后台线程中运行一个脚本进行耗时操作,极大避免类似于计算密集型或高延迟的任务阻塞主线程的运行。
10
11通常开发者使用相机功能需要创建相机会话,并持续接收处理预览流、拍照流、录像流等从而实现相关相机功能,这些密集型操作如果都放在主线程即UI线程,可能会阻塞UI绘制,推荐开发者在worker线程中实现相机功能。
12
13## 开发步骤
141. 导入依赖,本篇文档需要用到worker和相机框架等相关依赖包。
15   ```ts
16   import { BusinessError } from '@kit.BasicServicesKit';
17   import { camera } from '@kit.CameraKit';
18   import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
19   ```
20
212. 创建相机服务代理类,调用CameraKit方法都放在这个类里执行。
22
23   ```ts
24   class CameraService {
25     private imageWidth: number = 1920;
26     private imageHeight: number = 1080;
27     private cameraManager: camera.CameraManager | undefined = undefined;
28     private cameras: Array<camera.CameraDevice> | Array<camera.CameraDevice> = [];
29     private cameraInput: camera.CameraInput | undefined = undefined;
30     private previewOutput: camera.PreviewOutput | undefined = undefined;
31     private photoOutput: camera.PhotoOutput | undefined = undefined;
32     private session: camera.PhotoSession | camera.VideoSession | undefined = undefined;
33
34     // 初始化相机。
35     async initCamera(context: Context, surfaceId: string): Promise<void> {
36       console.info(`initCamera surfaceId: ${surfaceId}`);
37       try {
38         await this.releaseCamera();
39         // 获取相机管理器实例。
40         this.cameraManager = camera.getCameraManager(context);
41         if (this.cameraManager === undefined) {
42           console.error('cameraManager is undefined');
43           return;
44         }
45         this.cameras = this.cameraManager.getSupportedCameras();
46
47         // 创建cameraInput输出对象。
48         this.cameraInput = this.cameraManager.createCameraInput(this.cameras[0]);
49         if (this.cameraInput === undefined) {
50           console.error('Failed to create the camera input.');
51           return;
52         }
53         // 打开相机。
54         await this.cameraInput.open();
55
56         let previewProfile: camera.Profile = {
57           format: camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP,
58           size: {
59             width: this.imageWidth,
60             height: this.imageHeight
61           }
62         };
63         // 创建预览流输出。
64         this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, surfaceId);
65         if (this.previewOutput === undefined) {
66           console.error('Failed to create the preview stream.');
67           return;
68         }
69
70         let photoProfile: camera.Profile = {
71           format: camera.CameraFormat.CAMERA_FORMAT_JPEG,
72           size: {
73             width: this.imageWidth,
74             height: this.imageHeight
75           }
76         };
77         // 创建拍照流输出。
78         this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile);
79         if (this.photoOutput === undefined) {
80           console.error('Failed to create the photoOutput.');
81           return;
82         }
83
84         // 创建相机会话,启动会话。
85         this.session = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession;
86         this.session.beginConfig();
87         this.session.addInput(this.cameraInput);
88         this.session.addOutput(this.previewOutput);
89         this.session.addOutput(this.photoOutput);
90         await this.session.commitConfig();
91         await this.session.start();
92       } catch (error) {
93         let err = error as BusinessError;
94         console.error(`initCamera fail: ${err}`);
95       }
96     }
97
98     // 释放相机资源。
99     async releaseCamera(): Promise<void> {
100       console.info('releaseCamera is called');
101       try {
102         await this.previewOutput?.release();
103         await this.photoOutput?.release();
104         await this.session?.release();
105         await this.cameraInput?.close();
106       } catch (error) {
107         let err = error as BusinessError;
108         console.error(`releaseCamera fail: error: ${err}`);
109       } finally {
110         this.previewOutput = undefined;
111         this.photoOutput = undefined;
112         this.cameraManager = undefined;
113         this.session = undefined;
114         this.cameraInput = undefined;
115       }
116       console.info('releaseCamera success');
117     }
118   }
119   ```
120
1213. 创建worker线程文件,配置worker。
122
123   DevEco Studio支持一键生成Worker,在对应的{moduleName}目录下任意位置,点击鼠标右键 > New > Worker,即可自动生成Worker的模板文件及配置信息,无需再手动在build-profile.json5中进行相关配置。
124
125   CameraWorker.ets实现参考:
126
127   ```ts
128   let cameraService = new CameraService();
129   const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
130
131   // 自定义消息格式。
132   interface MessageInfo {
133     hasResolve: boolean;
134     type: string;
135     context: Context; // 注意worker线程中无法使用getContext()直接获取宿主线程context,需要通过消息从宿主线程通信到worker线程使用。
136     surfaceId: string;
137   }
138
139   workerPort.onmessage = async (e: MessageEvents) => {
140     const messageInfo: MessageInfo = e.data;
141     console.info(`worker onmessage type:${messageInfo.type}`)
142     if ('initCamera' === messageInfo.type) {
143       // 在worker线程中收到宿主线程初始化相机的消息。
144       console.info(`worker initCamera surfaceId:${messageInfo.surfaceId}`)
145       // 在worker线程中初始化相机。
146       await cameraService.initCamera(messageInfo.context, messageInfo.surfaceId);
147     } else if ('releaseCamera' === messageInfo.type) {
148       // 在worker线程中收到宿主线程释放相机的消息。
149       console.info('worker releaseCamera.');
150       // 在worker线程中释放相机。
151       await cameraService.releaseCamera();
152     }
153   }
154
155   workerPort.onmessageerror = (e: MessageEvents) => {
156   }
157
158   workerPort.onerror = (e: ErrorEvent) => {
159   }
160   ```
161
1624. 创建组件,用于显示预览流,在页面相关生命周期中构造ThreadWorker实例,在worker线程中完成相机初始化和释放。
163
164   ```ts
165   @Entry
166   @Component
167   struct Index {
168     private mXComponentController: XComponentController = new XComponentController();
169     private surfaceId: string = '';
170     @State imageWidth: number = 1920;
171     @State imageHeight: number = 1080;
172     // 创建ThreadWorker对象获取worker实例。
173     private workerInstance: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/CameraWorker.ets');
174     private uiContext: UIContext = this.getUIContext();
175     private context: Context | undefined = this.uiContext.getHostContext();
176
177     onPageShow(): void {
178       if ('' !== this.surfaceId) {
179         // 通过worker实例向worker线程发送消息初始化相机。
180         this.workerInstance.postMessage({
181           type: 'initCamera',
182           context: this.context,
183           surfaceId: this.surfaceId,
184         })
185       }
186     }
187
188     onPageHide(): void {
189       // 通过worker实例向worker线程发送消息销毁相机。
190       this.workerInstance.postMessage({
191         type: 'releaseCamera',
192       })
193     }
194
195     build() {
196       Column() {
197         Column() {
198           XComponent({
199             id: 'componentId',
200             type: XComponentType.SURFACE,
201             controller: this.mXComponentController
202           })
203             .onLoad(async () => {
204               console.info('onLoad is called');
205               // 初始化XComponent获取预览流surfaceId。
206               this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
207               let surfaceRect: SurfaceRect = {
208                 surfaceWidth: this.imageHeight,
209                 surfaceHeight: this.imageWidth
210               };
211               this.mXComponentController.setXComponentSurfaceRect(surfaceRect);
212               console.info(`onLoad surfaceId: ${this.surfaceId}`);
213               if (!this.workerInstance) {
214                 console.error('create stage worker failed');
215                 return;
216               }
217               // 宿主线程向worker线程发送初始化相机消息。
218               this.workerInstance.postMessage({
219                 type: 'initCamera',
220                 context: this.context, // 将宿主线程的context传给worker线程使用。
221                 surfaceId: this.surfaceId, // 将surfaceId传给worker线程使用。
222               })
223             })// The width and height of the surface are opposite to those of the XComponent.
224             .width(this.uiContext.px2vp(this.imageHeight))
225             .height(this.uiContext.px2vp(this.imageWidth))
226
227         }.justifyContent(FlexAlign.Center)
228         .height('90%')
229
230         Text('WorkerDemo')
231           .fontSize(36)
232       }
233       .justifyContent(FlexAlign.End)
234       .height('100%')
235       .width('100%')
236     }
237   }
238   ```
239
240## trace对比
241
242不使用Worker:
243
244![camera-in-ui-thread](figures/camera-in-ui-thread.png)
245
246使用Worker:
247
248![camera-in-worker-thread](figures/camera-in-worker-thread.png)