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