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