👨🏻‍💻's 博客

慢品人间烟火色,闲观万事岁月长

0%

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

环境

Flutter 3.29

macOS Sequoia 15.4.1

Xcode 16.3

集成

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

添加依赖

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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对象作为参数传入并调用该方法

1
GeneratedPluginRegistrant.register(with: self)

源码: FlutterAppDelegate.mm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- (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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
///  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协议的继承结构

17485097331657.png

FlutterEngine对象调用registrarForPlugin:方法

源码: FlutterEngine.mm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/// 文件: 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]

1
2
/// camera插件的类定义
public final class CameraPlugin: NSObject, FlutterPlugin {
1
2
3
@protocol FlutterPlugin <NSObject, FlutterApplicationLifeCycleDelegate>
@required
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar;

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

以camera插件为例,CameraPlugin.swift是对外的Swift的接口包装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// 文件: 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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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对象,实现上只是接收外界参数

1
2
3
4
5
6
7
8
9
10
11
12
- (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对象,所以不为空,然后消息的回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- (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

1
2
3
4
5
6
7
/// 调用GetPlatformMessageHandlerIos即返回platform_message_handler_属性
class PlatformViewIOS final : public PlatformView {
...
std::shared_ptr<PlatformMessageHandlerIos> GetPlatformMessageHandlerIos() const {
return platform_message_handler_;
}
...

文件: platform_view_ios.mm

1
2
3
4
5
6
7
8
9
10
11
12

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,回调,执行队列(可选)给原生平台的主线程。

获取可用相机列表

1
2
/// Dart端调用获取可用摄像头列表
final cameras = await availableCameras();

文件: camera_controller.dart

1
2
3
4
5
6
/// 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

1
2
3
4
5
6
7
8
9
10
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

1
2
3
4
5
6
7
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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?>?;

1
2
3
4
5
6
7
8
/// 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原生插件部分这里,比如当前走到获取可用摄像头的回调

1
2
3
4
5
6
...
[api availableCamerasWithCompletion:^(
NSArray<FCPPlatformCameraDescription *> *_Nullable output,
FlutterError *_Nullable error) {
callback(wrapResult(output, error));
}];

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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)
}

总结

17486664768352.jpg

  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与原生环境通信