HarmonyOS Camera Kit 全解析:从基础拍摄到跨设备协同的实战指南

在移动应用开发中,相机功能往往是提升用户体验的关键模块,但传统相机开发面临权限管理复杂、设备兼容性差、功能实现繁琐等痛点。HarmonyOS 作为面向全场景的分布式操作系统,其 Camera Kit(相机服务)通过统一的 API 接口和分布式能力,为开发者提供了从基础拍照到跨设备协同的完整解决方案。本文将系统讲解 HarmonyOS Camera 的架构设计、核心功能实现、高级特性应用及常见问题排查,帮助开发者快速掌握相机应用开发技巧。

一、Camera Kit 架构与开发基础

HarmonyOS Camera Kit 采用分层架构设计,上层提供简洁的 API 接口,下层封装设备驱动和硬件适配逻辑,中间层通过会话管理实现输入输出流的灵活调度。这种架构既简化了开发流程,又保留了对硬件的深度控制能力。对于开发者而言,核心是理解 Camera Kit 的会话管理机制和权限控制逻辑,这是实现所有相机功能的基础。

1.1 权限配置与申请流程

相机作为敏感权限应用,必须严格遵循 HarmonyOS 的权限管理规范。开发相机应用需完成静态权限声明和动态权限申请两步流程,缺一不可。

在module.json5配置文件中,需声明相机基础权限和存储访问权限:

复制代码
{
  "module": {
    "reqPermissions": [
      {
        "name": "ohos.permission.CAMERA",
        "reason": "用于拍摄照片和视频",
        "usedScene": {
          "ability": [".CameraAbility"],
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.WRITE_IMAGEVIDEO_STORAGE",
        "reason": "用于保存拍摄的媒体文件",
        "usedScene": {
          "ability": [".CameraAbility"],
          "when": "inuse"
        }
      }
    ]
  }
}

对于 Android 兼容模式开发,需在AndroidManifest.xml中添加对应权限:

复制代码
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

动态权限申请需在应用启动时触发,建议在相机页面加载前完成权限校验:

TypeScript 复制代码
import { abilityAccessCtrl, Permissions } from '@kit.AbilityAccessCtrlKit';

async requestCameraPermissions() {
  const permissions: Array<Permissions> = ['ohos.permission.CAMERA', 'ohos.permission.WRITE_IMAGEVIDEO_STORAGE'];
  const atManager = abilityAccessCtrl.createAtManager();
  
  try {
    // 检查权限状态
    const result = await atManager.checkPermissions(permissions);
    if (result.every(item => item === 0)) {
      // 已获得所有权限
      return true;
    }
    
    // 申请权限
    const requestResult = await atManager.requestPermissionsFromUser(permissions);
    return requestResult.grantedPermissions.length === permissions.length;
  } catch (err) {
    console.error(`权限申请失败: ${err.message}`);
    return false;
  }
}

权限申请结果需在onRequestPermissionsResult回调中处理,对于拒绝权限的情况,应引导用户到设置页面开启:

TypeScript 复制代码
onRequestPermissionsResult(requestCode: number, permissions: Array<string>, grantResults: Array<number>): void {
  if (requestCode === CAMERA_PERMISSION_REQUEST_CODE) {
    if (grantResults.every(result => result === 0)) {
      // 权限申请成功,初始化相机
      this.initCamera();
    } else {
      // 权限被拒绝,显示引导弹窗
      this.showPermissionGuideDialog();
    }
  }
}

1.2 核心组件与会话管理

Camera Kit 的核心能力围绕会话(Session)展开,会话是连接相机输入流(CameraInput)和输出流(如 PreviewOutput、PhotoOutput、VideoOutput)的桥梁。不同类型的会话对应不同的拍摄场景,主要包括:

  • PhotoSession:用于拍照场景,支持预览、拍照、HDR 等功能
  • VideoSession:用于录像场景,支持视频录制、音频采集等功能
  • MultiCameraSession:用于多摄像头协同场景(如前后摄像头同时工作)

会话管理的基本流程包括创建会话、配置输入输出流、提交配置、启动会话、执行操作、停止会话和释放资源七个步骤。以下是 PhotoSession 的完整生命周期示例:

TypeScript 复制代码
import { camera, photoAccessHelper } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';

class CameraManager {
  private cameraManager: camera.CameraManager;
  private photoSession: camera.PhotoSession | null = null;
  private cameraInput: camera.CameraInput | null = null;
  private previewOutput: camera.PreviewOutput | null = null;
  private photoOutput: camera.PhotoOutput | null = null;

  // 初始化相机管理器
  async init() {
    this.cameraManager = await camera.getCameraManager();
  }

  // 创建并配置会话
  async createPhotoSession() {
    try {
      // 获取可用相机设备(默认后置摄像头)
      const cameras = await this.cameraManager.getCameraDevices();
      const cameraDevice = cameras.find(cam => cam.position === camera.CameraPosition.CAMERA_POSITION_BACK);
      if (!cameraDevice) {
        throw new Error('未找到后置摄像头');
      }

      // 创建相机输入流
      this.cameraInput = await this.cameraManager.createCameraInput(cameraDevice);

      // 创建预览输出流(需绑定Surface组件)
      const previewSurface = this.getPreviewSurface(); // 获取预览界面的Surface
      this.previewOutput = await this.cameraManager.createPreviewOutput(previewSurface);

      // 获取拍照输出能力配置
      const outputCapability = await this.cameraManager.getSupportedOutputCapability(
        cameraDevice, camera.SceneMode.NORMAL_PHOTO
      );
      // 选择合适的拍照配置文件
      const photoProfile = outputCapability.photoProfiles[0];
      // 创建拍照输出流
      this.photoOutput = await this.cameraManager.createPhotoOutput(photoProfile);

      // 创建拍照会话
      this.photoSession = await this.cameraManager.createPhotoSession();

      // 配置会话:添加输入输出流
      this.photoSession.addInput(this.cameraInput);
      this.photoSession.addOutput(this.previewOutput);
      this.photoSession.addOutput(this.photoOutput);

      // 注册拍照回调
      this.photoOutput.on('photoAvailable', async (buffer: ArrayBuffer, metadata: camera.PhotoMetadata) => {
        // 处理照片数据
        await this.savePhoto(buffer, metadata);
      });

      // 提交配置并启动会话
      await this.photoSession.commitConfig();
      await this.photoSession.start();
    } catch (err) {
      console.error(`创建会话失败: ${(err as BusinessError).message}`);
      this.releaseResources();
    }
  }

  // 释放资源
  async releaseResources() {
    if (this.photoSession) {
      await this.photoSession.stop();
      this.photoSession.removeAllInputs();
      this.photoSession.removeAllOutputs();
      this.photoSession.release();
      this.photoSession = null;
    }
    if (this.photoOutput) {
      this.photoOutput.off('photoAvailable');
      this.photoOutput.release();
      this.photoOutput = null;
    }
    if (this.previewOutput) {
      this.previewOutput.release();
      this.previewOutput = null;
    }
    if (this.cameraInput) {
      await this.cameraInput.close();
      this.cameraInput = null;
    }
  }
}

会话管理是相机开发的核心,也是最容易出现问题的环节。常见错误包括:未正确释放资源导致相机被占用(错误码 7400109)、会话配置未提交直接启动、输出流类型与会话不匹配等。最佳实践是:始终在onDestroy生命周期中释放资源,使用 try-catch 捕获所有异步操作,确保资源释放逻辑的健壮性。

二、基础拍摄功能实现

掌握了会话管理的基本流程后,我们可以开始实现具体的拍摄功能。HarmonyOS Camera Kit 提供了丰富的 API 支持拍照、录像、预览等基础功能,同时允许开发者通过参数配置实现个性化需求。

2.1 拍照功能实现

拍照功能的核心是通过PhotoOutput的capture方法触发拍摄,并在回调中处理照片数据。基础拍照流程如下:

  1. 配置拍照参数(质量、旋转角度、镜像等)
  2. 调用capture方法触发拍摄
  3. 在photoAvailable回调中处理照片数据
  4. 保存照片到媒体库
TypeScript 复制代码
// 配置拍照参数并触发拍摄
async capturePhoto(isFrontCamera: boolean = false) {
  if (!this.photoOutput || !this.photoSession) {
    throw new Error('相机会话未初始化');
  }

  try {
    // 获取设备旋转角度,用于纠正照片方向
    const deviceDegree = await this.getDeviceRotationDegree();
    
    // 配置拍照参数
    const settings: camera.PhotoCaptureSetting = {
      quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, // 高质量模式
      rotation: this.calculatePhotoRotation(deviceDegree, isFrontCamera),
      mirror: isFrontCamera, // 前置摄像头开启镜像
      location: await this.getLocationInfo() // 可选:添加位置信息
    };

    // 触发拍照
    await this.photoOutput.capture(settings);
  } catch (err) {
    console.error(`拍照失败: ${(err as BusinessError).message}`);
  }
}

// 计算照片旋转角度
private calculatePhotoRotation(deviceDegree: number, isFront: boolean): camera.ImageRotation {
  let rotation = deviceDegree % 360;
  // 前置摄像头需要额外处理旋转方向
  if (isFront) {
    rotation = (360 - rotation) % 360;
  }
  
  switch (rotation) {
    case 90:
      return camera.ImageRotation.ROTATION_90;
    case 180:
      return camera.ImageRotation.ROTATION_180;
    case 270:
      return camera.ImageRotation.ROTATION_270;
    default:
      return camera.ImageRotation.ROTATION_0;
  }
}

// 保存照片到媒体库
private async savePhoto(buffer: ArrayBuffer, metadata: camera.PhotoMetadata) {
  try {
    // 创建照片资产
    const photoAsset = await photoAccessHelper.createPhotoAsset(buffer, {
      width: metadata.width,
      height: metadata.height,
      mimeType: 'image/jpeg',
      dateTaken: Date.now()
    });
    
    // 保存到媒体库
    await photoAsset.save();
    return photoAsset.uri; // 返回照片URI
  } catch (err) {
    console.error(`保存照片失败: ${(err as BusinessError).message}`);
    return '';
  }
}

拍照质量设置会显著影响图片大小和处理速度,开发者应根据实际场景选择:

  • QUALITY_LEVEL_LOW:低质量,适合快速预览或网络传输
  • QUALITY_LEVEL_NORMAL:普通质量,平衡画质和性能
  • QUALITY_LEVEL_HIGH:高质量,适合正式拍摄场景

2.2 预览功能优化

预览功能是相机应用的基础交互界面,直接影响用户体验。优化预览效果需要关注帧率控制、分辨率适配和画面比例调整三个方面。

TypeScript 复制代码
// 配置预览参数
async configurePreview() {
  if (!this.previewOutput) {
    return;
  }

  // 获取支持的预览配置
  const previewProfiles = await this.cameraManager.getSupportedPreviewProfiles();
  
  // 选择最佳预览配置(优先高帧率)
  let bestProfile = previewProfiles[0];
  previewProfiles.forEach(profile => {
    // 优先选择帧率≥30fps,分辨率接近屏幕的配置
    if (profile.fps.max >= 30 && 
        profile.resolution.width > bestProfile.resolution.width) {
      bestProfile = profile;
    }
  });

  // 应用预览配置
  await this.previewOutput.setProfile(bestProfile);
  
  // 设置预览帧率范围
  await this.previewOutput.setFpsRange({
    min: 15,
    max: 30
  });
}

// 处理预览画面拉伸问题
adjustPreviewLayout(previewElement: Element) {
  // 获取预览分辨率
  const previewWidth = this.previewOutput?.getProfile().resolution.width || 1920;
  const previewHeight = this.previewOutput?.getProfile().resolution.height || 1080;
  
  // 获取控件尺寸
  const containerWidth = previewElement.clientWidth;
  const containerHeight = previewElement.clientHeight;
  
  // 计算宽高比
  const previewRatio = previewWidth / previewHeight;
  const containerRatio = containerWidth / containerHeight;
  
  // 根据比例调整预览控件尺寸
  if (previewRatio > containerRatio) {
    previewElement.style.width = `${containerWidth}px`;
    previewElement.style.height = `${containerWidth / previewRatio}px`;
  } else {
    previewElement.style.width = `${containerHeight * previewRatio}px`;
    previewElement.style.height = `${containerHeight}px`;
  }
}

常见的预览问题及解决方案:

  • 画面卡顿:降低预览分辨率或帧率,关闭不必要的图像处理
  • 画面拉伸:按预览比例调整控件尺寸,或使用黑边填充
  • 预览延迟:启用硬件加速,减少预览数据的额外处理

2.3 录像功能开发

录像功能基于VideoSession实现,需要同时处理视频和音频数据流。完整的录像功能包括开始录制、暂停 / 继续、停止录制和视频保存四个核心操作。

TypeScript 复制代码
import { audio } from '@kit.AudioKit';

class VideoRecorder {
  private videoSession: camera.VideoSession | null = null;
  private videoOutput: camera.VideoOutput | null = null;
  private audioInput: audio.AudioInput | null = null;
  private isRecording = false;
  private isPaused = false;

  // 初始化录像会话
  async initVideoSession() {
    // 获取相机设备(同上)
    const cameraDevice = await this.getCameraDevice();
    
    // 创建相机输入和视频输出
    const cameraInput = await this.cameraManager.createCameraInput(cameraDevice);
    const videoProfile = this.getBestVideoProfile(cameraDevice);
    this.videoOutput = await this.cameraManager.createVideoOutput(videoProfile);
    
    // 创建音频输入
    this.audioInput = await audio.createAudioInput();
    
    // 创建并配置视频会话
    this.videoSession = await this.cameraManager.createVideoSession();
    this.videoSession.addInput(cameraInput);
    this.videoSession.addInput(this.audioInput);
    this.videoSession.addOutput(this.videoOutput);
    
    // 配置视频输出
    await this.videoOutput.setRecorderConfig({
      outputFormat: camera.VideoOutputFormat.MPEG_4,
      audioCodec: camera.AudioCodec.AAC,
      videoCodec: camera.VideoCodec.H264,
      bitRate: 10 * 1024 * 1024, // 10Mbps
      frameRate: 30
    });
    
    await this.videoSession.commitConfig();
    await this.videoSession.start();
  }

  // 开始录制
  async startRecording(outputPath: string) {
    if (!this.videoOutput || this.isRecording) {
      return;
    }
    
    // 设置输出路径
    await this.videoOutput.setOutputPath(outputPath);
    
    // 开始录制
    await this.videoOutput.startRecording();
    this.isRecording = true;
    this.isPaused = false;
  }

  // 暂停录制
  async pauseRecording() {
    if (!this.videoOutput || !this.isRecording || this.isPaused) {
      return;
    }
    
    await this.videoOutput.pauseRecording();
    this.isPaused = true;
  }

  // 继续录制
  async resumeRecording() {
    if (!this.videoOutput || !this.isRecording || !this.isPaused) {
      return;
    }
    
    await this.videoOutput.resumeRecording();
    this.isPaused = false;
  }

  // 停止录制
  async stopRecording() {
    if (!this.videoOutput || !this.isRecording) {
      return '';
    }
    
    const result = await this.videoOutput.stopRecording();
    this.isRecording = false;
    this.isPaused = false;
    return result.videoUri; // 返回视频URI
  }
}

录像功能对设备性能要求较高,开发时应注意:

  • 根据设备性能动态调整码率和分辨率
  • 长时间录制需监控设备温度,避免过热
  • 录制过程中提供明确的状态指示(如计时器、录制图标)

三、高级特性与场景化应用

HarmonyOS Camera Kit 不仅支持基础拍摄功能,还提供了多项高级特性,如 HDR Vivid 高动态范围拍摄、分段式拍照和动图拍摄等,帮助开发者打造专业级相机应用。同时,借助 HarmonyOS 的分布式能力,还能实现跨设备相机协同等创新场景。

3.1 HDR Vivid 高动态范围拍摄

HDR Vivid 是一种先进的动态 HDR 标准,能够捕捉更丰富的光影细节和色彩层次,特别适合高对比度场景(如逆光拍摄)。在 HarmonyOS 中实现 HDR Vivid 拍摄需要特定的色彩空间配置和设备支持。

TypeScript 复制代码
import { colorSpaceManager } from '@kit.ColorSpaceKit';

// 检查设备是否支持HDR Vivid
async checkHdrVividSupport(): Promise<boolean> {
  if (!this.photoSession) {
    return false;
  }
  
  // 获取支持的色彩空间
  const supportedSpaces = await this.photoSession.getSupportedColorSpaces();
  // DISPLAY_P3色彩空间支持HDR Vivid
  return supportedSpaces.includes(colorSpaceManager.ColorSpace.DISPLAY_P3);
}

// 配置HDR Vivid拍摄
async enableHdrVivid(enable: boolean) {
  if (!this.photoSession) {
    return;
  }
  
  // 检查设备支持性
  const isSupported = await this.checkHdrVividSupport();
  if (!isSupported && enable) {
    console.warn('当前设备不支持HDR Vivid');
    return;
  }
  
  // 设置色彩空间(需在commitConfig前配置)
  const targetSpace = enable 
    ? colorSpaceManager.ColorSpace.DISPLAY_P3 
    : colorSpaceManager.ColorSpace.SRGB;
  
  await this.photoSession.setColorSpace(targetSpace);
  
  // 验证设置是否生效
  const activeSpace = await this.photoSession.getActiveColorSpace();
  return activeSpace === targetSpace;
}

// HDR模式下的拍照流程
async captureHdrPhoto() {
  // 启用HDR模式
  const hdrEnabled = await this.enableHdrVivid(true);
  if (!hdrEnabled) {
    // 若HDR不可用,使用普通模式
    return this.capturePhoto();
  }
  
  // 配置HDR特定参数
  const settings: camera.PhotoCaptureSetting = {
    quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
    rotation: await this.getPhotoRotation(),
    // HDR模式下禁用自动美化,保留更多细节
    effectMode: camera.EffectMode.EFFECT_MODE_OFF
  };
  
  // 触发HDR拍照
  return this.photoOutput?.capture(settings);
}

实现 HDR Vivid 拍摄的注意事项:

  1. 必须在会话commitConfig前设置色彩空间
  2. HDR 处理会增加计算量,可能导致拍照响应时间延长
  3. 部分设备可能需要开启硬件加速才能支持 HDR Vivid
  4. 保存 HDR 照片时需确保相册应用支持 HDR 格式查看

根据测试数据,启用 HDR Vivid 后,照片的动态范围可提升 2-3 档,暗部细节保留更完整,高光部分不易过曝。

3.2 分段式拍照与快速预览

传统拍照模式下,用户需要等待完整图像处理完成才能看到结果,体验较差。分段式拍照通过分阶段返回不同质量的图片,显著提升交互体验:第一阶段快速返回预览级图片,第二阶段在后台处理并返回高质量原图。

TypeScript 复制代码
// 启用分段式拍照
async enableSegmentedCapture() {
  if (!this.photoOutput) {
    return;
  }
  
  // 检查设备支持性
  if (!this.photoOutput.isSegmentedCaptureSupported()) {
    console.warn('当前设备不支持分段式拍照');
    return false;
  }
  
  // 注册分段式拍照回调
  this.photoOutput.on('photoAssetAvailable', async (photoAsset: photoAccessHelper.PhotoAsset) => {
    // 第一阶段:快速获取低质量图片
    const previewUri = await this.getPreviewImage(photoAsset);
    // 显示预览图
    this.updatePreviewUI(previewUri);
  });
  
  this.photoOutput.on('highQualityPhotoAvailable', async (photoAsset: photoAccessHelper.PhotoAsset) => {
    // 第二阶段:获取高质量原图
    const highQualityUri = await this.saveHighQualityPhoto(photoAsset);
    // 更新UI显示高质量图片
    this.updateFinalPhotoUI(highQualityUri);
  });
  
  return true;
}

// 获取预览级图片
private async getPreviewImage(asset: photoAccessHelper.PhotoAsset): Promise<string> {
  // 请求低分辨率图片数据
  const buffer = await asset.requestBuffer({
    width: 640,
    height: 480,
    quality: 0.6
  });
  
  // 保存为临时预览图
  const tempUri = await this.saveTempImage(buffer);
  return tempUri;
}

// 处理高质量原图
private async saveHighQualityPhoto(asset: photoAccessHelper.PhotoAsset): Promise<string> {
  // 获取原始质量图片数据
  const buffer = await asset.requestBuffer({
    quality: 1.0 // 原始质量
  });
  
  // 保存到媒体库
  const savedAsset = await photoAccessHelper.createPhotoAsset(buffer);
  await savedAsset.save();
  return savedAsset.uri;
}

分段式拍照的优势数据对比:

  • 传统模式:从拍摄到预览显示平均需 800-1500ms
  • 分段式模式:第一阶段预览显示平均仅需 200-300ms,整体体验提升 60% 以上

3.3 跨设备相机协同

HarmonyOS 的分布式能力允许设备间共享相机资源,例如平板可以调用手机的摄像头进行拍摄,这在大屏幕设备无摄像头或需要更高质量拍摄时非常实用。

跨设备相机调用需要满足以下条件:

  • 本端设备:HarmonyOS NEXT 及以上的平板或 2in1 设备
  • 远端设备:HarmonyOS NEXT 及以上、具有相机能力的手机或平板
  • 双端设备登录同一华为账号
  • 开启 WLAN 和蓝牙,最好接入同一局域网
TypeScript 复制代码
import { collaboration } from '@kit.CollaborationKit';

class CrossDeviceCamera {
  private collaborationManager: collaboration.CollaborationManager;
  private remoteDevice: collaboration.DeviceInfo | null = null;

  // 初始化协同服务
  async init() {
    this.collaborationManager = collaboration.getCollaborationManager();
    // 监听设备变化
    this.collaborationManager.on('deviceFound', (devices: Array<collaboration.DeviceInfo>) => {
      this.handleFoundDevices(devices);
    });
    // 开始发现设备
    await this.collaborationManager.startDiscovering({
      filter: [collaboration.DeviceType.PHONE] // 只搜索手机设备
    });
  }

  // 处理发现的设备
  private handleFoundDevices(devices: Array<collaboration.DeviceInfo>) {
    // 选择第一个可用设备
    this.remoteDevice = devices.find(device => 
      device.capabilities.includes(collaboration.Capability.CAMERA)
    ) || null;
  }

  // 发起跨设备拍照请求
  async takeRemotePhoto(): Promise<string> {
    if (!this.remoteDevice) {
      throw new Error('未找到可用的远程设备');
    }

    // 调用远程设备相机
    const result = await collaboration.invokeRemoteAbility({
      deviceId: this.remoteDevice.deviceId,
      abilityType: collaboration.AbilityType.CAMERA,
      parameters: {
        action: collaboration.CameraAction.TAKE_PHOTO,
        quality: 'high',
        needPreview: true
      },
      timeout: 30000 // 30秒超时
    });

    if (result.code === 0 && result.data?.uri) {
      // 获取远程照片
      return this.fetchRemotePhoto(result.data.uri);
    } else {
      throw new Error(`跨设备拍照失败: ${result.message}`);
    }
  }

  // 获取远程照片
  private async fetchRemotePhoto(remoteUri: string): Promise<string> {
    // 通过分布式文件服务获取照片
    const fileContent = await collaboration.getRemoteFile({
      deviceId: this.remoteDevice!.deviceId,
      uri: remoteUri
    });
    
    // 保存到本地
    const localAsset = await photoAccessHelper.createPhotoAsset(fileContent);
    await localAsset.save();
    return localAsset.uri;
  }
}

跨设备相机应用的典型场景:

  1. 平板调用手机高像素摄像头进行拍摄
  2. 家庭合影时用平板预览,手机作为远程相机放置在最佳角度
  3. 会议场景中,电脑调用手机摄像头获得更好的拍摄角度

四、调试与优化实践

相机应用开发涉及硬件交互、权限管理和资源调度等复杂逻辑,容易出现各种问题。掌握有效的调试方法和优化技巧,能显著提升开发效率和应用质量。

4.1 常见错误与解决方案

|--------------------|---------------------|---------------------------------------|
| 错误现象 | 可能原因 | 解决方案 |
| 相机无法打开,错误码 7400109 | 相机设备被占用 | 确保所有相机资源正确释放;在创建新会话前检查并关闭现有会话 |
| 拍照后无回调响应 | 会话提前停止;权限不足 | 等待拍照回调完成后再停止会话;添加适当延时;检查存储权限 |
| 预览画面黑屏 | Surface 未正确绑定;会话未启动 | 确认预览 Surface 已正确初始化;检查会话启动状态 |
| 照片保存失败 | 存储权限未授予;磁盘空间不足 | 检查 WRITE_IMAGEVIDEO_STORAGE 权限;处理存储异常 |
| 跨设备调用失败 | 设备未登录同一账号;网络问题 | 验证账号一致性;检查 WLAN 和蓝牙状态;确保设备在同一网络 |

资源释放不彻底是最常见的问题根源,正确的释放顺序应为:

  1. 停止会话(stop ())
  2. 移除所有输入输出流
  3. 释放输出流资源(release ())
  4. 关闭相机输入(close ())
  5. 释放会话资源(release ())
TypeScript 复制代码
// 安全释放相机资源的完整实现
async safeReleaseResources() {
  // 使用try-catch确保所有步骤都能执行
  try {
    if (this.photoSession) {
      // 停止会话时使用延时确保任务完成
      await Promise.all([
        this.photoSession.stop(),
        new Promise(resolve => setTimeout(resolve, 500))
      ]);
      this.photoSession.removeAllInputs();
      this.photoSession.removeAllOutputs();
      await this.photoSession.release();
      this.photoSession = null;
    }
  } catch (err) {
    console.error(`释放会话失败: ${(err as BusinessError).message}`);
  }

  try {
    if (this.photoOutput) {
      this.photoOutput.off('photoAvailable');
      this.photoOutput.off('highQualityPhotoAvailable');
      await this.photoOutput.release();
      this.photoOutput = null;
    }
  } catch (err) {
    console.error(`释放拍照输出失败: ${(err as BusinessError).message}`);
  }

  // 其他资源释放...
}

4.2 性能优化策略

相机应用的性能直接影响用户体验,尤其是在低端设备上,需要特别注意优化。

1.分辨率适配优化

根据设备性能动态调整相机分辨率,避免在低端设备上使用过高分辨率:

TypeScript 复制代码
// 根据设备性能选择合适的分辨率
async selectOptimalResolution() {
  const deviceLevel = await this.getDevicePerformanceLevel();
  const allProfiles = await this.cameraManager.getSupportedPhotoProfiles();
  
  // 低端设备选择中等分辨率
  if (deviceLevel === 'low') {
    return allProfiles.find(p => p.resolution.width <= 1920) || allProfiles[0];
  }
  // 高端设备选择高分辨率
  return allProfiles[allProfiles.length - 1];
}

2.帧率控制

在光线充足场景降低帧率以节省电量,在动态场景提高帧率保证流畅度:

TypeScript 复制代码
// 根据场景自动调整帧率
async adjustFrameRate(lightLevel: number) {
  if (!this.previewOutput) return;
  
  // 光线充足时降低帧率
  if (lightLevel > 50) {
    await this.previewOutput.setFpsRange({ min: 15, max: 20 });
  } else {
    // 低光环境提高帧率保证流畅度
    await this.previewOutput.setFpsRange({ min: 25, max: 30 });
  }
}

3.内存管理

相机数据处理会占用大量内存,需及时释放不再使用的缓冲区:

TypeScript 复制代码
// 高效处理图像数据
async processImageBuffer(buffer: ArrayBuffer) {
  try {
    // 处理图像数据...
    const processedData = await this.imageProcessor.process(buffer);
    return processedData;
  } finally {
    // 手动释放缓冲区(如适用)
    if (buffer instanceof SharedArrayBuffer) {
      this.releaseSharedBuffer(buffer);
    }
  }
}

4.后台处理优化

将照片后期处理等耗时操作放到工作线程执行,避免阻塞 UI:

TypeScript 复制代码
// 使用工作线程处理照片
async processPhotoInBackground(photoUri: string) {
  // 创建工作线程
  const worker = new Worker('entry/ets/workers/photoProcessor.ts');
  
  return new Promise<string>((resolve, reject) => {
    worker.onmessage = (msg) => {
      worker.terminate(); // 处理完成后终止工作线程
      resolve(msg.data.resultUri);
    };
    
    worker.onerror = (err) => {
      worker.terminate();
      reject(err);
    };
    
    // 发送处理任务
    worker.postMessage({ uri: photoUri, action: 'enhance' });
  });
}

4.3 兼容性适配

HarmonyOS 设备种类繁多,从手机、平板到智能设备,硬件能力差异较大,需要做好兼容性适配。

1.功能分级适配

根据设备能力动态启用或禁用高级功能:

TypeScript 复制代码
// 功能分级适配策略
async adaptToDeviceCapabilities() {
  // 基础功能:所有设备都支持
  this.supportedFeatures = {
    basicPhoto: true,
    preview: true
  };
  
  // 检查高级功能支持性
  try {
    // 检查HDR支持
    this.supportedFeatures.hdrVivid = await this.checkHdrVividSupport();
    
    // 检查分段式拍照支持
    this.supportedFeatures.segmentedCapture = this.photoOutput?.isSegmentedCaptureSupported() || false;
    
    // 检查动图拍摄支持
    this.supportedFeatures.movingPhoto = this.photoOutput?.isMovingPhotoSupported() || false;
  } catch (err) {
    console.warn('检查设备功能时出错', err);
  }
  
  // 根据支持的功能更新UI
  this.updateFeatureUI();
}

2.分辨率适配

获取设备屏幕信息,选择匹配的预览和拍照分辨率:

TypeScript 复制代码
// 根据屏幕尺寸适配分辨率
async adaptToScreen() {
  // 获取屏幕信息
  const screenInfo = this.getScreenInfo();
  const screenRatio = screenInfo.width / screenInfo.height;
  
  // 获取支持的预览配置
  const previewProfiles = await this.cameraManager.getSupportedPreviewProfiles();
  
  // 选择与屏幕比例最接近的预览配置
  let bestPreviewProfile = previewProfiles[0];
  let minRatioDiff = Math.abs(bestPreviewProfile.resolution.width / bestPreviewProfile.resolution.height - screenRatio);
  
  previewProfiles.forEach(profile => {
    const profileRatio = profile.resolution.width / profile.resolution.height;
    const ratioDiff = Math.abs(profileRatio - screenRatio);
    if (ratioDiff < minRatioDiff) {
      minRatioDiff = ratioDiff;
      bestPreviewProfile = profile;
    }
  });
  
  // 应用最佳预览配置
  await this.previewOutput?.setProfile(bestPreviewProfile);
}

3.权限适配

不同 HarmonyOS 版本的权限要求可能不同,需做好版本适配:

TypeScript 复制代码
// 权限版本适配
getRequiredPermissions(): Array<string> {
  const osVersion = this.getHarmonyOSVersion();
  const permissions = ['ohos.permission.CAMERA'];
  
  // HarmonyOS 4.0及以上版本使用新的存储权限
  if (osVersion >= 4.0) {
    permissions.push('ohos.permission.WRITE_IMAGEVIDEO_STORAGE');
  } else {
    permissions.push('ohos.permission.WRITE_EXTERNAL_STORAGE');
  }
  
  // 跨设备功能需要额外权限
  if (this.enableCrossDevice) {
    permissions.push('ohos.permission.DISTRIBUTED_DATASYNC');
  }
  
  return permissions;
}

五、总结

HarmonyOS Camera Kit 为开发者提供了强大而灵活的相机开发框架,通过统一的 API 接口和完善的功能封装,大幅降低了相机应用的开发难度。从基础的拍照录用到高级的 HDR Vivid 拍摄,从本地设备到跨设备协同,Camera Kit 覆盖了各种场景需求。

本文详细介绍了 Camera Kit 的核心概念、会话管理机制和基础功能实现,深入讲解了 HDR Vivid、分段式拍照等高级特性,并提供了完整的代码示例。同时,针对开发过程中常见的问题,给出了实用的调试方法和优化策略。

随着 HarmonyOS 生态的不断发展,相机能力还将持续增强。未来,我们可以期待更多创新功能,如 AI 辅助构图、多设备协同拍摄、更高效的图像处理等。对于开发者而言,掌握 Camera Kit 不仅能开发出高质量的相机应用,还能将相机能力与其他服务结合,创造出更丰富的全场景应用体验。

相机应用开发虽然复杂,但只要理解了核心原理,掌握了正确的开发方法,就能充分发挥 HarmonyOS 的优势,打造出体验卓越的相机功能。希望本文的内容能为 HarmonyOS 相机应用开发者提供有价值的参考和帮助。