iOS录屏直播(四)主App和宿主App数据共享,通信功能实现


Morris_
2019.06.17

上一篇总结了一下AppGroup相关的基础知识,通过AppGroup组即可实现App之间共享同一块存储区,以达到数据共享的目的。

在录屏直播中,我们采用AppGroup这种方式可以实现宿主App和Extention的数据传递。在App中写入数据,然后在Extention中读取数据,反之也可以。

这里主要总结一下跨进程在两个App中事件传递,从Extention发送通知到宿主App。

CFNotificationCenterGetDarwinNotifyCenter

平时开发中发送通知我们使用NSNotificationCenter这个单例类,这个类仅限在一个App内部发送通知,如果跨进程发通知的话就不能使用它了。

CFNotificationCenterGetDarwinNotifyCenter,是CoreFundation中的一个类,它可以实现跨进程发送通知,将通知从Extention App发送到宿主App中。

如果我们的App要实现录屏直播功能,需要添加Broadcast Upload Extension,这子App是负责采集、传输数据的,创建后会自动生成一个类SampleHandler。系统将采集到的录屏数据,通过SampleHandler中的Api输出,同时有一些其他Api:

- (void)broadcastStartedWithSetupInfo:(NSDictionary<NSString *,NSObject *> *)setupInfo {
    [self sendNotificationForMessageWithIdentifier:@"broadcastStartedWithSetupInfo" userInfo:setupInfo];
}

- (void)broadcastPaused {
    [self sendNotificationForMessageWithIdentifier:@"broadcastPaused" userInfo:nil];
}

- (void)broadcastResumed {
    [self sendNotificationForMessageWithIdentifier:@"broadcastResumed" userInfo:nil];
}

- (void)broadcastFinished {
    [self sendNotificationForMessageWithIdentifier:@"broadcastFinished" userInfo:nil];
}

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType {
    
}

打开手机桌面,然后长按录屏,选择录屏的App为我们自己的App,这时候就开始录制了。以上的接口就会被执行。开始录屏执行broadcastStartedWithSetupInfo,暂停录制执行broadcastPaused…

如果我们要将开始、暂停、结束这些事件以消息的形式发送到宿主App中,需要使用CFNotificationCenterGetDarwinNotifyCenter。

发送通知

写一个发送通知的方法:

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

这里的identifier是发送此条通知的标识,每个通知定义一个唯一的标识,以便接收端辨认是哪一条通知。

接收通知

在Extension中发送通知后,在宿主App中接收消息。

需要接收通知,首先需要注册该条通知,这里写了注册和移除注册通知的方法。这里的identifier需要和发送端保持一致。

- (void)registerForNotificationsWithIdentifier:(nullable NSString *)identifier {
    [self unregisterForNotificationsWithIdentifier:identifier];

    CFNotificationCenterRef const center = CFNotificationCenterGetDarwinNotifyCenter();
    CFStringRef str = (__bridge CFStringRef)identifier;
    CFNotificationCenterAddObserver(center,
                                    (__bridge const void *)(self),
                                    MyHoleNotificationCallback,
                                    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);
}

这里我们需要接收多个通知消息,需要一一注册才能收到消息:

注册对应事件的通知

- (void)addUploaderEventMonitor {
    NSLog(@"addUploaderEventMonitor");
    [self registerForNotificationsWithIdentifier:@"broadcastStartedWithSetupInfo"];
    [self registerForNotificationsWithIdentifier:@"broadcastPaused"];
    [self registerForNotificationsWithIdentifier:@"broadcastResumed"];
    [self registerForNotificationsWithIdentifier:@"broadcastFinished"];
    [self registerForNotificationsWithIdentifier:@"processSampleBuffer"];
    
    //这里同时注册了纷发消息的通知,在宿主App中使用
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(broadcastInfo:) name:ScreenHoleNotificationName object:nil];
}

移除注册的通知

- (void)removeUploaderEventMonitor {
    NSLog(@"removeUploaderEventMonitor");
    [self unregisterForNotificationsWithIdentifier:@"broadcastStartedWithSetupInfo"];
    [self unregisterForNotificationsWithIdentifier:@"broadcastPaused"];
    [self unregisterForNotificationsWithIdentifier:@"broadcastResumed"];
    [self unregisterForNotificationsWithIdentifier:@"broadcastFinished"];
    [self unregisterForNotificationsWithIdentifier:@"processSampleBuffer"];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:ScreenHoleNotificationName object:nil];
}

MyHoleNotificationCallback是一个回调block,当收到通知时,就会回调这个block。它的实现如下:

static NSString * const ScreenHoleNotificationName = @"ScreenHoleNotificationName";

void MyHoleNotificationCallback(CFNotificationCenterRef center,
                                   void * observer,
                                   CFStringRef name,
                                   void const * object,
                                   CFDictionaryRef userInfo) {
    NSString *identifier = (__bridge NSString *)name;
    NSObject *sender = (__bridge NSObject *)observer;
    //NSDictionary *info = (__bridge NSDictionary *)userInfo;
    NSDictionary *info = CFBridgingRelease(userInfo);
    
    NSLog(@"userInfo %@  %@",userInfo,info);

    NSDictionary *notiUserInfo = @{@"identifier":identifier};
    [[NSNotificationCenter defaultCenter] postNotificationName:ScreenHoleNotificationName
                                                        object:sender
                                                      userInfo:notiUserInfo];
}

这里收到通知后,做了一步转发,将消息转发出去,在在宿主App中发送通知。

在宿主App中收到MyHoleNotificationCallback这个回调后,我们将此条通知通过NSNotificationCenter纷发出去,App中其他地方如果需要监听这些事件就可以注册ScreenHoleNotificationName接收消息了。

如果其他地方不需要接收通知消息,则收到MyHoleNotificationCallback后直接处理就行,也不必再转发了。

这样事件传递,实现了宿主App和主App之间的事件通信。

- (void)broadcastInfo:(NSNotification *)noti {
    
    NSDictionary *userInfo = noti.userInfo;
    NSString *identifier = userInfo[@"identifier"];
    
    if ([identifier isEqualToString:@"broadcastStartedWithSetupInfo"]) {
        
    }
    if ([identifier isEqualToString:@"broadcastPaused"]) {
        
    }
    if ([identifier isEqualToString:@"broadcastResumed"]) {
        
    }
    if ([identifier isEqualToString:@"broadcastFinished"]) {
    
    }
    if ([identifier isEqualToString:@"processSampleBuffer"]) {
        
    }
}

这块只是在录屏直播中是否有用,得根据具体的业务需求来看,有时候不需要将事件传递给宿主App,也就不用发送通知了。

注意事项

不要在- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType这个方法里发通知,这几方法是采集到数据就会回调,录屏过程中一直被执行,如果一直发送通知的话,会造成主线程卡顿,因为通知是同步进行的。

遗留问题

在发送通知的时候,可以传递一个参数userInfo,即CFNotificationCenterPostNotification(center, identifierRef, NULL, userInfo, deliverImmediately);

这个userInfo的传值问题我没有解决,应该是可以转值的,可能是转化问题,不知道怎么弄,有知道的可以一起探讨一下,或者直接在品论了告知,谢谢先。

补充

这里是之后补充的内容

以下是别人的处理思路,出处

App 与 Extension 的代码共用iOS 10 新增了很多种 Extension,包括本文提到的两种 Broadcast Extension。主 App 与 Extension 属于不同的两个进程,代码逻辑也是分离的,而实际情况中,主 App 与 Extension 往往会包含相同的逻辑,需要共用代码。

主 App 与 Extension 属于两个不同的 target,共用代码,有以下几种方式:

一份代码创建两个副本,分别加到 App 和 Extension 两个 target 中。这种方法简单粗暴而有效,只是,如果需要改动逻辑,则需要改两份代码,想象一下,假如这种改动很频繁,世界上又有几个程序员能受得了?

抽离公共代码,放到独立的 framework,然后两个 target 都依赖该 framework,这是标准而方便的做法。
使用 CocoaPods,只需要在 Podfile 中分别写两个 target 所依赖的 pod 即可,最简洁。

我觉得 使用 CocoaPods,只需要在 Podfile 中分别写两个 target 所依赖的 pod 即可,最简洁。 这个也是一个可考虑的思路。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论
Win10操作系统中,你可以使用一些第三方工具来检测iOS应用程序是否具有防录屏功能。以下是一些常用的工具: 1. iTools:iTools是一款功能强大的iOS设备管理工具,它可以帮助你管理你的iOS设备并检测应用程序的防录屏功能。你可以下载并安装iTools,然后将iOS设备连接到计算机,并在iTools中打开应用程序,尝试录制屏幕,如果应用程序有防录屏功能,则录制的屏幕将是黑屏或显示“无法录制屏幕”的提示。 2. AirServer:AirServer是一款可以在Windows系统上运行的AirPlay接收器,它可以接收来自iOS设备的投屏信号,并将其显示在Windows系统上。你可以使用AirServer来尝试投屏并录制应用程序的屏幕,如果应用程序有防录屏功能,则录制的屏幕将是黑屏或显示“无法录制屏幕”的提示。 3. QuickTime Player:QuickTime Player是苹果官方的播放器,它可以用于录制iOS设备的屏幕。你可以将iOS设备连接到计算机,并在QuickTime Player中打开设备,尝试录制屏幕。如果应用程序有防录屏功能,则录制的屏幕将是黑屏或显示“无法录制屏幕”的提示。 需要注意的是,这些工具并不能保证100%检测应用程序是否具有防录屏功能,因为一些应用程序可能使用更高级的防录屏技术,可以绕过这些工具的检测。因此,如果你需要更加准确地检测应用程序的防录屏功能,可以使用逆向工程技术进行代码分析,或者使用专业的安全测试工具进行测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Morris_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值