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 240 241使用woker: 242 243