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 239 240使用Worker: 241 242