Flutter 相机插件 camera_android 完全指南:Camera2 实现原理、接入方式与模拟器录制限制
【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages
导读
camera_android是 Flutter 官方camera插件在 Android 平台上的实现之一,底层基于 Android 的 Camera2 库 构建,负责把 Dart 层的相机 API 调用翻译为 Android 原生相机能力。本篇文章将带你了解它作为联邦插件(Federated Plugin)在 Flutter 相机体系中的定位、如何通过一条命令把它接入项目、它在 Dart 与 Java 两侧的实现架构,以及官方明确警告的模拟器视频录制测试限制——读完你既能快速上手,也能理解其底层工作原理,避免在实际开发中踩坑。
一、camera_android 在 Flutter 相机体系中的定位
从仓库目录结构可以看出,packages/camera下同时存在多个实现包:
camera/camera:对外统一暴露 API 的“外壳”包(app-facing 包);camera/camera_android:基于Camera2 库的 Android 实现;camera/camera_android_camerax:基于CameraX的另一种 Android 实现;camera/camera_avfoundation、camera_web、camera_windows:分别对应 iOS/macOS、Web、Windows 平台。
这是 Flutter 官方推荐的endorsed federated plugin(联邦插件)模式:camera_android通过implements: camera声明自己是camera平台接口的实现者。打开 pubspec.yaml 可以看到这段关键声明:
flutter: plugin: implements: camera platforms: android: package: io.flutter.plugins.camera pluginClass: CameraPlugin dartPluginClass: AndroidCamera其中:
implements: camera表示该插件“代言”camera包的 Android 平台实现;package: io.flutter.plugins.camera与pluginClass: CameraPlugin指向 Android 原生入口类;dartPluginClass: AndroidCamera指向 Dart 层实现类 AndroidCamera,它在registerWith()中将自己注册为CameraPlatform.instance的默认实现。
因此,在 Flutter 工程中只要依赖了camera,Android 平台上会自动加载camera_android提供的实现;开发者无需手动在 AndroidManifest 中注册任何东西。
二、快速接入:一条命令启用 Camera2 实现
官方 README 给出的接入方式非常简洁。从camera: ^0.11.0开始,如果你希望使用本插件(而不是默认的camera_android_camerax)作为 Android 端实现,只需在项目根目录执行:
$ flutter pub add camera_androidflutter pub add会自动完成以下工作:
- 把
camera_android写入项目的pubspec.yaml依赖中; - 解析其依赖项(
camera_platform_interface、flutter_plugin_android_lifecycle、meta、stream_transform等,见 pubspec.yaml); - 由于它是
camera的代言实现,运行时CameraPlatform.instance会被替换为AndroidCamera实例。
提示:
camera: ^0.11.0之后,Flutter 官方在 Android 上的默认推荐实现是camera_android_camerax(CameraX);若你的项目因特定原因需要使用基于 Camera2 的传统实现,才显式添加camera_android。仓库 CHANGELOG 中也记录了这一变化过程。
手动指定依赖的等价写法
如果你更习惯手写pubspec.yaml,等价做法是在dependencies中加入:
dependencies: camera: ^0.11.0 camera_android: ^0.10.11三、Dart 层实现剖析:AndroidCamera 与 Pigeon 通道
Dart 侧的实现集中在 lib/src/android_camera.dart。核心类AndroidCamera继承自CameraPlatform,它的职责是:把平台接口层的调用转译为对原生端的 Pigeon 调用,并把原生端回调转译为 Dart 事件流。
3.1 注册与初始化
static void registerWith() { CameraPlatform.instance = AndroidCamera(); }插件加载时,通过这一行把AndroidCamera设为全局平台实例,此后camera包内部所有调用都会路由到这里。
3.2 调用原生端的方式:Pigeon 生成代码
AndroidCamera内部持有一个CameraApi实例(由messages.g.dart通过 Pigeon 生成),所有原生调用都走这个类型安全的通道:
Future<List<CameraDescription>> availableCameras() async { final List<PlatformCameraDescription> cameraDescriptions = await _hostApi.getAvailableCameras(); // ...转换为 CameraDescription 列表 }从 CHANGELOG 可以看到,camera_android在 0.10.9+15 / 0.10.9+14 / 0.10.9+13 一系列版本中,把Dart→原生、原生→Dart以及getAvailableCameras都陆续迁移到了 Pigeon,取代了早期的手写 MethodChannel。仓库中 pigeons 目录保存了接口定义源文件。
3.3 相机事件流:广播 StreamController
原生端产生的事件(初始化完成、分辨率变化、相机关闭、错误、视频录制完成、设备方向变化)通过 Pigeon 回调进入 Dart 层,再由AndroidCamera转发到不同的 Stream:
final StreamController<CameraEvent> cameraEventStreamController = StreamController<CameraEvent>.broadcast(); Stream<CameraEvent> _cameraEvents(int cameraId) => cameraEventStreamController.stream.where((event) => event.cameraId == cameraId); @override Stream<CameraInitializedEvent> onCameraInitialized(int cameraId) { return _cameraEvents(cameraId).whereType<CameraInitializedEvent>(); }选择broadcast类型是因为可能存在多个CameraController同时订阅不同相机事件;每个相机 ID 通过HostCameraMessageHandler建立独立的 Pigeon 消息通道(后缀为$cameraId)。
3.4 图像流(Image Streaming)
AndroidCamera明确支持图像流式输出(supportsImageStreaming() => true)。onStreamedFrameAvailable通过plugins.flutter.io/camera_android/imageStream这个EventChannel接收原生端推送的帧数据,并封装为CameraImageData提供给上层做实时分析等用途。相关实现可见_startStreamListener()与_onFrameStreamCancel()。
3.5 预览 Widget
@override Widget buildPreview(int cameraId) { return Texture(textureId: cameraId); }Android 端预览通过 Flutter 的Texture组件承载,cameraId即原生端注册的纹理 ID,这也是视频录制使用VideoSource.SURFACE作为输入源的原因——渲染与录制共享同一份 Surface 数据。
四、Android 原生层实现:CameraPlugin 与特性化架构
原生端入口是 CameraPlugin.java,它实现了FlutterPlugin与ActivityAware,能够优雅处理 Activity 生命周期变化(如旋转、配置变更),并在合适时机创建CameraApiImpl完成消息通道的建立与权限请求的注册。
4.1 特性化(Feature)架构
从源码目录 features 可以看到,原生实现把相机能力拆成了多个独立“特性”模块:
| 特性模块 | 对应能力 |
|---|---|
autofocus/focuspoint | 对焦模式与对焦点设置 |
exposurelock/exposureoffset/exposurepoint | 曝光锁定、曝光补偿、曝光点 |
flash | 闪光灯模式 |
fpsrange | 帧率范围控制(录制时生效) |
jpegquality | JPEG 压缩质量(对应 0.10.11 新增的setJpegImageQuality) |
noisereduction | 降噪开关 |
resolution | 分辨率预设 |
sensororientation | 传感器方向 |
zoomlevel | 变焦控制 |
它们统一继承自 CameraFeature.java,由CameraFeatureFactory/CameraFeatures统一装配管理。这种设计让每个相机特性的启用、参数校验与状态查询职责单一,也便于针对单个特性编写单元测试(仓库android/src/test下每个 feature 都有对应测试类)。
4.2 权限处理
相机权限由CameraPermissions.java负责,权限结果通过addRequestPermissionsResultListener注册的回调接收。权限被拒绝时,Dart 层会收到CameraException,错误码与平台相关(见下文示例章节的CameraAccessDenied、AudioAccessDenied等)。
五、视频录制与 MediaRecorderBuilder:FPS/码率如何生效
README 特别提到了MediaRecorder,而原生端对MediaRecorder的封装正是 MediaRecorderBuilder.java。
5.1 固定的配置顺序
build()方法内有一段醒目的注释:“There's a fixed order that mediaRecorder expects.”。MediaRecorder对配置调用顺序非常敏感,源码严格按照“音频源 → 视频源 → 输出格式 → 编码器 → 码率/帧率/尺寸 → 输出文件 → 方向提示 → prepare”的顺序执行,任意打乱都可能导致IllegalStateException。
5.2 编码档位选择:双路径兼容
if (SdkCapabilityChecker.supportsEncoderProfiles() && encoderProfiles != null) { // 新 API:EncoderProfiles(Android 12+) mediaRecorder.setOutputFormat(encoderProfiles.getRecommendedFileFormat()); ... } else if (camcorderProfile != null) { // 兼容旧 API:CamcorderProfile mediaRecorder.setOutputFormat(camcorderProfile.fileFormat); ... }从源码可以看出,插件对 Android 12(API 31)及以上使用EncoderProfiles,对旧版本回退到CamcorderProfile,以此兼容不同系统版本上的编码配置获取方式。
5.3 自定义 FPS 与比特率
RecordingParameters携带了fps、videoBitrate、audioBitrate三个可选参数。当开发者显式传入大于 0 的值时,优先使用自定义值,否则回退到系统推荐的档位值:
int fps = (parameters.fps != null && parameters.fps.intValue() > 0) ? parameters.fps : videoProfile.getFrameRate();对应到 Dart 侧,camera包的CameraController.withSettings(以及本仓库示例中使用的MediaSettings)可以把这些参数一路传递到原生端,实现录制帧率与码率的精细控制(见 CHANGELOG 0.10.9 条目:Adds support to control video FPS and bitrate)。
5.4 一个需要留意的行为细节
CHANGELOG 0.10.10+2 记录了一个值得注意的行为:除非正在录制视频,否则不会设置 FPS 范围。原因是在某些设备上固定 min/max FPS 会约束自动曝光算法,导致预览画面偏暗;副作用是仅传fps参数不会影响非录制状态下的预览帧率,如果需要在图像流中做降帧处理,官方建议在 Dart 侧按时间戳跳过帧。
六、模拟器上测试视频录制的限制(官方明确警告)
这是官方 README 用专门章节强调的问题,属于使用本插件(乃至整个 Android 相机生态)时最容易踩的坑:
MediaRecorder在模拟器上无法正常工作(Android 官方文档亦有说明)。具体表现是:当开启声音录制视频并尝试回放时,视频时长不正确,且只能看到第一帧。
这意味着:
- 如果你在 Android 模拟器上编写、调试“带声音的视频录制 → 回放”这类测试用例,很可能得到时长错误、画面卡在第一帧的结果,这是平台层限制,而不是你代码的 bug;
- 建议在真机上验证视频录制的完整链路(采集、编码、落盘、回放);模拟器更适合验证相机枚举、预览、拍照等不依赖
MediaRecorder的功能; - 若必须在 CI/模拟器上跑录制相关冒烟测试,应把断言重点放在“录制流程不抛异常、文件成功生成”等层面,而不是回放时长的精确性。
七、示例工程:完整的相机操作范式
仓库自带示例应用 example/lib/main.dart,它演示了从枚举相机到录制视频的完整流程,是理解 API 用法的第一手资料。
7.1 初始化 CameraController
final cameraController = CameraController( cameraDescription, mediaSettings: MediaSettings( resolutionPreset: kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium, enableAudio: enableAudio, ), imageFormatGroup: ImageFormatGroup.jpeg, ); await cameraController.initialize();初始化完成后,示例还会通过CameraPlatform.instance.getMaxZoomLevel(...)、getMinExposureOffset(...)等接口查询相机能力边界,用于驱动 UI 中的变焦与曝光控件。
7.2 统一的权限错误处理范式
示例中对CameraException的code做了系统化分类处理:
} on CameraException catch (e) { switch (e.code) { case 'CameraAccessDenied': // 相机权限被拒绝 case 'CameraAccessDeniedWithoutPrompt': // iOS:需到设置中开启 case 'CameraAccessRestricted': // 相机访问受限 case 'AudioAccessDenied': // 麦克风权限被拒绝 case 'AudioAccessDeniedWithoutPrompt': case 'AudioAccessRestricted': case 'cameraPermission': // Android 旧版错误码 ... } }注意:CHANGELOG 0.10.0 记录了一个Breaking Change:Android 的相机权限错误码被统一为与其他平台一致的格式。如果你的代码仍处理旧的
cameraPermission异常码,请更新为新的权限异常码体系。
7.3 支持“录制中切换相机”
AndroidCamera实现了setDescriptionWhileRecording,示例中onNewCameraSelected在录制过程中切换相机时会走这条路径(对应 CHANGELOG 0.10.5:Allows camera to be switched while video recording)。
八、版本演进要点(来自 CHANGELOG)
通过 CHANGELOG.md 可以梳理出该插件的重要演进脉络,帮助你判断升级影响:
- 0.9.7+1:从
camera包中拆分为独立的联邦实现包,成为现在的形态; - 0.10.0:统一 Android 权限错误码(Breaking Change);
- 0.10.9:支持视频 FPS 与比特率控制(
CameraController.withSettings); - 0.10.9+13 ~ +15:平台通信全面迁移到 Pigeon;
- 0.10.10+3:初始化时等待 capture session 创建完成,避免线程竞争;
- 0.10.10+2:非录制状态不再设置 FPS 范围(见上文 5.4);
- 0.10.10+4:修复主线程暂停时
startImageStream的 OOM 问题; - 0.10.11:新增
setJpegImageQuality,控制 JPEG 压缩质量; - 0.10.10+16 / +17 / +18:Gradle 构建文件迁移至 Kotlin DSL、修复拍照后闪光灯残留问题(重置 AE/AF 触发器)、最低 SDK 提升至 Flutter 3.38 / Dart 3.10。
同时注意:0.10.9+3 起移除了对 v1 Android embedding 的支持,0.10.9+7 起移除了 API 21–23 的代码,如果你的工程还停留在旧嵌入模式或极低系统版本,升级前需要评估兼容性。
总结
camera_android作为 Fluttercamera生态中基于 Camera2 的 Android 实现,架构上采用联邦插件模式(Dart 层AndroidCamera+ Pigeon 通道 + 原生CameraPlugin),原生端以特性化模块组织各类相机能力,录制链路则由MediaRecorderBuilder按严格顺序装配。接入只需flutter pub add camera_android一条命令(camera: ^0.11.0起)。最后请务必牢记 README 的警告:MediaRecorder在模拟器上无法可靠工作,带声音录制回放会出现时长错误且只能看到第一帧,视频录制相关功能请在真机上验证。
【免费下载链接】packagesA collection of useful packages maintained by the Flutter team项目地址: https://gitcode.com/GitHub_Trending/pac/packages
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考