Flutter - 原生交互 - 相机Camera - 01

环境

Flutter 3.29

macOS Sequoia 15.4.1

Xcode 16.3

集成

Flutter提供了camera插件来拍照和录视频,它提供了一系列可用的相机,并使用特定的相机展示相机预览、拍照、录视频。

添加依赖

  • camera: 提供使用设备相机模块的工具
  • path_provider: 寻找存储图片的正确路径
  • path: 创建适配任何平台的路径
sh 复制代码
flutter pub add camera path_provider path

执行完成后iOS工程的GeneratedPluginRegistrant.m文件会自动生成对应的集成代码

objc 复制代码
#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.swfitapplication(_:didFinishLaunchingWithOptions:)中将FlutterAppDelegate的子类AppDelegate对象作为参数传入并调用该方法

swift 复制代码
GeneratedPluginRegistrant.register(with: self)

源码: FlutterAppDelegate.mm

objc 复制代码
- (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

objc 复制代码
///  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

objc 复制代码
/// 文件: 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]
swift 复制代码
/// camera插件的类定义
public final class CameraPlugin: NSObject, FlutterPlugin {
objc 复制代码
@protocol FlutterPlugin <NSObject, FlutterApplicationLifeCycleDelegate>
@required
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar;

Flutter中的iOS插件遵守FlutterPlugin协议且必须实现+registerWithRegistrar:方法

以camera插件为例,CameraPlugin.swift是对外的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

objc 复制代码
/// 文件: 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

objc 复制代码
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对象,实现上只是接收外界参数

objc 复制代码
- (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对象,所以不为空,然后消息的回调

objc 复制代码
- (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);
}
objc 复制代码
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

objc 复制代码
- (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

objc 复制代码
- (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

objc 复制代码
/// 调用GetPlatformMessageHandlerIos即返回platform_message_handler_属性
class PlatformViewIOS final : public PlatformView {
...
std::shared_ptr<PlatformMessageHandlerIos> GetPlatformMessageHandlerIos() const {
    return platform_message_handler_;
  }
...

文件: platform_view_ios.mm

objc 复制代码
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 复制代码
/// Dart端调用获取可用摄像头列表
final cameras = await availableCameras();

文件: camera_controller.dart

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

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

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

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);
    }
  }

从上面代码可知实际调用的是_hostApigetAvailableCameras方法

dart 复制代码
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?>?;
dart 复制代码
/// 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原生插件部分这里,比如当前走到获取可用摄像头的回调

swift 复制代码
...
[api availableCamerasWithCompletion:^(
                 NSArray<FCPPlatformCameraDescription *> *_Nullable output,
                 FlutterError *_Nullable error) {
          callback(wrapResult(output, error));
        }];

即调用api(CameraPlugin)的availableCamerasWithCompletion:方法

swift 复制代码
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)
}

总结

  1. 原生插件将方法的channel(约定的字符串),回调设置给Flutter原生平台代码(C++ && OC)
  2. Flutter的Dart层发送消息并传递channel,Flutter根据channel找到设置的回调(Dart -> C++ && OC),然后执行原生插件提供的方法(C++ && OC)
  3. 获取执行结果并返回给Flutter的原生平台代码(C++ && OC),再发送给Dart代码(C++ && OC -> Dart)

参考

  1. camera
  2. 使用 Camera 插件实现拍照功能
  3. do-not-use-buildcontexts-across-async-gaps
  4. Flutter pubspec options
  5. 一张图理解Flutter中Dart与原生环境通信
相关推荐
小蜜蜂嗡嗡23 分钟前
flutter项目迁移空安全
javascript·安全·flutter
北极象3 小时前
在Flutter中定义全局对象(如$http)而不需要import
网络协议·flutter·http
明似水5 小时前
Flutter 包依赖升级指南:让项目保持最新状态
前端·flutter
二流小码农6 小时前
鸿蒙开发:hvigorw,一个你不得不去了解的神器
android·ios·harmonyos
吴Wu涛涛涛涛涛Tao6 小时前
老项目登录模块的 Combine + MVVM响应式改造实践
ios
一丝晨光9 小时前
Windows搭建Swift语言编译环境?如何构建ObjC语言编译环境?Swift如何引入ObjC框架?Interface Builder的历史?
linux·windows·macos·ios·objective-c·xcode·swift
别叫我9 小时前
Swift 访问控制
ios
唯有选择9 小时前
flutter_localizations:轻松实现Flutter国际化
flutter
YourReference13 小时前
iOS 集成网易云信IM
ios·im·网易云信·云信
他们都不看好你,偏偏你最不争气13 小时前
iOS —— UI 初探
开发语言·macos·ui·ios·objective-c·面向对象