马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
情况
Flutter 3.29
macOS Sequoia 15.4.1
Xcode 16.3
集成
Flutter提供了camera插件来拍照和录视频,它提供了一系列可用的相机,并利用特定的相机展示相机预览、拍照、录视频。
添加依赖
- camera: 提供利用设备相机模块的工具
- path_provider: 寻找存储图片的精确路径
- path: 创建适配任何平台的路径
- flutter pub add camera path_provider path
复制代码 执行完成后iOS工程的GeneratedPluginRegistrant.m文件会自动天生对应的集成代码
- #if __has_include(<camera_avfoundation/CameraPlugin.h>)
- #import <camera_avfoundation/CameraPlugin.h>
- #else
- @import camera_avfoundation;
- #endif
- #if __has_include(<path_provider_foundation/PathProviderPlugin.h>)
- #import <path_provider_foundation/PathProviderPlugin.h>
- #else
- @import path_provider_foundation;
- #endif
- ...
- [CameraPlugin registerWithRegistrar:[registry registrarForPlugin:@"CameraPlugin"]];
- [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]];
复制代码 分析
[registry registrarForPlugin “CameraPlugin”]
在AppDelegate.swfit的application(_:didFinishLaunchingWithOptions 中将FlutterAppDelegate的子类AppDelegate对象作为参数传入并调用该方法
- GeneratedPluginRegistrant.register(with: self)
复制代码 源码: FlutterAppDelegate.mm
- - (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
- /// <1> 获取应用的flutterRootViewController对象
- FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
- if (flutterRootViewController) {
- /// <4> 返回一个FlutterEngine对象
- return [[flutterRootViewController pluginRegistry] registrarForPlugin:pluginKey];
- }
- return nil;
- }
- // Returns the key window's rootViewController, if it's a FlutterViewController.
- // Otherwise, returns nil.
- - (FlutterViewController*)rootFlutterViewController {
- ///- (FlutterViewController*(^ rootFlutterViewControllerGetter) (void))
- /// <2> 检查是否有外部注入,有则使用自定义的回调获取FlutterViewController对象
- if (_rootFlutterViewControllerGetter != nil) {
- return _rootFlutterViewControllerGetter();
- }
- /// <3> 没有则检查window的rootViewController属性,如果是FlutterViewController则返回,否则返回nil
- UIViewController* rootViewController = _window.rootViewController;
- if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
- return (FlutterViewController*)rootViewController;
- }
- return nil;
- }
复制代码 [flutterRootViewController pluginRegistry]
第<4>步中的方法调用在 FlutterViewController.mm
- /// pluginRegistry方法获得一个遵守FlutterPluginRegistry协议的对象
- - (id<FlutterPluginRegistry>)pluginRegistry {
- return self.engine;
- }
- ...
- /// engine是FlutterEngine对象
- - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project
- initialRoute:(nullable NSString*)initialRoute {
- ...
- engine = [[FlutterEngine alloc] initWithName:@"io.flutter"
- project:project
- allowHeadlessExecution:self.engineAllowHeadlessExecution
- restorationEnabled:self.restorationIdentifier != nil];
- ...
- _engine = engine;
- ...
- - (instancetype)initWithEngine:(FlutterEngine*)engine
- nibName:(nullable NSString*)nibName
- bundle:(nullable NSBundle*)nibBundle {
- ...
- _engine = engine;
复制代码 FlutterPluginRegistry协议的继承布局
FlutterEngine对象调用registrarForPlugin:方法
源码: FlutterEngine.mm
- /// 文件: FlutterEngine.mm
- ...
- /**
- * All registrars returned from registrarForPlugin:
- */
- @property(nonatomic, readonly)
- NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
- ...
- - (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {
- /// <5> 检查可变字典中是否已存在插件名的key
- id<FlutterPluginRegistrar> registrar = self.pluginRegistrars[pluginName];
- if (!registrar) {
- /// <6> 首次注册,生成FlutterEngineRegistrar对象并持有pluginName和弱引用FlutterEngine 对象
- /// 为什么是弱引用?
- /// 文件:FlutterViewController.m 强引入了FlutterEngine对象
- /// @property(nonatomic, readonly) FlutterEngine* engine;
-
- FlutterEngineRegistrar* registrarImpl =
- [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self];
- /// 接收传入的pluginName + self即FlutterEngine对象
- /// @interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
- /// @property(nonatomic, weak) FlutterEngine* flutterEngine;
- /// @implementation FlutterEngineRegistrar {
- /// NSString* _pluginKey;
- /// }
-
- /// 因为FlutterViewController.m 已经强引入了FlutterEngine对象,所以这里的flutterEngine弱引用即可
- /// @property(nonatomic, readonly) FlutterEngine* engine;
- ///
- /// <7> 添加到FlutterEngine对象的可变字典中
- self.pluginRegistrars[pluginName] = registrarImpl;
- registrar = registrarImpl;
- }
- /// <8> 返回FlutterEngineRegistrar注册对象,其中保存FlutterEngine相关的信息,负责与Flutter的iOS插件交互
- return registrar;
- }
复制代码 [CameraPlugin registerWithRegistrar:registrar]
- /// camera插件的类定义
- public final class CameraPlugin: NSObject, FlutterPlugin {
复制代码- @protocol FlutterPlugin <NSObject, FlutterApplicationLifeCycleDelegate>
- @required
- + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar;
复制代码 Flutter中的iOS插件服从FlutterPlugin协议且必须实现+registerWithRegistrar:方法
以camera插件为例,CameraPlugin.swift是对外的Swift的接口包装
- public static func register(with registrar: FlutterPluginRegistrar) {
- let instance = CameraPlugin(
- /// 文件: FlutterEngine.mm
- /// <9> 从FlutterEngineRegistrar对象中获取纹理的对象
- /// - (NSObject<FlutterTextureRegistry>*)textures {
- /// return _flutterEngine.textureRegistry;
- /// }
- registry: registrar.textures(),
- /// Returns a `FlutterBinaryMessenger` for creating Dart/iOS communication
- /// channels to be used by the plugin.
- /// <10> 从FlutterEngineRegistrar返回Dart与iOS原生消息的对象
- messenger: registrar.messenger(),
- globalAPI: FCPCameraGlobalEventApi(binaryMessenger: registrar.messenger()),
- deviceDiscoverer: FLTDefaultCameraDeviceDiscoverer(),
- permissionManager: FLTCameraPermissionManager(
- permissionService: FLTDefaultPermissionService()),
- deviceFactory: { name in
- // TODO(RobertOdrowaz) Implement better error handling and remove non-null assertion
- FLTDefaultCaptureDevice(device: AVCaptureDevice(uniqueID: name)!)
- },
- captureSessionFactory: { FLTDefaultCaptureSession(captureSession: AVCaptureSession()) },
- captureDeviceInputFactory: FLTDefaultCaptureDeviceInputFactory(),
- captureSessionQueue: DispatchQueue(label: "io.flutter.camera.captureSessionQueue")
- )
-
- /// <11>设置Dart相机API的消息通道
- SetUpFCPCameraApi(registrar.messenger(), instance)
- }
复制代码 registrar.messenger()
从前面可知registrar是一个FlutterEngineRegistrar
- /// 文件: FlutterEngine.mm
- @implementation FlutterEngineRegistrar {
- ...
- - (NSObject<FlutterBinaryMessenger>*)messenger {
- /// 返回的是FlutterEngineRegistrar对象绑定的FlutterEngine中的binaryMessenger属性
- return _flutterEngine.binaryMessenger;
- }
- ...
- @implementation FlutterEngine {
- ...
- FlutterBinaryMessengerRelay* _binaryMessenger;
- ...
- /// FlutterEngine对象中的binaryMessenger属性是FlutterBinaryMessengerRelay对象
- /// 且parent属性关联的是FlutterEngine对象
- _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
复制代码 SetUpFCPCameraApi(registrar.messenger(), instance)
代码执行进入文件 message.g.m
- void SetUpFCPCameraApiWithSuffix(id<FlutterBinaryMessenger> binaryMessenger,
- NSObject<FCPCameraApi> *api, NSString *messageChannelSuffix) {
- messageChannelSuffix = messageChannelSuffix.length > 0
- ? [NSString stringWithFormat:@".%@", messageChannelSuffix]
- : @"";
- /// Returns the list of available cameras.
- /// 建立设备的相机可用列表API
- {
- FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
- initWithName:[NSString stringWithFormat:@"%@%@",
- @"dev.flutter.pigeon.camera_avfoundation."
- @"CameraApi.getAvailableCameras",
- messageChannelSuffix]
- binaryMessenger:binaryMessenger
- codec:FCPGetMessagesCodec()];
- if (api) {
- NSCAssert(
- [api respondsToSelector:@selector(availableCamerasWithCompletion:)],
- @"FCPCameraApi api (%@) doesn't respond to @selector(availableCamerasWithCompletion:)",
- api);
- [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
- [api availableCamerasWithCompletion:^(
- NSArray<FCPPlatformCameraDescription *> *_Nullable output,
- FlutterError *_Nullable error) {
- callback(wrapResult(output, error));
- }];
- }];
- } else {
- [channel setMessageHandler:nil];
- }
- }
- ...
- /// 绑定一系列相机操作的API
-
复制代码 FlutterBasicMessageChannel类在FlutterChannel.m中,先初始化一个FlutterBasicMessageChannel对象,实现上只是接收外界参数
- - (instancetype)initWithName:(NSString*)name
- binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
- codec:(NSObject<FlutterMessageCodec>*)codec
- taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
- self = [super init];
- NSAssert(self, @"Super init cannot be nil");
- _name = [name copy];
- _messenger = messenger;
- _codec = codec;
- _taskQueue = taskQueue;
- return self;
- }
复制代码 [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) ...];
接着判断入参的api是否不为空,api是天生的CameraPlugin对象,以是不为空,然后消息的回调
- - (void)setMessageHandler:(FlutterMessageHandler)handler {
- /// 未自定义回调时,这里应该是多次调用则清空上一次的,然后再重新创建
- if (!handler) {
- if (_connection > 0) {
- [_messenger cleanUpConnection:_connection];
- _connection = 0;
- } else {
- [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
- }
- return;
- }
- // Grab reference to avoid retain on self.
- // `self` might be released before the block, so the block needs to retain the codec to
- // make sure it is not released with `self`
- /// 从前面的代码可以知道这个self即channel对象只有一个局部对象在持有,所以超过作用域会被回收,所以这里接收到codec
- NSObject<FlutterMessageCodec>* codec = _codec;
- FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
- handler([codec decode:message], ^(id reply) {
- callback([codec encode:reply]);
- });
- };
- _connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
- }
复制代码- static FlutterBinaryMessengerConnection SetMessageHandler(
- NSObject<FlutterBinaryMessenger>* messenger,
- NSString* name,
- FlutterBinaryMessageHandler handler,
- NSObject<FlutterTaskQueue>* taskQueue) {
- /// 是否要在指定的任务队列上执行
- /// name在这里是 dev.flutter.pigeon.camera_avfoundation.CameraApi.getAvailableCameras...
- /// handler是设置的回调
- /// 发送给FlutterBinaryMessengerRelay对象
- if (taskQueue) {
- NSCAssert([messenger respondsToSelector:@selector(setMessageHandlerOnChannel:
- binaryMessageHandler:taskQueue:)],
- @"");
- return [messenger setMessageHandlerOnChannel:name
- binaryMessageHandler:handler
- taskQueue:taskQueue];
- } else {
- return [messenger setMessageHandlerOnChannel:name binaryMessageHandler:handler];
- }
- }
复制代码 进入到FlutterBinaryMessengerRelay对象的setMessageHandlerOnChannel:binaryMessageHandler:taskQueue:
文件: FlutterBinaryMessengerRelay.mm
- - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel
- binaryMessageHandler:(FlutterBinaryMessageHandler)handler
- taskQueue:
- (NSObject<FlutterTaskQueue>*)taskQueue {
- /// parent就是engine对象,因此又回到engine上setMessageHandlerOnChannel:binaryMessageHandler: taskQueue:
- if (self.parent) {
- return [self.parent setMessageHandlerOnChannel:channel
- binaryMessageHandler:handler
- taskQueue:taskQueue];
- } else {
- FML_LOG(WARNING) << "Communicating on a dead channel.";
- return -1;
- }
- }
复制代码 文件: FlutterEngine.mm
- - (FlutterBinaryMessengerConnection)
- setMessageHandlerOnChannel:(NSString*)channel
- binaryMessageHandler:(FlutterBinaryMessageHandler)handler
- taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
- NSParameterAssert(channel);
- if (_shell && _shell->IsSetup()) {
- /// 获取原生平台的线程,并传入channel名,回调,任务队列
- self.platformView->GetPlatformMessageHandlerIos()->SetMessageHandler(channel.UTF8String,
- handler, taskQueue);
- /// std::unique_ptr<flutter::ConnectionCollection> _connections;
- ///
- /// 文件:connection_collection.mm
- /// ConnectionCollection::Connection ConnectionCollection::AquireConnection(const std::string& name) {
- /// Connection nextConnection = ++counter_;
- /// connections_[name] = nextConnection;
- /// return nextConnection;
- /// }
- /// FlutterEngine对象中的连接集合属性,AcquireConnection方法让connections字典中key为channel的计数加1
- return _connections->AquireConnection(channel.UTF8String);
- } else {
- NSAssert(!handler, @"Setting a message handler before the FlutterEngine has been run.");
- // Setting a handler to nil for a channel that has not yet been set up is a no-op.
- return flutter::ConnectionCollection::MakeErrorConnection(-1);
- }
- }
复制代码 文件: platform_view_ios.h
- /// 调用GetPlatformMessageHandlerIos即返回platform_message_handler_属性
- class PlatformViewIOS final : public PlatformView {
- ...
- std::shared_ptr<PlatformMessageHandlerIos> GetPlatformMessageHandlerIos() const {
- return platform_message_handler_;
- }
- ...
复制代码 文件: platform_view_ios.mm
- PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate,
- const std::shared_ptr<IOSContext>& context,
- __weak FlutterPlatformViewsController* platform_views_controller,
- const flutter::TaskRunners& task_runners)
- : PlatformView(delegate, task_runners),
- ios_context_(context),
- platform_views_controller_(platform_views_controller),
- accessibility_bridge_([this](bool enabled) { PlatformView::SetSemanticsEnabled(enabled); }),
- /// 从初始化列表中可以看出platform_message_handler_的值通过GetPlatformTaskRunner获取UI的主线程
- platform_message_handler_(
- new PlatformMessageHandlerIos(task_runners.GetPlatformTaskRunner())) {}
复制代码 到这里Flutter的插件的原生代码部分已经将channel,回调,执行队列(可选)给原生平台的主线程。
获取可用相机列表
- /// Dart端调用获取可用摄像头列表
- final cameras = await availableCameras();
复制代码 文件: camera_controller.dart
- /// Completes with a list of available cameras.
- ///
- /// May throw a [CameraException].
- Future<List<CameraDescription>> availableCameras() async {
- return CameraPlatform.instance.availableCameras();
- }
复制代码 调用CameraPlatform对象的availableCameras方法
文件: camera_platform.dart
- abstract class CameraPlatform extends PlatformInterface {
- /// Constructs a CameraPlatform.
- CameraPlatform() : super(token: _token);
- ...
- /// Completes with a list of available cameras.
- ///
- /// This method returns an empty list when no cameras are available.
- Future<List<CameraDescription>> availableCameras() {
- throw UnimplementedError('availableCameras() is not implemented.');
- }
复制代码 CameraPlatform是个抽象类,要找详细的实现。找到camera插件的pubspec.yaml
- flutter:
- plugin:
- implements: camera
- platforms:
- ios:
- pluginClass: CameraPlugin
- dartPluginClass: AVFoundationCamera
复制代码 dartPluginClass: Optional. The Dart class that serves as the entry point for a Flutter plugin. This can be used with the Android, iOS, Linux macOS, and Windows platforms.
因此camera插件dart的入口应该是AVFoundationCamera这个类,它继承了上面的CameraPlatform抽象类
文件: avfoundation_camera.dart
- class AVFoundationCamera extends CameraPlatform {
- /// Creates a new AVFoundation-based [CameraPlatform] implementation instance.
- AVFoundationCamera({@visibleForTesting CameraApi? api})
- : _hostApi = api ?? CameraApi();
- ...
- @override
- Future<List<CameraDescription>> availableCameras() async {
- try {
- return (await _hostApi.getAvailableCameras())
- .map(cameraDescriptionFromPlatform)
- .toList();
- } on PlatformException catch (e) {
- throw CameraException(e.code, e.message);
- }
- }
复制代码 从上面代码可知实际调用的是_hostApi的getAvailableCameras方法
- Future<List<PlatformCameraDescription>> getAvailableCameras() async {
- /// 前面建立channel时已经传入了,这样原生执行完相关方法后能通过channel调用对应的回调
- final String pigeonVar_channelName =
- 'dev.flutter.pigeon.camera_avfoundation.CameraApi.getAvailableCameras$pigeonVar_messageChannelSuffix';
-
- /// A named channel for communicating with platform plugins using asynchronous message passing.
- /// 创建异步消息
- final BasicMessageChannel<Object?> pigeonVar_channel =
- BasicMessageChannel<Object?>(
- pigeonVar_channelName,
- pigeonChannelCodec,
- binaryMessenger: pigeonVar_binaryMessenger,
- );
- /// 阻塞**发送消息**
- final List<Object?>? pigeonVar_replyList =
- await pigeonVar_channel.send(null) as List<Object?>?;
-
- /// 消息为空抛异常
- if (pigeonVar_replyList == null) {
- throw _createConnectionError(pigeonVar_channelName);
- } else if (pigeonVar_replyList.length > 1) {
- throw PlatformException(
- code: pigeonVar_replyList[0]! as String,
- message: pigeonVar_replyList[1] as String?,
- details: pigeonVar_replyList[2],
- );
- } else if (pigeonVar_replyList[0] == null) {
- throw PlatformException(
- code: 'null-error',
- message: 'Host platform returned null value for non-null return value.',
- );
- } else {
- /// 得到可用列表转换为Flutter上的摄像头描述的对象
- return (pigeonVar_replyList[0] as List<Object?>?)!
- .cast<PlatformCameraDescription>();
- }
- }
复制代码 await pigeonVar_channel.send(null) as List<Object?>?;
- /// Sends the specified [message] to the platform plugins on this channel.
- ///
- /// Returns a [Future] which completes to the received response, which may
- /// be null.
- Future<T?> send(T message) async {
- /// 调用binaryMessenger对象编码发送消息 && 得到返回后再解码
- return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
- }
复制代码 Flutter Dart端发送指定channel消息 && 调用原来添加的回调走到Flutter原生插件部分这里,好比当前走到获取可用摄像头的回调
- ...
- [api availableCamerasWithCompletion:^(
- NSArray<FCPPlatformCameraDescription *> *_Nullable output,
- FlutterError *_Nullable error) {
- callback(wrapResult(output, error));
- }];
复制代码 即调用api(CameraPlugin)的availableCamerasWithCompletion:方法
- extension CameraPlugin: FCPCameraApi {
- public func availableCameras(
- completion: @escaping ([FCPPlatformCameraDescription]?, FlutterError?) -> Void
- ) {
- captureSessionQueue.async { [weak self] in
- guard let strongSelf = self else { return }
- var discoveryDevices: [AVCaptureDevice.DeviceType] = [
- .builtInWideAngleCamera,
- .builtInTelephotoCamera,
- ]
- ...
- /// 前置,后置等摄像头,然后统一添加到reply这个数组对象
- for device in devices {
- var lensFacing: FCPPlatformCameraLensDirection
- switch device.position {
- case .back:
- lensFacing = .back
- case .front:
- lensFacing = .front
- case .unspecified:
- ...
- }
- reply.append(cameraDescription)
- }
-
- /// 最后执行callback(wrapResult(output, error));
- /// 获取成功后,Flutter的Dart部分可以获取到可用的摄像头列表
- completion(reply, nil)
- }
复制代码 总结
- 原生插件将方法的channel(约定的字符串),回调设置给Flutter原生平台代码(C++ && OC)
- Flutter的Dart层发送消息并通报channel,Flutter根据channel找到设置的回调(Dart -> C++ && OC),然后执行原生插件提供的方法(C++ && OC)
- 获取执行结果并返回给Flutter的原生平台代码(C++ && OC),再发送给Dart代码(C++ && OC -> Dart)
参考
- camera
- 利用 Camera 插件实现拍照功能
- do-not-use-buildcontexts-across-async-gaps
- Flutter pubspec options
- 一张图明白Flutter中Dart与原生情况通信
|