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 245 246使用Worker: 247 248