
HarmonyOS的相机API分散在kit.MultimediaKit里photoAccessHelper管相册、camera管拍照、media管录像。三条线各自独立合在一起才是完整相机功能。这篇把拍照和录像的主线走通顺带说清楚权限和生命周期。权限声明相机开发需要三个权限// module.json5 { requestPermissions: [ { name: ohos.permission.CAMERA }, { name: ohos.permission.MICROPHONE }, { name: ohos.permission.READ_IMAGEVIDEO } ] }CAMERA和MICROPHONE是用户授权权限运行时必须动态申请。READ_IMAGEVIDEO用于保存照片到相册。三个权限缺一个功能就残废。动态申请import{abilityAccessCtrl,common}fromkit.AbilityKit;asyncrequestPermissions(context:common.UIAbilityContext):Promiseboolean{letatManager:abilityAccessCtrl.AccessManagerabilityAccessCtrl.createAtManager();letpermissions:string[][ohos.permission.CAMERA,ohos.permission.MICROPHONE];letresultawaitatManager.requestPermissionsFromUser(context,permissions);for(leti0;iresult.authResults.length;i){if(result.authResults[i]!0){returnfalse;}}returntrue;}authResults为0表示授权通过。不通过的要引导用户去设置页手动开启。CameraManager初始化import{camera}fromkit.MultimediaKit;letcameraManager:camera.CameraManagercamera.getCameraManager(context);就一行但context必须是UIAbilityContext不是BaseContext。在UIAbility里用this.context在页面里用getContext(this)。获取可用相机列表letcameras:camera.CameraDevice[]cameraManager.getSupportedCameras();// cameras[0]通常是后置, cameras[1]通常是前置getSupportedCameras返回的数组长度取决于设备。模拟器可能返回空数组——相机API基本都要真机测试。创建会话拍照用PhotoSession录像用VideoSessionimport{camera}fromkit.MultimediaKit;// 拍照会话letphotoSession:camera.PhotoSessioncameraManager.createPhotoSession(cameras[0]);// 录像会话letvideoSession:camera.VideoSessioncameraManager.createVideoSession(cameras[0]);Session是相机操作的核心。所有配置和操作都挂在Session上。预览预览需要XComponent作为输出面XComponent({id:cameraPreview,type:surface,controller:this.xComponentController}).onLoad((){letsurfaceId:stringthis.xComponentController.getXComponentSurfaceId();// 将surfaceId设置到session的预览输出letpreviewOutput:camera.PreviewOutputcameraManager.createPreviewOutput(profile,surfaceId);photoSession.addOutput(previewOutput);photoSession.start();})流程XComponent提供surface → 创建PreviewOutput → 添加到Session → start。Profile从cameraManager.createProfiles获取letprofiles:camera.CameraOutputCapabilitycameraManager.getSupportedOutputCapability(cameras[0]);letpreviewProfile:camera.Profileprofiles.previewProfiles[0];letphotoProfile:camera.PhotoProfileprofiles.photoProfiles[0];取第一个Profile是最简单的做法生产环境要根据分辨率和帧率筛选。拍照letphotoOutput:camera.PhotoOutputcameraManager.createPhotoOutput(photoProfile,surfaceId);photoSession.addOutput(photoOutput);// 设置拍照回调photoOutput.on(photoAvailable,(photo:camera.Photo):void{// photo可以保存到相册});// 触发拍照photoSession.capture();capture()是异步的拍照结果通过photoAvailable回调返回。回调里拿到的是Photo对象需要进一步处理才能保存。保存到相册import{photoAccessHelper}fromkit.MediaLibraryKit;asyncsavePhoto(photo:camera.Photo,context:common.UIAbilityContext):Promisestring{lethelper:photoAccessHelper.PhotoAccessHelperphotoAccessHelper.getPhotoAccessHelper(context);leturi:stringawaithelper.createAsset(photoAccessHelper.PhotoType.IMAGE,jpg);letfile:fileIo.FilefileIo.openSync(uri,fileIo.OpenMode.WRITE_ONLY);// 从photo中读取数据并写入file// ...fileIo.closeSync(file);returnuri;}createAsset创建相册条目返回uri。然后把照片数据写入uri对应的文件。录像录像需要AVRecorder配合VideoSessionimport{media}fromkit.MultimediaKit;// 创建VideoOutputletvideoOutput:camera.VideoOutputcameraManager.createVideoOutput(videoProfile,surfaceId);videoSession.addOutput(videoOutput);// 创建AVRecorderletavRecorder:media.AVRecorderawaitmedia.createAVRecorder();letconfig:media.AVRecorderConfig{videoSourceType:media.VideoSourceType.CAMERA_SOURCE,profile:{fileFormat:media.ContainerFormatType.CAMERA_MP4,videoBitrate:2000000,videoCodec:media.VideoCodecType.H264,videoFrameWidth:1920,videoFrameHeight:1080,videoFrameRate:30},url:fd://fileDescriptor};awaitavRecorder.prepare(config);// 关联videoOutput和avRecordervideoOutput.start();awaitavRecorder.start();录像流程比较长createAVRecorder → prepare → videoOutput.start → avRecorder.start。每一步都必须等上一步完成。停止录像awaitavRecorder.stop();awaitavRecorder.release();videoOutput.stop();切换前后摄像头asyncswitchCamera(session:camera.PhotoSession,cameraManager:camera.CameraManager,newCamera:camera.CameraDevice):Promisevoid{awaitsession.stop();session.removeOutput(previewOutput);session.removeOutput(photoOutput);// 重新创建session和outputletnewSession:camera.PhotoSessioncameraManager.createPhotoSession(newCamera);// 重新addOutput和start}切换摄像头不能直接替换camera必须停止session、移除旧output、创建新session。简单粗暴但有效。闪光灯// 检查是否支持闪光灯lethasFlash:booleanphotoSession.hasFlash();// 设置闪光灯模式photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_ALWAYS_ON);// 常开photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_AUTO);// 自动photoSession.setFlashMode(camera.FlashMode.FLASH_MODE_CLOSED);// 关闭// 获取当前模式letmode:camera.FlashModephotoSession.getFlashMode();前置摄像头通常不支持闪光灯hasFlash()返回false。这种情况下调setFlashMode会报错。变焦// 获取变焦范围letzoomRange:ArraynumberphotoSession.getZoomRatioRange();letminZoom:numberzoomRange[0];// 如1.0letmaxZoom:numberzoomRange[1];// 如6.0// 设置变焦倍数photoSession.setZoomRatio(2.0);// 获取当前变焦letcurrentZoom:numberphotoSession.getZoomRatio();setZoomRatio的值必须在zoomRatioRange范围内。超出范围直接报参数错误。对焦// 设置对焦模式photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_CONTINUOUS_AUTO);// 连续自动对焦photoSession.setFocusMode(camera.FocusMode.FOCUS_MODE_MANUAL);// 手动对焦// 手动对焦指定点photoSession.setFocusPoint({x:0.5,y:0.5});// 归一化坐标(0-1)手动对焦需要先设FOCUS_MODE_MANUAL再设焦点坐标。坐标是归一化的——(0,0)左上角(1,1)右下角。分辨率选择letoutputCapability:camera.CameraOutputCapabilitycameraManager.getSupportedOutputCapability(cameraDevice);// 遍历可用的photoProfileletphotoProfiles:camera.PhotoProfile[]outputCapability.photoProfiles;for(leti0;iphotoProfiles.length;i){letprofile:camera.PhotoProfilephotoProfiles[i];letwidth:numberprofile.size.width;letheight:numberprofile.size.height;// 选择需要的分辨率}photoProfiles按分辨率从高到低排列。取[0]通常是最高分辨率。状态监听// Session状态photoSession.on(stateChange,(state:camera.SessionState){switch(state){casecamera.SessionState.SESSION_CONFIGURED:// 配置完成break;casecamera.SessionState.SESSION_STARTED:// 已启动break;casecamera.SessionState.SESSION_STOPPED:// 已停止break;}});// 错误监听photoSession.on(error,(error:camera.BusinessError){// 处理相机错误});SessionState有CONFIGURING、CONFIGURED、STARTED、STOPPED四种状态。createPhotoSession后进入CONFIGURINGaddOutput后到CONFIGUREDstart后到STARTED。生命周期管理相机资源必须正确释放否则其他应用无法使用相机aboutToDisappear():void{if(this.photoSession){this.photoSession.stop();this.photoSession.release();}if(this.cameraManager){this.cameraManager.release();}}release顺序Session → Output → CameraManager。每个release都是异步的应该await确保完成。踩坑清单问题原因解决模拟器getSupportedCameras返回空模拟器没有摄像头用真机测试getCameraManager报错context类型不对传UIAbilityContext预览黑屏surfaceId没传或session没start检查XComponent onLoad流程capture()无回调没addOutput(photoOutput)必须把photoOutput加入session切换摄像头crash直接替换camerastop→removeOutput→新建session闪光灯报错前置不支持先hasFlash()检查变焦报错超出zoomRatioRange在范围内设值录像文件打不开AVRecorder没prepare直接start按prepare→start顺序相机被占用上次没releaseaboutToDisappear中释放保存相册失败缺READ_IMAGEVIDEO权限module.json5声明权限相机开发最大的痛点是调试——模拟器几乎不能跑真机每次部署又慢。建议先把权限和初始化流程写对再逐步加功能。拍照→保存→预览→录像一个功能调通再加下一个。