当前位置: 首页 > news >正文

iOS中宿主APP与录屏扩展进程数据传递方式

背景

在iOS生态系统中,应用程序的功能不再局限于单一的宿主应用,而是可以通过扩展进程实现更丰富的用户体验和功能。其中一种引人注目的扩展是录屏功能,它使用户能够捕捉设备屏幕上的活动,无论是游戏过程、教育演示还是其他应用场景。

然而,实现这一功能并非只涉及宿主应用的单一努力,更需要与扩展进程之间的高效数据传递。这种数据传递是确保录屏功能顺利运作的关键,也是开发者需要深入了解和灵活应用的技术之一。

在本篇博客中,我们将深入探讨iOS宿主APP与扩展进程之间的数据传递方式,了解它们如何协同工作,以实现无缝的录屏体验。

实现方案

方案一:通知

关于Fondation框架中的NSNotificationCenter应该都不陌生,但这里要使用的是Core Fondation框架中的CFNotificationCenterRef,是NSNotificationCenter更底层的实现。使用CFNotificationCenterRef进行数据传递的前提是,主应用和扩展应用需要设置相同的APP Group.

另外需要注意的是,通知只适合传递少量数据,比如一些重连,断开,或者其它少量信息的情况,大量数据的传递不适用,和我们使用NSNotificationCenter一样,应该也没有人使用它来连续不断地传递数据。

以主应用监听扩展应用为例,主应用中需要有下面三个需要实现方法:

  • 声明回调block
static NSString * const notificationName = @"notificationName";
void notificationCallback(CFNotificationCenterRef center,void * observer,CFStringRef name,void const * object,CFDictionaryRef userInfo) {NSString *identifier = (__bridge NSString *)name;NSObject *sender = (__bridge NSObject *)observer;NSDictionary *notiUserInfo = @{@"identifier":identifier};//为了简化处理过程,使用常规通知承接一下。[[NSNotificationCenter defaultCenter] postNotificationName:notificationNameobject:senderuserInfo:notiUserInfo];
}
  • 注册通知
//参数1:通知中心
//参数2:接收监听的对象。
//参数3:监听的回调
//参数4:通知给观察者的名称
//参数5:对于非Darwin通知中心,要观察的对象。
//参数6:决定应用程序在后台时如何处理通知。- (void)registerNotificationsWithIdentifier:(nullable NSString *)identifier {[self unregisterNotificationsWithIdentifier:identifier];CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();CFStringRef str = (__bridge CFStringRef)identifier;CFNotificationCenterAddObserver(center,(__bridge const void *)(self),notificationCallback,str,NULL,CFNotificationSuspensionBehaviorDeliverImmediately);
}
  • 注销通知
- (void)unregisterForNotificationsWithIdentifier:(nullable NSString *)identifier {CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();CFStringRef str = (__bridge CFStringRef)identifier;CFNotificationCenterRemoveObserver(center,(__bridge const void *)(self),str,NULL);
}

而在使用过程中和NSNotificationCenter十分相似:

  • 开始注册监听
//MARK: 注册监听插件消息通知
- (void)addUploaderEventMonitor {[self registerForNotificationsWithIdentifier:@"setupSocket"];//这里同时注册了纷发消息的通知,在宿主App中使用[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(broadcastInfo:) name:notificationName object:nil];
}
  • 实现监听到通知的回调方法
- (void)broadcastInfo:(NSNotification *)noti {NSDictionary *userInfo = noti.userInfo;NSString *identifier = userInfo[@"identifier"];if ([identifier isEqualToString:@"setupSocket"]) {}
}
  • dealloc方法中移除监听
- (void)removeUploaderEventMonitor {[self unregisterForNotificationsWithIdentifier:@"setupSocket"];[[NSNotificationCenter defaultCenter] removeObserver:self name:ScreenHoleNotificationName object:nil];
}- (void)dealloc{[self removeUploaderEventMonitor];
}

而扩展进程只需要负责在合适的时候发送通知:

//MARK: 发送通知
- (void)sendNotificationForMessageWithIdentifier:(nullable NSString *)identifier userInfo:(NSDictionary *)info {CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();CFDictionaryRef userInfo = (__bridge CFDictionaryRef)info;BOOL const deliverImmediately = YES;CFStringRef identifierRef = (__bridge CFStringRef)identifier;CFNotificationCenterPostNotification(center, identifierRef, NULL, userInfo, deliverImmediately);
}- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {// User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional.[self sendNotificationForMessageWithIdentifier:@"setupSocket" userInfo:@{}];
}

我们可以把数据放到userInfo中传递给主应用。

方案二:KVO和KVC

iOS 7引入了应用群组功能允许创建一个共享沙盒,应用和应用扩展都可以访问它。还可以支持多个应用之间共享数据,但前提是应用必须使用相同的证书进行签名。

该方案同样也离不开应用群组。同时需要借助NSUserDefauls及KVO和KVC。

仍然是以扩展进程向主应用传递数据为例,主应用需要实现以下几个步骤:

  • 创建NSUserDefaults对象,并进行监听(注意该对象需要显式的强引用)。
self.userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.m10v.hperf"];
[self.userDefaulst addObserver:self forKeyPath:@"didReceiveVideo" options:NSKeyValueObservingOptionNew context:nil];
  • 实现监听的回调
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"didReceiveVideo"]){NSData *data = [userDefaulst objectForKey:@"didReceiveVideo”];} 
}

而对于扩展进程同样也只需要负责发送数据:

  • 创建NSUserDefaults对象,并使用KVC给didReceiveVideo进行赋值。
NSUserDefaults * userDefaulst = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.m10v.hperf"]; 
NSData *videoData = [NSData new]; 
[userDefaulst setObject:videoData forKey:@"didReceiveVideo"];

方案三:使用NSFileManager

该方案也需要使用到应用群组,核心思想还是通过应用群组创建共享容器来帮我祝我们在两个进程间共享数据。

以扩展进程向主APP传递数据为例,与上面方案的不同之处在于,此方案的侧重点在扩展进程。

扩展进程:

  • 创建NSFileManager将数据写入文件
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"your.app.group.identifier"];
NSURL *dataURL = [containerURL URLByAppendingPathComponent:@"recordedData.bin"];// 将 CMSampleBuffer 数据写入文件
CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
NSData *data = [NSData dataWithContentsOfURL:dataURL];
[data writeToURL:dataURL atomically:YES];

主应用:

  • 读取共享文件中的内容
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"your.app.group.identifier"];
NSURL *dataURL = [containerURL URLByAppendingPathComponent:@"recordedData.bin"];// 从文件读取 CMSampleBuffer 数据
NSData *data = [NSData dataWithContentsOfURL:dataURL];

方案四:使用Socket

使用这种方式进行数据传递比较灵活,但实现起来也比较繁琐,首先需要两个进程之间建立好Socket链接,之后才能进行数据传递的操作。以GCDAysncSocket为例。

以扩展进程发送数据到主应用为例

主应用:

  • 属性声明
#import "GCDAsyncSocket.h"
@interface ViewController ()<GCDAsyncSocketDelegate>
@property (nonatomic, strong) GCDAsyncSocket * socket;
@property (nonatomic, strong) NSMutableArray * socketArrayM;
//消息接口队列(串行)
@property (nonatomic, strong) dispatch_queue_t queue;
@end
  • 数据初始化,创建队列,初始化socket
- (void)viewDidLoad {[super viewDidLoad];[self initData];[self setupSocket];
}- (void)initData{self.socketArrayM = [NSMutableArray array];self.queue = dispatch_queue_create("com.joyme.panghu.socket", DISPATCH_QUEUE_SERIAL);
}//MARK: 初始化socket
- (void)setupSocket{self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.queue];self.socket.IPv6Enabled = NO;NSError * error;//设置端口号[self.socket acceptOnPort:8999 error:&error];//读取数据[self.socket readDataWithTimeout:-1 tag:0];}
  • 实现Socket的代理方法
//MARK: GCDAsyncSocketDelegate//链接断开
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{[self.socketArrayM removeObject:sock];
}//读取流通道关闭
- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock{[self.socketArrayM removeObject:sock];
}//接收到新的链接
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{[self.socketArrayM addObject:newSocket];[newSocket readDataWithTimeout:-1 tag:0];
}//读取到数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{NSLog(@"收到:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}

扩展进程:

扩展进程同样也需要进行类似的步骤,来创建Socket以进行连接。

  • 属性声明
#import "SampleHandler.h"
#import "GCDAsyncSocket.h"@interface SampleHandler ()<GCDAsyncSocketDelegate>@property (nonatomic, strong) GCDAsyncSocket * socket;
///消息接收队列
@property (nonatomic, strong) dispatch_queue_t queue;@end
  • 初始化数据
(void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {[self initData];[self setupSocket];
}- (void)initData{self.queue = dispatch_queue_create("com.joyme.panghu.socket_2", DISPATCH_QUEUE_SERIAL);
}- (void)setupSocket{self.socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:self.queue];NSError * error;[self.socket connectToHost:@"127.0.0.1" onPort:8999 error:&error];[self.socket readDataWithTimeout:-1 tag:0];
}
  • Socket代理方法
//MARK: GCDAsyncSocketDelegate
//链接成功
- (void)socket:(GCDAsyncSocket *)sock didConnectToUrl:(NSURL *)url{}- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{[self.socket readDataWithTimeout:-1 tag:0];[self sendReadData];
}//写入数据成功
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{}//读取到数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{}- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{}
  • 发送数据
//MARK: 发送数据
- (void)sendReadData{NSString * ready = @"ready";NSData * data = [ready dataUsingEncoding:NSUTF8StringEncoding];[self.socket writeData:data withTimeout:-1 tag:0];
}

建立连接成功之后,我们会在APP接收到扩展进程传递来的数据

2023-02-10 11:49:50.143508+0800 RecordDemo[7575:1395544] 收到:ready

使用Socket与上面的方法不同之处,首先不需要创建应用群组;数据传递是双向的,建立好链接后,主APP可以向扩展进程发送数据,扩展进程也可以向宿主APP发送数据。

但上面的只是一个基础的数据传递方案,想要相对完整的建立链接流程,我们还需要考虑到端口冲突的问题。实现大量数据的传递还需要其它工具来实现比如(NTESSocketPacket,数据打包,NTESTPCircularBuffer数据解包)。

结语

当涉及主APP与扩展进程之间的数据传递方案时,我们面临着多种选择,并没有一种绝对优越的方案,只有最适合特定场景的方案。在实践中,我们可能需要结合多个方案以满足不同需求。这种灵活性使得我们能够根据具体情境选择最合适的方式,从而更好地满足项目的要求。在选择方案时,务必考虑到实际需求和性能优化,以确保系统的稳定性和效率。综上所述,灵活运用不同的数据传递方案,是构建强大应用的重要一环。

相关文章:

iOS中宿主APP与录屏扩展进程数据传递方式

背景 在iOS生态系统中&#xff0c;应用程序的功能不再局限于单一的宿主应用&#xff0c;而是可以通过扩展进程实现更丰富的用户体验和功能。其中一种引人注目的扩展是录屏功能&#xff0c;它使用户能够捕捉设备屏幕上的活动&#xff0c;无论是游戏过程、教育演示还是其他应用场…...

Windows系统下的可用RADIUS软件-[资源]

RADIUS协议相关原理介绍&#xff0c;可参考博客RADIUS协议原理介绍报文分析配置指导-RFC2865/RFC2866。 本文用于提供和介绍Window系统下几种可用的RADIUS软件。主要涉及软件有radius_ping&#xff08;绿色免安装版&#xff09;和WinRadius&#xff08;绿色免安装版&#xff09…...

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十五:基础数据模块相关功能实现

一、本章内容 本章使用已实现的公共组件实现系统管理中的基础数据中的验证码管理、消息管理等功能。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址: 基于VUE3+Layui从头搭建通用后台管理系统合集-验证码功能实现 3.2 西瓜…...

MAC苹果笔记本电脑如何彻底清理垃圾文件软件?

苹果电脑以其流畅的操作系统和卓越的性能而备受用户喜爱。然而&#xff0c;随着时间的推移&#xff0c;系统可能会积累大量垃圾文件&#xff0c;影响性能。本文将介绍苹果电脑怎么清理垃圾文件的各种方法&#xff0c;以提升系统运行效率。 CleanMyMac X是一款专业的Mac清理软件…...

【Linux C | 文件I/O】文件的打开关闭 | open、creat、colse 函数

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…...

【BEV感知】BEVFormer 融合多视角图形的空间特征和时序特征 ECCV 2022

前言 本文分享BEV感知方案中&#xff0c;具有代表性的方法&#xff1a;BEVFormer。 它基于Deformable Attention&#xff0c;实现了一种融合多视角相机空间特征和时序特征的端到端框架&#xff0c;适用于多种自动驾驶感知任务。 主要由3个关键模块组成&#xff1a; BEV Que…...

Amazon Toolkit — CodeWhisperer 使用

tFragment--> 官网&#xff1a;https://aws.amazon.com/cn/codewhisperer/?trkcndc-detail 最近学习了亚马逊云科技的 代码工具&#xff0c;感慨颇多。下面是安装 和使用的分享。 CodeWhisperer&#xff0c;亚马逊推出的实时 AI 编程助手&#xff0c;是一项基于机器学习…...

Flink SQL填坑记2:Flink和MySQL的Bigdata类型不同导致ClassCastException报错

最近在开发Flink SQL的时候,需要关联Kafka事实表和MySQL维表,得到的数据写入Phoenix表中,但是其中有个字段,Kafka表、MySQL表和Phoenix表都是BigData类型,但是在实现的时候却报“java.math.BigInteger cannot be cast to java.lang.Long”异常,从报错信息来看,是由于Big…...

本地MinIO存储服务如何创建Buckets并实现公网访问上传文件

文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 前言 MinIO是一款高性能、分布式的对象存储系统&#xff0c;它可以100%的运行在标准硬件上&#xff0c;即X86等…...

通过https协议访问Tomcat部署并使用Shiro认证的应用跳转登到录页时协议变为http的问题

问题描述&#xff1a; 在最近的一个项目中&#xff0c;有一个存在较久&#xff0c;并且只在内部城域网可访问的一个使用Shiro框架进行安全管理的Java应用&#xff0c;该应用部署在Tomcat服务器上。起初&#xff0c;应用程序可以通过HTTP协议访问&#xff0c;一切运行都没…...

Backend - Django 项目创建 运行

目录 一、配置环境 二、创建 Django 项目 &#xff08;一&#xff09;新建文件夹 &#xff08;二&#xff09;打开文件夹 &#xff08;三&#xff09;打开运行终端 &#xff08;四&#xff09;创建基础项目 &#xff08;五&#xff09;创建app 1. 安装Django &#xf…...

C# .Net学习笔记—— Expression 表达式目录树

一、什么是表达式目录树 &#xff08;1&#xff09;Expression我们称为是表达式树&#xff0c;是一种数据结构体&#xff0c;用于存储需要计算&#xff0c;运算的一种结构&#xff0c;这种结构可以只是存储&#xff0c;而不进行运算。通常表达式目录树是配合Lambda一起来使用的…...

《论文阅读28》Unsupervised 3D Shape Completion through GAN Inversion

GAN&#xff0c;全称GenerativeAdversarialNetworks&#xff0c;中文叫生成式对抗网络。顾名思义GAN分为两个模块&#xff0c;生成网络以及判别网络&#xff0c;其中 生成网络负责根据随机向量产生图片、语音等内容&#xff0c;产生的内容是数据集中没有见过的&#xff0c;也可…...

一个正则快速找到在ES中使用profile的时产生慢查询的分片

在es中使用profile分析慢查询的时候&#xff0c;往往因为分片过多&#xff0c;或者因为查询条件太复杂&#xff0c;分析的结果几十万行。在kibana上点半天&#xff0c;也找不到一个耗时长的分片。 kibana上可以通过正则来匹配。其实我们只需要匹配到耗时大于10秒的请求。 检索语…...

链接未来:深入理解链表数据结构(一.c语言实现无头单向非循环链表)

在上一篇文章中&#xff0c;我们探索了顺序表这一基础的数据结构&#xff0c;它提供了一种有序存储数据的方法&#xff0c;使得数据的访 问和操作变得更加高效。想要进一步了解&#xff0c;大家可以移步于上一篇文章&#xff1a;探索顺序表&#xff1a;数据结构中的秩序之美 今…...

Python tkinter控件全集之组合选择框 ttk.ComboBox

Tkinter标准库 Tkinter是Python的标准GUI库&#xff0c;也是最常用的Python GUI库之一&#xff0c;提供了丰富的组件和功能&#xff0c;包括窗口、按钮、标签、文本框、列表框、滚动条、画布、菜单等&#xff0c;方便开发者进行图形界面的开发。Tkinter库基于Tk for Unix/Wind…...

Axure之中继器的使用(交互动作reperter属性Item属性)

目录 一.中继器的基本使用 二.中继器的动作&#xff08;增删改查&#xff09; 2.1 新增 2.2 删除 2.3 更新行 2.4 效果展示 2.5 模糊查询 三.reperter属性 在Axure中&#xff0c;中继器&#xff08;Repeater&#xff09;是一种功能强大的组件&#xff0c;用于创建重复…...

数字化医疗新篇章:构建智能医保支付购药系统

在迎接数字化医疗时代的挑战和机遇中&#xff0c;智能医保支付购药系统的建设显得尤为重要。本文将深入介绍如何通过先进的技术实现&#xff0c;构建一套智能、高效的医保支付购药系统&#xff0c;为全面建设健康中国贡献力量。 1. 引言 随着医疗科技的飞速发展&#xff0c;…...

11_12-Golang中的运算符

**Golang **中的运算符 主讲教师&#xff1a;&#xff08;大地&#xff09; 合作网站&#xff1a;www.itying.com** **&#xff08;IT 营&#xff09; 我的专栏&#xff1a;https://www.itying.com/category-79-b0.html 1、Golang 内置的运算符 算术运算符关系运算符逻辑运…...

k8s-ingress特性 9

TLS加密 创建证书 测试访问 auth认证 创建认证文件 rewrite重定向 进入域名时&#xff0c;会自动重定向到hostname.html 示例&#xff1a; 测试 版本的升级迭代&#xff0c;之前利用控制器进行滚动更新&#xff0c;在升级过程中无法做到快速回滚 更加平滑的升级&#xff1…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...