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

环境

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.swfitapplication(_: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则返回,否则返回nilUIViewController* 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*)projectinitialRoute:(nullable NSString*)initialRoute {
...
engine = [[FlutterEngine alloc] initWithName:@"io.flutter"project:projectallowHeadlessExecution:self.engineAllowHeadlessExecutionrestorationEnabled:self.restorationIdentifier != nil];
...
_engine = engine;
...              - (instancetype)initWithEngine:(FlutterEngine*)enginenibName:(nullable NSString*)nibNamebundle:(nullable NSBundle*)nibBundle {
...
_engine = engine;          

FlutterPluginRegistry协议的继承结构

17485097331657.png

FlutterEngine对象调用registrarForPlugin:方法

源码: FlutterEngine.mm

/// 文件: FlutterEngine.mm
...
/*** All registrars returned from registrarForPlugin:*/
@property(nonatomic, readonly)NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
...- (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {/// <5> 检查可变字典中是否已存在插件名的keyid<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 assertionFLTDefaultCaptureDevice(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:binaryMessengercodec: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*)namebinaryMessenger:(NSObject<FlutterBinaryMessenger>*)messengercodec:(NSObject<FlutterMessageCodec>*)codectaskQueue:(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对象只有一个局部对象在持有,所以超过作用域会被回收,所以这里接收到codecNSObject<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:namebinaryMessageHandler:handlertaskQueue:taskQueue];} else {return [messenger setMessageHandlerOnChannel:name binaryMessageHandler:handler];}
}

进入到FlutterBinaryMessengerRelay对象的setMessageHandlerOnChannel:binaryMessageHandler:taskQueue:

文件: FlutterBinaryMessengerRelay.mm

- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channelbinaryMessageHandler:(FlutterBinaryMessageHandler)handlertaskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {/// parent就是engine对象,因此又回到engine上setMessageHandlerOnChannel:binaryMessageHandler:                                         taskQueue:if (self.parent) {return [self.parent setMessageHandlerOnChannel:channelbinaryMessageHandler:handlertaskQueue:taskQueue];} else {FML_LOG(WARNING) << "Communicating on a dead channel.";return -1;}
}

文件: FlutterEngine.mm

- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channelbinaryMessageHandler:(FlutterBinaryMessageHandler)handlertaskQueue:(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的计数加1return _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: cameraplatforms:ios:pluginClass: CameraPlugindartPluginClass: 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({ CameraApi? api}): _hostApi = api ?? CameraApi();...Future<List<CameraDescription>> availableCameras() async {try {return (await _hostApi.getAvailableCameras()).map(cameraDescriptionFromPlatform).toList();} on PlatformException catch (e) {throw CameraException(e.code, e.message);}}

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

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] inguard let strongSelf = self else { return }var discoveryDevices: [AVCaptureDevice.DeviceType] = [.builtInWideAngleCamera,.builtInTelephotoCamera,].../// 前置,后置等摄像头,然后统一添加到reply这个数组对象for device in devices {var lensFacing: FCPPlatformCameraLensDirectionswitch device.position {case .back:lensFacing = .backcase .front:lensFacing = .frontcase .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与原生环境通信

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/83562.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于 Amazon Q Developer CLI 和 Amazon Bedrock Knowledge Bases 实现智能问答系统

1. 引言 传统企业通常将常见问题&#xff08;FAQ&#xff09;发布在网站上&#xff0c;方便客户自助查找信息。然而&#xff0c;随着生成式 AI 技术的迅速发展与商业渗透&#xff0c;这些企业正积极探索构建智能问答系统的新途径。这类系统不仅能显著提升客户体验&#xff0c;…

Go 为何天生适合云原生?

当前我们正处在 AI 时代&#xff0c;但是在基础架构领域&#xff0c;仍然处在云原生时代。云原生仍然是当前时代的风口之一。作为一个 Go 开发者&#xff0c;职业进阶的下一站就是学习云原生技术。作为 Go 开发者学习云原生技术有得天独厚的优势&#xff0c;这是因为 Go 天生适…

Mac查看MySQL版本的命令

通过 Homebrew 查看&#xff08;如果是用 Homebrew 安装的&#xff09; brew info mysql 会显示你安装的版本、路径等信息。 你的终端输出显示&#xff1a;你并没有安装 MySQL&#xff0c;只是查询了 brew 中的 MySQL 安装信息。我们一起来看下重点&#xff1a; &#x1f9fe…

Kafka ACK机制详解:数据可靠性与性能的权衡之道

在分布式消息系统中&#xff0c;消息确认机制是保障数据可靠性的关键。Apache Kafka 通过 ACK&#xff08;Acknowledgment&#xff09;机制 实现了灵活的数据确认策略&#xff0c;允许用户在 数据可靠性 和 系统性能 之间进行权衡。本文将深入解析 Kafka ACK 机制的工作原理、配…

FastMCP:构建 MCP 服务器和客户端的高效 Python 框架

在人工智能领域&#xff0c;模型上下文协议&#xff08;Model Context Protocol&#xff0c;简称 MCP&#xff09;作为一种标准化的协议&#xff0c;为大型语言模型&#xff08;LLM&#xff09;提供了丰富的上下文和工具支持。而 FastMCP 作为构建 MCP 服务器和客户端的 Python…

动态库导出符号与extern “C“

1. windows下动态库导出符号 根据C/C语法规则&#xff0c;函数声明中的修饰符&#xff08;如__declspec(dllexport)&#xff09;可以放在返回类型之前或返回类型之后、函数名之前。这两种方式在功能上是等价的&#xff0c;编译器会以相同的方式处理。 __declspec(dllexport) …

Linux(9)——进程(控制篇——下)

目录 三、进程等待 1&#xff09;进程等待的必要性 2&#xff09;获取子进程的status 3&#xff09;进程的等待方法 wait方法 waitpid方法 多进程创建以及等待的代码模型 非阻塞的轮训检测 四、进程程序替换 1&#xff09;替换原理 2&#xff09;替换函数 3&…

Datatable和实体集合互转

1.使用已废弃的 JavaScriptSerializer&#xff0c;且反序列化为弱类型 ArrayList。可用但不推荐。 using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Reflection; using System.Web; using Sy…

阿里云服务器ECS详解:云服务器是什么,云服务器优势和应用场景及参考

云服务器ECS是阿里云众多云产品中&#xff0c;最受用户关注的产品&#xff0c;阿里云服务器提供多样化的计算能力&#xff0c;支持x86、Arm架构&#xff0c;涵盖CPU、GPU等多种服务器类型&#xff0c;满足各种用户需求。其便捷易用特性包括分钟级交付、通用API和性能监控框架&a…

【Oracle】游标

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 游标基础概述1.1 游标的概念与作用1.2 游标的生命周期1.3 游标的分类 2. 显式游标2.1 显式游标的基本语法2.1.1 声明游标2.1.2 带参数的游标 2.2 游标的基本操作2.2.1 完整的游标操作示例 2.3 游标属性2.3.1…

pikachu靶场通关笔记11 XSS关卡07-XSS之关键字过滤绕过(三种方法渗透)

目录 一、源码分析 1、进入靶场 2、代码审计 3、攻击思路 二、渗透实战 1、探测过滤信息 2、注入Payload1 3、注入Payload2 4、注入Payload3 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关&#xff09;渗透集合&#xff0c;通过对XSS关卡源码的代码审计找到安…

XML 元素:基础、应用与优化

XML 元素:基础、应用与优化 引言 XML(可扩展标记语言)作为一种数据交换的标准格式,广泛应用于互联网数据交换、数据存储等领域。XML 元素是 XML 文档的核心组成部分,本文将深入探讨 XML 元素的概念、特性、应用以及优化方法。 一、XML 元素概述 1.1 XML 元素的定义 X…

【Axure高保真原型】交通事故大屏可视化分析案例

今天和大家分享交通事故大屏可视化分析案例的原型模板&#xff0c;包括饼图分类分析、动态显示发生数、柱状图趋势分析、中部地图展示最新事故发现地点和其他信息、右侧列表记录发生事故的信息…… 通过多种可视化图表展示分析结果&#xff0c;具体效果可以点击下方视频观看或…

HCIP(BGP基础)

一、BGP 基础概念 1. 网络分类与协议定位 IGP&#xff08;内部网关协议&#xff09;&#xff1a;用于自治系统&#xff08;AS&#xff09;内部路由&#xff0c;如 RIP、OSPF、EIGRP&#xff0c;关注选路效率、收敛速度和资源占用。EGP&#xff08;外部网关协议&#xff09;&a…

【HarmonyOS 5】 ArkUI-X开发中的常见问题及解决方案

一、跨平台编译与适配问题 1. 平台特定API不兼容 ‌问题现象‌&#xff1a;使用Router模块的replaceUrl或startAbility等鸿蒙专属API时&#xff0c;编译跨平台工程报错cant support crossplatform application。 ‌解决方案‌&#xff1a; 改用ohos.router的跨平台封装API&a…

Matlab2018a---安装教程

目录 壹 | 引 言 贰 | 安装环境 叁 | 安 装 肆 | 结 语 壹 | 引 言 大家好&#xff0c;我是子正。 最近想学习一下DSP数字信号处理有关的知识&#xff0c;要用到Matlab进行数据处理&#xff0c;于是又重新把Matlab捡了回来; 记得上学那会儿用的还是Matlab2012a&#xff…

分布式流处理与消息传递——Kafka ISR(In-Sync Replicas)算法深度解析

Java Kafka ISR&#xff08;In-Sync Replicas&#xff09;算法深度解析 一、ISR核心原理 #mermaid-svg-OQtnaUGNQ9PMgbW0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-OQtnaUGNQ9PMgbW0 .error-icon{fill:#55222…

ARM GIC V3概述

中断类型 locality- specific peripheral interrupt&#xff08;LPI&#xff09;&#xff1a;LPI是一个有针对性的外设中断&#xff0c;通过affinity路由到特定的PE。 为非安全group1中断边沿触发可以通过its进行路由没有active状态&#xff0c;所以不需要明确的停用操作LPI总…

蓝桥杯国赛训练 day1

目录 k倍区间 舞狮 交换瓶子 k倍区间 取模后算组合数就行 import java.util.HashMap; import java.util.Map; import java.util.Scanner;public class Main {static Scanner sc new Scanner(System.in);public static void main(String[] args) {solve();}public static vo…

安装和配置 Nginx 和 Mysql —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录6

前言 昨天更新了四篇博客&#xff0c;我们顺利的 安装了 ubuntu server 服务器&#xff0c;并且配置好了 ssh 免密登录服务器&#xff0c;安装好了 服务器常用软件安装, 配置好了 zsh 和 vim 以及 通过 NVM 安装好Nodejs&#xff0c;还有PNPM包管理工具 。 作为服务器的运行…