在iOS和OS X中获取系统范围通知的状态
我正在尝试写一个代码来处理iOS上的开/关屏幕(你可以看看类似的问题讨论这个问题)。 我为这个问题提供了OSX标签,因为OSX具有相同的系统范围通知function。 下面描述的问题是inheritance到通知设施(与iOS或OSX)。
有一个众所周知的方法来注册系统范围的通知com.apple.springboard.hasBlankedScreen在屏幕closures或打开时接收通知。
仅供参考(以下是用于注册系统范围通知的API):
- notify_post,notify_check_ notify_get_state和朋友
- CFNotificationCenterPostNotification,CFNotificationCenterAddObserver和朋友 (内部使用notify_post等)
但是,这种方法存在两个相互关联的问题:
- 对于两个屏幕closures和通知的通知具有相同的名称(com.apple.springboard.hasBlankedScreen)
- 观察者没有收到状态作为通知的一部分。
因此,我们需要实现一些不同的屏幕打开和closures的解决scheme(因为相同的通知callback将被调用,并且没有参数将具有状态)。
一般来说,国家与通知callback脱钩的核心问题。 我看不出如何处理这个优雅。
我提出了两个简单的方法(每个都是有缺陷的)。 寻找另一种方法或改进这种方法的想法。
计数解决scheme
我们可以实施一个计数器来统计我们已经收到了多less通知,根据这些信息我们将知道是否通知打开或closures屏幕(基于我们的柜台的奇怪)。
但是,它有两个缺点:
1)在这种情况下,如果系统(由于devise时间的原因未知)将发送具有相同名称的附加通知,我们的逻辑将被拧紧,因为它会打破奇怪的检查。
2)另外,我们需要正确设置初始状态。 所以在代码中的某处我们会有这样的东西:
counter = getInitialState(); registerForNotification();
在这种情况下,我们有一个竞争条件。 如果系统会在getInitialState()之后发送通知并改变状态,但是在registerForNotification()之前,我们将以错误的计数器值结束。
如果我们会做以下代码:
registerForNotification(); counter = getInitialState();
在这种情况下,我们有另一种竞争条件。 如果系统发送通知并在我们做了registerForNotification()之后,但在getInitialState()之前改变状态,我们将得到一个计数器,将进入通知callback,并增加一个计数器(这会使其错误)。
确定通知收到解决scheme时的状态
在这种情况下,我们不存储任何计数器,而是在通知callback中使用API notify_get_state来获取当前状态。
这有它自己的问题:
1)asynchronous传送到应用程序的通知。 所以,如果closures屏幕的速度非常快,您可以在屏幕已经打开的情况下收到两个通知。 所以,notify_check会得到一个当前的状态(相对于发送通知时的状态)。
因此,当应用程序将在通知callback中使用notify_get_state时,将确定有两个通知“屏幕已打开”,而不是一个通知“屏幕已closures”,另一个“屏幕已打开”。
PS一般来说,所有描述的问题都不是特定于屏幕开/关的情况。 它们对于具有独特状态并以相同的通知名称发送的任何系统范围的通知都是实际的。
更新1
我没有准确地testing开启/closures屏幕的情况,并且获得了notify_get_state()的相同结果。
然而,当我收到两个通知com.apple.springboard.lockstate (通过CFNotificationCenterAddObserver订阅)时,我有类似的情况,我使用另一个API来获取当前的设备locking状态,并收到两个通知相同的值。
所以这只是我的假设,notify_get_state也会返回相同的值。 不过,我认为这是受过教育的猜测。 notify_get_state的input参数对于两个调用是相同的(它不会改变)。 我不认为系统存储应该由notify_get_state返回的状态的FIFO队列。
所以,我build立了一个非常简单的实验。 我在debugging器之外的越狱的iOS 6.1 iPhone 5上运行这个。
码
我使用下面的代码构build了一个消费者应用程序:
#define EVENT "com.mycompany.bs" - (void)registerForNotifications { int result = notify_register_dispatch(EVENT, ¬ifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0l), ^(int info) { uint64_t state; notify_get_state(notifyToken, &state); NSLog(@"notify_register_dispatch() : %d", (int)state); }); if (result != NOTIFY_STATUS_OK) { NSLog(@"register failure = %d", result); } CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center NULL, // observer notifyCallback, // callback CFSTR(EVENT), // event name NULL, // object CFNotificationSuspensionBehaviorDeliverImmediately); } static void notifyCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { uint64_t state; notify_get_state(notifyToken, &state); NSLog(@"notifyCallback(): %d", (int)state); }
所以,如您所见,它使用两种不同的方法来注册相同的自定义事件。 我启动这个应用程序,让它注册的事件,然后把它放到后台(主页按下button)。
然后,生产者的应用程序,它让我生成一个button按下事件:
double delayInSeconds = 0.001; dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0l); dispatch_async(q, ^(void) { notify_set_state(notifyToken, 2); notify_post(EVENT); }); dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, q, ^(void){ notify_set_state(notifyToken, 3); notify_post(EVENT); });
结果
然后,我运行生产者应用程序,每两秒钟手动生成一对事件。 正如你所看到的,制片人很快将状态为2
的事件发布,然后立即发布另一个状态为3
事件。 因此,消费者应该打印2
3
, 两种callback方法,如果这是完美的工作。 它不(如你所害):
Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:12 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:18 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:22 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 2 Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:26 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:30 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:33 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3 Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 2 Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notify_register_dispatch() : 3 Feb 13 21:46:36 iPhone5 MyApp[1971] <Warning>: notifyCallback(): 3
我试着改变一个消费者注册方法来使用CFNotificationSuspensionBehaviorCoalesce
(而不是立即交付)。 结果:
Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:17 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:20 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2 Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:24 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:29 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2 Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:32 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2 Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:35 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 2 Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:38 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3 Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 2 Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notify_register_dispatch() : 3 Feb 13 21:48:39 iPhone5 MyApp[1990] <Warning>: notifyCallback(): 3
然后,我尝试将notify_register_dispatch()
消费者的队列优先级改为高 ,而不是后台优先级。 结果:
Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:51 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:49:53 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:55 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:49:59 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:01 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:04 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:06 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 2 Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:09 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3 Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 2 Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notify_register_dispatch() : 3 Feb 13 21:50:10 iPhone5 MyApp[2006] <Warning>: notifyCallback(): 3
结论(?)
- 您怀疑存在问题,而不仅仅是
SBGetScreenLockStatus
调用。 有时,消费者从来没有看到国家设置为2
。 - 如果我把制作者的时间延迟增加到5毫秒,我从来没有看到这个问题。 所以,这可能只是事件真正接近时间的一个问题。 屏幕locking/解锁可能不是什么大不了的事情。 显然,较慢的电话(iPhone <5)会有不同的响应。
- 静态
notifyCallback()
方法似乎首先被callback,除非GCDcallback块放在高优先级队列中。 即使如此,通常静态callback函数首先被调用。 很多时候,第一种方法得到了正确的状态(2
),而第二种方法没有。 所以,如果你不得不忍受这个问题,你可以selectcallback机制,似乎performance最好(或者至less,你自己的原型,在你的应用程序内)。 - 我不能说
suspensionBehavior
参数有很大的不同。 也就是说,根据iOS 发布事件的方式,他们可能会使用类似CFNotificationCenterPostNotification的调用,可能会忽略消费者的行为请求。 -
如果你看这个苹果文档 ,你会看到两件事。
- 首先,
notify_set_state
不是最原始的API的一部分 - 其次,该文件的第一段说
- 首先,
达尔文通知API参考
这些例程允许进程交换无状态的通知事件。
所以,也许我们只是试图做一些不符合原来的devise:(
- 如果您也看一下Apple的NotificationPoster示例 ,则会看到它们不使用
notify_get_state
和notify_set_state
来传递状态。 他们将这个通知作为用户信息字典传递给它。 很显然,如果你正在观察苹果的iOS事件,你无法控制事件的发布方式。 但是,在你创build生产者和消费者的应用程序中,我会远离notify_set_state
。