从iOS服务中检测屏幕开/关

11

我正在开发一款在后台作为服务运行的网络监测应用。当手机屏幕开启或关闭时,是否可以通过通知或调用方式得到相应的提醒呢?

在Android中,可以使用以下代码实现:

private void registerScreenOnOffReceiver()
{
   IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
   filter.addAction(Intent.ACTION_SCREEN_OFF);
   registerReceiver(screenOnOffReceiver, filter);
}

当屏幕开/关时,screenOnOffReceiver会被调用。 iOS有类似的解决方案吗?

编辑:到目前为止,我找到的最佳解决方案是UIApplicationProtectedDataWillBecomeUnavailable (Detect if iPhone screen is on/off) 但需要用户在设备上启用数据保护(密码保护)。


是的,但你将无法在后台监控网络...苹果不允许这样做。 - borrrden
我知道。但是这个应用程序不是为了App Store,所以这不是问题。 - Sunkas
3个回答

16
你可以使用Darwin notifications来监听事件。我不确定,但在越狱的iOS 5.0.1 iPhone 4上运行时,其中一个事件可能是你需要的:
com.apple.iokit.hid.displayStatus
com.apple.springboard.hasBlankedScreen
com.apple.springboard.lockstate

更新:此外,当手机锁定时(但不是解锁时),以下通知也会发布:

com.apple.springboard.lockcomplete

要使用此功能,请像这样注册活动(这仅为一个活动注册,如果不适用于您,请尝试其他活动):
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), //center
                                NULL, // observer
                                displayStatusChanged, // callback
                                CFSTR("com.apple.iokit.hid.displayStatus"), // event name
                                NULL, // object
                                CFNotificationSuspensionBehaviorDeliverImmediately);

其中displayStatusChanged是您的事件回调函数:

static void displayStatusChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
    NSLog(@"event received!");
    // you might try inspecting the `userInfo` dictionary, to see 
    //  if it contains any useful info
    if (userInfo != nil) {
        CFShow(userInfo);
    }
}

如果你真的想让这段代码作为一个“服务”在后台运行,并且你已经越狱了,我建议你研究一下iOS Launch Daemons。与你只是让应用程序在后台运行不同,启动守护进程可以在重新启动后自动启动,而且你不必担心iOS规则限制应用程序在后台运行任务。请告诉我们这个方法是否有效!

1
你可以尝试检查com.apple.springboard.lockcomplete。我相信只有在屏幕锁定时才会调用它,而不是在解锁时调用。因此,这可能有助于您确定正在检测哪种情况。至于userInfo为空,我怀疑这不是因为您没有越狱。并非所有通知都填充了该userInfo字典。它基本上是一个可选的有效负载。因此,它很可能是空的。它可用于希望提供其他详细信息的事件。我希望它能告诉你屏幕还是,但我想不是这样。 - Nate
1
@iOSAppDev,你可以通过将你的应用程序变成一个Launch Daemon(如我上面所提到的),或者查看此答案中描述的未记录的UIBackgroundModes来使你的应用程序比正常的iOS规则允许的时间更长地运行。这些后台模式是未记录的API,不会被批准用于App Store应用程序,但它们不需要越狱即可使用。 - Nate
@Nate 感谢您的回复。那么这意味着,即使我使用未记录的 UIBackgroundModes,我也不能将应用提交到App Store,对吗? - iOSAppDev
我猜他们会拒绝它。 - Nate
感谢 Nate 的回复。非常感谢你的帮助。 - iOSAppDev
显示剩余5条评论

2
使用较低级别的通知API,您可以在接收到通知时查询锁定状态:
#import <notify.h>

int notify_token;
notify_register_dispatch("com.apple.springboard.lockstate", &notify_token, dispatch_get_main_queue(), ^(int token) {
    uint64_t state = UINT64_MAX;
    notify_get_state(token, &state);
    NSLog(@"com.apple.springboard.lockstate = %llu", state);
});

当然,您的应用程序将需要启动一个UIBackgroundTask以获取通知,这会由于iOS允许的有限运行时间而限制此技术的实用性。


如何在Swift中使用这个? - Abhishek Mitra

-2

当 iPhone 屏幕被锁定时,appdelegate 方法"- (void)applicationWillResignActive:(UIApplication *)application"将会被调用,您可以检查它。希望这能对您有所帮助。


检查必须在应用程序不在前台时也能正常工作。我假设applicationWillResignActive:只有在应用程序在前台时才会被调用? - Sunkas
哎呀,如果是这样的话,你将不得不反向工程私有API,尝试找到相应的通知。没有公开的方法可以做到这一点。此外,您还需要越狱设备,以便它能够像那样持续在后台运行。书籍《Hacking and Securing iOS Applications》中列出了一些启动守护进程的方法。 - borrrden
好的。谢谢你的提示。实际上,到目前为止,我还没有需要越狱设备才能运行长时间后台服务。只需将其“声明”为VOIP应用程序,iOS就不会关闭它。 - Sunkas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接