由于iOS框架在发布之前不允许本地通知执行代码,因此我正在寻找一种在越狱设备上实现代码的方法。
更新
好吧,我已经成功创建了一个守护进程,它在启动时启动并保持运行。但是,发布通知需要UIApplication
对象。根据documentation,这个单例由UIApplicationMain()
方法创建,对于常规应用,由main()
调用。由于我希望通知由守护进程发布,因此单例是零。
我可以创建UIApplication
的实例吗?或者以其他方式发布通知?
我试过调用UIApplicationMain()
然后在app委托中发布通知,以及杀死应用程序,但这显示了一个黑屏暂时;我想它启动了应用程序。此外,当应用程序无法启动时(当手机尚未完全启动时),它会导致守护程序崩溃。
这是代码的草图
int main(){
if(launchedBySpringBoard || launchedBynotification)
UIApplicationMain(...);
else if(launchedByDaeamon)
StartRunLoop();
}
void triggerdByRunLoopEveryXhours(){
downloadData();
if(isNewData())
postNotification();
}
...或者以其他方式发布通知?
是。您可以使用触发通知的后台(启动)守护程序(不一定是UILocalNotification
)来完成此工作。当通知向用户显示警报时,您的守护程序可以决定打开正常的UI应用程序(或不打开)。
This is the best tutorial I've found。启动守护程序在手机启动时启动,并作为非图形后台进程一直运行。从那里,您可以安排检查更新。 (我有一个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:
内)以检查更新以便随时下载。
如果守护程序决定应该通知用户,那么您可以:
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
权利才能成功使用它。 See here for adding an entitlement。您需要一个用于守护程序可执行文件的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)显示来自守护程序的simple alert window,通知用户该事件,并提示他们打开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
标头以我上面的方式使用代码。你可以通过谷歌搜索或see one here找到一个。这个older wiki document还显示了一些使用iOS应用程序中的CFUserNotification
的好信息。
answer I linked to from KennyTM above还显示了如何设置警报弹出窗口,即使设备已锁定。
首先,我要说BigLex提供了非常有趣的信息。但是,我从未试图为越狱的iPhone写一个守护神。所以,我不知道有限制(看起来有一些 - 像UIApplication sharedApplication是零。
几个想法:
1)如果您计划通过Cydia进行分发(意味着应用程序最终将在系统卷上),您可以使用两种无证文档:
“continuos”(这个将继续在后台运行)“无界任务完成”(这个将有无限时间,如果你会做[UIApplication beginBackgroundTaskWithExpirationHandler]
你可以看看使用连续的Info.plist qazxsw poi示例。
2)还有其他方法可以获得永久性背景(甚至不需要设备越狱)。
例如,常见的方法是在循环上运行静音。 here就是如何做到这一点的例子。
请注意,此方法不会被App Store接受。
3)在这种情况下,如果您使用路由1)或2),您将可以访问[UIApplication sharedApplication]发布本地通知
4)您可能有兴趣看看Here。我相信它为越狱设备实现了后台功能。但是,它可能已经过时了。
5)关于守护进程的问题。如果你仔细阅读Backgrounder,你会看到
首先要注意的是,使用UIApplication类来启动守护进程(它需要的内存比我们需要的多)是不好的,所以我们将编写自己的main方法。
因此,那里的代码针对内存进行了优化。但是,我很确定您可以使用常见的iOS应用程序代码替换它:
article
因此,我认为你应该有UIApplication单例,并且应该能够发布本地通知。
是的...它会消耗额外的X千字节内存,但谁在乎(如果你没有运行100个这样的守护进程)
只是猜测,这不是一个真正的答案,但也许你可以使用MobileSubstrate的挂钩功能来连接操作系统的通知处理过程并告诉操作系统执行一些代码来检查通知是否来自你的应用程序,如果是这样的话,检查更新并确定是否应显示通知?
或者也许您可以启动一个后台进程,每隔X分钟检查一次是否有任何更新,如果是,则立即设置本地通知。不知道你怎么能这样做。