# 在Worker线程中使用相机(ArkTS)

Worker主要作用是为应用程序提供一个多线程的运行环境，可满足应用程序在执行过程中与主线程分离，在后台线程中运行一个脚本进行耗时操作，极大避免类似计算密集型或高延迟的任务阻塞主线程的运行。

通常开发者使用相机功能需要创建相机会话，并持续接收处理预览流、拍照流、录像流等从而实现相关相机功能，这些密集型操作如果都放在主线程即UI线程，可能会阻塞UI绘制，推荐开发者在worker线程中实现相机功能。

#### 开发步骤

1.  导入依赖，本篇文档需要用到worker和相机框架等相关依赖包。
    
    ```
    import { BusinessError } from '@kit.BasicServicesKit';
    import { camera } from '@kit.CameraKit';
    import { ErrorEvent, MessageEvents, ThreadWorkerGlobalScope, worker } from '@kit.ArkTS';
    ```
    
2.  创建相机服务代理类，调用CameraKit方法都放在这个类里执行。
    
    ```
    class CameraService {
      private imageWidth: number = 1920;
      private imageHeight: number = 1080;
      private cameraManager: camera.CameraManager | undefined = undefined;
      private cameras: Array<camera.CameraDevice> = [];
      private cameraInput: camera.CameraInput | undefined = undefined;
      private previewOutput: camera.PreviewOutput | undefined = undefined;
      private photoOutput: camera.PhotoOutput | undefined = undefined;
      private session: camera.PhotoSession | camera.VideoSession | undefined = undefined;
    
      // 初始化相机。
      async initCamera(context: Context, surfaceId: string): Promise<void> {
        console.info(`initCamera surfaceId: ${surfaceId}`);
        try {
          await this.releaseCamera();
          // 获取相机管理器实例。
          this.cameraManager = camera.getCameraManager(context);
          if (this.cameraManager === undefined) {
            console.error('cameraManager is undefined');
            return;
          }
          this.cameras = this.cameraManager.getSupportedCameras();
          if (!this.cameras || this.cameras.length <= 0) {
            console.error("cameraManager.getSupportedCameras error");
            return;
          }
          // 创建cameraInput输出对象。
          this.cameraInput = this.cameraManager.createCameraInput(this.cameras[0]);
          if (this.cameraInput === undefined) {
            console.error('Failed to create the camera input.');
            return;
          }
          // 打开相机。
          await this.cameraInput.open();
    
          let previewProfile: camera.Profile = {
            format: camera.CameraFormat.CAMERA_FORMAT_YUV_420_SP,
            size: {
              width: this.imageWidth,
              height: this.imageHeight
            }
          };
          // 创建预览流输出。
          this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, surfaceId);
          if (this.previewOutput === undefined) {
            console.error('Failed to create the preview stream.');
            this.releaseCamera();
            return;
          }
    
          let photoProfile: camera.Profile = {
            format: camera.CameraFormat.CAMERA_FORMAT_JPEG,
            size: {
              width: this.imageWidth,
              height: this.imageHeight
            }
          };
          // 创建拍照流输出。
          this.photoOutput = this.cameraManager.createPhotoOutput(photoProfile);
          if (this.photoOutput === undefined) {
            console.error('Failed to create the photoOutput.');
            this.releaseCamera();
            return;
          }
    
          // 创建相机会话，启动会话。
          let session = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO);
          if (!session) {
            console.error('session is null');
            this.releaseCamera();
            return;
          }
          this.session = session as camera.PhotoSession;
          this.session.beginConfig();
          this.session.addInput(this.cameraInput);
          this.session.addOutput(this.previewOutput);
          this.session.addOutput(this.photoOutput);
          await this.session.commitConfig();
          await this.session.start();
        } catch (error) {
          let err = error as BusinessError;
          console.error(`initCamera fail: ${err}`);
          this.releaseCamera();
        }
      }
    
      // 释放相机资源。
      async releaseCamera(): Promise<void> {
        console.info('releaseCamera is called');
        // 停止当前会话。
        await this.session?.stop().catch((e: BusinessError) => {console.error('Failed to stop session: ', e)});
        // 释放相机输入流。
        await this.cameraInput?.close().catch((e: BusinessError) => {console.error('Failed to close the camera: ', e)});
        // 释放预览输出流。
        await this.previewOutput?.release().catch((e: BusinessError) => {console.error('Failed to stop the preview stream: ', e)});
        // 释放拍照输出流。
        await this.photoOutput?.release().catch((e: BusinessError) => {console.error('Stop Photo Stream Failure: ', e)});
        // 释放会话。
        await this.session?.release().catch((e: BusinessError) => {console.error('Failed to release session: ', e)});
        console.info('releaseCamera success');
      }
    }
    ```
    
3.  创建worker线程文件，配置worker。
    
    DevEco Studio支持一键生成Worker，在对应的{moduleName}目录下任意位置，点击鼠标右键 > New > Worker，即可自动生成Worker的模板文件及配置信息，无需再手动在build-profile.json5中进行相关配置。
    
    CameraWorker.ets实现参考：
    
    ```
    let cameraService = new CameraService();
    const workerPort: ThreadWorkerGlobalScope = worker.workerPort;
    
    // 自定义消息格式。
    interface MessageInfo {
      hasResolve: boolean;
      type: string;
      context: Context; // 注意worker线程中无法使用getContext()直接获取宿主线程context，需要通过消息从宿主线程通信到worker线程使用。
      surfaceId: string;
    }
    
    workerPort.onmessage = async (e: MessageEvents) => {
      const messageInfo: MessageInfo = e.data;
      console.info(`worker onmessage type:${messageInfo.type}`)
      if ('initCamera' === messageInfo.type) {
        // 在worker线程中收到宿主线程初始化相机的消息。
        console.info(`worker initCamera surfaceId:${messageInfo.surfaceId}`)
        // 在worker线程中初始化相机。
        await cameraService.initCamera(messageInfo.context, messageInfo.surfaceId);
      } else if ('releaseCamera' === messageInfo.type) {
        // 在worker线程中收到宿主线程释放相机的消息。
        console.info('worker releaseCamera.');
        // 在worker线程中释放相机。
        await cameraService.releaseCamera();
      }
    }
    
    workerPort.onmessageerror = (e: MessageEvents) => {
    }
    
    workerPort.onerror = (e: ErrorEvent) => {
    }
    ```
    
4.  创建组件，用于显示预览流，在页面相关生命周期中构造ThreadWorker实例，在worker线程中完成相机初始化和释放。
    
    ```
    @Entry
    @Component
    struct Index {
      private mXComponentController: XComponentController = new XComponentController();
      private surfaceId: string = '';
      @State imageWidth: number = 1920;
      @State imageHeight: number = 1080;
      // 创建ThreadWorker对象获取worker实例。
      private workerInstance: worker.ThreadWorker = new worker.ThreadWorker('entry/ets/workers/CameraWorker.ets');
      private uiContext: UIContext = this.getUIContext();
      private context: Context | undefined = this.uiContext.getHostContext();
     private mXComponentOptions: XComponentOptions = {
       type: XComponentType.SURFACE,
       controller: this.mXComponentController
     }
    
      onPageShow(): void {
        if ('' !== this.surfaceId) {
          // 通过worker实例向worker线程发送消息初始化相机。
          this.workerInstance.postMessage({
            type: 'initCamera',
            context: this.context,
            surfaceId: this.surfaceId,
          })
        }
      }
    
      onPageHide(): void {
        // 通过worker实例向worker线程发送消息销毁相机。
        this.workerInstance.postMessage({
          type: 'releaseCamera',
        })
      }
    
      build() {
        Column() {
          Column() {
            XComponent(this.mXComponentOptions)
              .onLoad(async () => {
                console.info('onLoad is called');
                // 初始化XComponent获取预览流surfaceId。
                this.surfaceId = this.mXComponentController.getXComponentSurfaceId();
                let surfaceRect: SurfaceRect = {
                  surfaceWidth: this.imageHeight,
                  surfaceHeight: this.imageWidth
                };
                this.mXComponentController.setXComponentSurfaceRect(surfaceRect);
                console.info(`onLoad surfaceId: ${this.surfaceId}`);
                if (!this.workerInstance) {
                  console.error('create stage worker failed');
                  return;
                }
                // 宿主线程向worker线程发送初始化相机消息。
                this.workerInstance.postMessage({
                  type: 'initCamera',
                  context: this.context, // 将宿主线程的context传给worker线程使用。
                  surfaceId: this.surfaceId, // 将surfaceId传给worker线程使用。
                })
              })// The width and height of the surface are opposite to those of the XComponent.
              .width(this.uiContext.px2vp(this.imageHeight))
              .height(this.uiContext.px2vp(this.imageWidth))
    
          }.justifyContent(FlexAlign.Center)
          .height('90%')
    
          Text('WorkerDemo')
            .fontSize(36)
        }
        .justifyContent(FlexAlign.End)
        .height('100%')
        .width('100%')
      }
    }
    ```
    

#### trace对比

不使用Worker：

使用Worker：