越狱设备上的本地通知
由于iOS框架不允许本地通知在发布之前执行代码,因此我正在寻找一种在越狱设备上实现它的方法。
- 是否有function在越狱设备上安排代码执行,而不需要用户进行交互?
- 代码应该下载更新并确定用户是否应该收到通知。
- 我不想使用推送通知,这需要一个外部服务器推送给用户。
更新
那么,我已经设法创build一个启动时启动并保持运行的守护进程。 但是,发布通知需要UIApplication
对象。 根据文档,这个单例是由UIApplicationMain()
方法创build的,对于一个普通的应用程序来说,它是由main()
调用的。 由于我想通知由守护进程发布,单身人士是零。
我可以创build一个UIApplication
实例吗? 或以其他方式发布通知?
我已经尝试调用UIApplicationMain()
,然后在应用程序委托中发布通知,以及杀死应用程序,但是这会显示一个黑屏; 我想它启动应用程序。 而且,当应用程序启动不可能时(当电话尚未完全启动时),它会导致守护进程崩溃。
这是代码草图
int main(){ if(launchedBySpringBoard || launchedBynotification) UIApplicationMain(...); else if(launchedByDaeamon) StartRunLoop(); } void triggerdByRunLoopEveryXhours(){ downloadData(); if(isNewData()) postNotification(); }
…或以其他方式发布通知?
是的 。 您可以使用触发通知( 不一定是UILocalNotification
)的后台(启动)守护程序来执行UILocalNotification
。 当通知向用户显示警报时,守护程序可以决定打开一个正常的UI应用程序(或不)。
build立一个启动守护进程。
这是我find的最好的教程 。 启动守护程序在手机启动时启动,并始终作为非graphics化后台进程运行。 从那里,你可以安排你的支票更新。 (我有一个HelloDaemon
类,在run:
方法中完成所有工作):
int main(int argc, char *argv[]) { @autoreleasepool { HelloDaemon* daemon = [[HelloDaemon alloc] init]; // start a timer so that the process does not exit. NSTimer* timer = [[NSTimer alloc] initWithFireDate: [NSDate date] interval: 1.0 target: daemon selector: @selector(run:) userInfo: nil repeats: NO]; NSRunLoop* runLoop = [NSRunLoop currentRunLoop]; [runLoop addTimer: timer forMode: NSDefaultRunLoopMode]; [runLoop run]; } return 0; }
守护进程可以正常使用NSTimer
,因此安排另一个计时器(在run:
NSTimer
来检查更新以便随时下载。
从守护进程通知用户
如果守护进程决定应该通知用户,那么您可以:
1)打开完整的UI应用程序。
#include <dlfcn.h> #define SBSERVPATH "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices" -(void) openApp { // the SpringboardServices.framework private framework can launch apps, // so we open it dynamically and find SBSLaunchApplicationWithIdentifier() void* sbServices = dlopen(SBSERVPATH, RTLD_LAZY); int (*SBSLaunchApplicationWithIdentifier)(CFStringRef identifier, Boolean suspended) = dlsym(sbServices, "SBSLaunchApplicationWithIdentifier"); int result = SBSLaunchApplicationWithIdentifier(CFSTR("com.mycompany.AppName"), false); dlclose(sbServices); }
此代码需要com.apple.springboard.launchapplications
权限才能使守护进程成功使用它。 请参阅此处添加权利 。 你需要一个entitlements.xml文件来保存你的守护进程可执行文件,如下所示:
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.springboard.launchapplications</key> <true/> </dict> </plist>
2)从守护进程中显示一个简单的警报窗口 ,通知用户该事件,并提示他们打开UI应用程序
#include "CFUserNotification.h" -(void) showAlert { NSMutableDictionary* dict = [NSMutableDictionary dictionary]; [dict setObject: @"Alert!" forKey: (__bridge NSString*)kCFUserNotificationAlertHeaderKey]; [dict setObject: @"Updates Ready!" forKey: (__bridge NSString*)kCFUserNotificationAlertMessageKey]; [dict setObject: @"View" forKey:(__bridge NSString*)kCFUserNotificationDefaultButtonTitleKey]; [dict setObject: @"Cancel" forKey:(__bridge NSString*)kCFUserNotificationAlternateButtonTitleKey]; SInt32 error = 0; CFUserNotificationRef alert = CFUserNotificationCreate(NULL, 0, kCFUserNotificationPlainAlertLevel, &error, (__bridge CFDictionaryRef)dict); CFOptionFlags response; // we block, waiting for a response, for up to 10 seconds if((error) || (CFUserNotificationReceiveResponse(alert, 10, &response))) { NSLog(@"alert error or no user response after 10 seconds"); } else if((response & 0x3) == kCFUserNotificationAlternateResponse) { // user clicked on Cancel ... just do nothing NSLog(@"cancel"); } else if((response & 0x3) == kCFUserNotificationDefaultResponse) { // user clicked on View ... so, open the UI App NSLog(@"view"); [self openApp]; } CFRelease(alert); }
你需要一个CFUserNotification.h
头文件来按照上面的方式使用代码。 你可以通过Googlesearchfind一个,或者在这里看到一个 。 这个较旧的wiki文档还显示了从iOS应用程序使用CFUserNotification
一些很好的信息。
我从上面的KennyTM链接到的答案也显示了如何使您的警报popup显示,即使设备被locking。
首先,让我说BigLex提供了非常有趣的信息。 但是,我从来没有试图为越狱iPhone写一个deamon。 所以,我不知道有限制(它看起来像有一些 – 像UIApplication共享应用程序是零。
几个想法:
Backgrounding
1)如果您计划通过Cydia分发(意味着应用程序将最终位于系统卷上),则可以使用两种非文档背景模式:
“连续”(这个会继续在后台运行)“unboundedTaskCompletion”(这个会有无限的时间,如果你愿意的话[UIApplication beginBackgroundTaskWithExpirationHandler]
你可以看看Info.plist 这个例子,它使用了连续的。
2)还有其他方法获得永久背景(甚至不需要甚至设备越狱)。
例如,常见的方法是在循环中运行无声audio。 这里是例子如何做到这一点。
只是要知道,这种方法不会被接受到App Store。
3)在这种情况下,如果设备使用路由1)或2),您将有权访问[UIApplication sharedApplication]发布本地通知
4)你可能有兴趣看看Backgrounder 。 我相信它为越狱设备实施了后台function。 但是,这可能是过时的。
用UIApplication守护进程问题
5)关于守护进程的问题。 如果你仔细阅读那篇文章,你会看到
首先要注意的是,使用UIApplication类来启动守护进程是不好的(它会占用比我们需要的更多的内存),所以我们要编写自己的主要方法。
所以,那里的代码被优化了一个内存。 不过,我很确定你可以用普通的iOS应用程序代码replace它:
int main(int argc, char *argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
因此,我认为你应该有UIApplication单身人士,应该能够发布本地通知。
是的,它会消耗额外的X千字节的内存,但是谁在乎(如果你没有运行100个这样的守护进程)
只是猜测,这不是一个真正的答案,但也许你可以使用MobileSubstrate的挂钩function挂钩在操作系统的通知处理过程,并告诉操作系统执行一些代码来检查通知来自你的应用程序,如果是这样的话,检查更新,并决定是否应该显示通知?
或者,也许你可以开始一个后台进程,每X分钟检查是否有任何更新,如果这样做设置立即本地通知。 不知道你怎么可以这样做。