12

iOS フレームワークでは、ローカル通知が投稿される前にコードを実行することが許可されていないため、ジェイルブレイクされたデバイスでそれを実現する方法を探しています。

  • ジェイルブレイクされたデバイスに、ユーザーが対話する必要なくコード実行をスケジュールする機能が組み込まれていますか?
  • コードは更新をダウンロードし、ユーザーが通知を受け取るかどうかを決定する必要があります。
  • プッシュ通知を使用したくありません。プッシュ通知をユーザーにプッシュするために外部サーバーが必要です。

アップデート

さて、起動時に起動し、それ自体を実行し続けるデーモンを作成することができました。ただし、通知を投稿するにはUIApplicationオブジェクトが必要です。ドキュメントによると、このシングルトンはUIApplicationMain()、通常のアプリケーションの場合、によって呼び出されるメソッドによって作成されmain()ます。デーモンによって通知を投稿したいので、シングルトンは nil です。

のインスタンスを作成できますUIApplicationか? または、他の方法で通知を投稿しますか?

UIApplicationMain()アプリのデリゲートに呼び出してから通知を投稿し、アプリケーションを強制終了しようとしましたが、これは一瞬黒い画面を表示します。アプリケーションを起動していると思います。さらに、アプリの起動が不可能な場合 (電話がまだ完全に起動していない場合) にデーモンがクラッシュします。

ここにコードのスケッチがあります

int main(){
   if(launchedBySpringBoard || launchedBynotification)
      UIApplicationMain(...);
   else if(launchedByDaeamon)
      StartRunLoop();
}

void triggerdByRunLoopEveryXhours(){
    downloadData();
    if(isNewData())
       postNotification();
}
4

3 に答える 3

12

...または他の方法で通知を投稿しますか?

はい。通知をトリガーするバックグラウンド (起動) デーモンを使用して、これを機能させることができます (必ずしもではありませんUILocalNotification)。通知がユーザーに警告を表示すると、デーモンは通常の UI アプリケーションを開くかどうかを決定できます。

起動デーモンを構築します。

これは私が見つけた最高のチュートリアルです。起動デーモンは、電話機の起動時に開始され、非グラフィカル バックグラウンド プロセスとして常に実行されます。そこから、更新のチェックをスケジュールできます。(メソッド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を正常に使用するには、デーモンの資格が必要です。 エンタイトルメントの追加については、こちらを参照してください次のように、デーモン実行可能ファイルには 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上記の方法でコードを使用するには、ヘッダーが必要です。グーグルで検索するか、こちらを参照してください。この古い wiki ドキュメントCFUserNotificationには、iOS アプリから使用するための優れた情報も示されています。

上記のKennyTM からリンクした回答は、デバイスがロックされている場合でもアラート ポップアップを表示する方法も示しています。

于 2013-03-16T22:44:53.037 に答える
5

まず、BigLex が非常に興味深い情報を提供していることをお伝えします。しかし、ジェイルブレイクされた iPhone 用のデーモンを書き込もうとしたことはありません。したがって、私は制限を認識していません (そして、UIApplication sharedApplication が nil.

いくつかの考え:

バックグラウンド処理

1) Cydia を介して配布する予定の場合 (つまり、アプリケーションが最終的にシステム ボリューム上にあることを意味します)、2 つのドキュメント化されていないバックグラウンド モードを使用できます。

"continuos" (これはバックグラウンドで実行され続けます) "unboundedTaskCompletion" (これは [UIApplication beginBackgroundTaskWithExpirationHandler] を行う場合、時間は無制限になります)

continouse を使用する Info.plist の例をここで見ることができます。

2) 恒久的なバックグラウンドを取得する方法は他にもあります (デバイスをジェイルブレイクする必要さえありません)。

例として、一般的な方法は、ループでサイレント オーディオを実行することです。これを行う方法の例を次に示します。

この方法は App Store では受け入れられないことに注意してください。

3) この場合、ルート 1) または 2) を使用するデバイスの場合、[UIApplication sharedApplication) にアクセスしてローカル通知を投稿できます。

4) バックグラウンダーをご覧になることをお勧めします。ジェイルブレイクされたデバイスのバックグラウンド機能を実装したと思います。ただし、古くなっている可能性があります。

UIApplication のデーモンの問題

5) デーモンの問題について。その記事をよく読めばわかる

最初に注意すべきことは、UIApplication クラスを使用してデーモンを起動するのは良くないということです (必要以上のメモリを必要とします)。そのため、独自の main メソッドを作成します。

そのため、そこにあるコードはメモリ用に最適化されています。ただし、一般的な iOS アプリケーション コードに置き換えることができると確信しています。

int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

その結果、UIApplication シングルトンが必要であり、ローカル通知を投稿できるはずです。

ええ...さらにXキロバイトのメモリを消費しますが、誰が気にしますか(そのようなデーモンを100個実行していない場合)

于 2013-03-16T21:30:19.157 に答える
2

推測ですが、これは本当の答えではありませんが、MobileSubstrate のフック機能を使用して OS の通知処理プロセスに接続し、通知がアプリからのものかどうかを確認するコードを実行するように OS に指示することができます。更新を確認して、通知を表示するかどうかを決定しますか?

または、X 分ごとに更新があるかどうかをチェックし、更新がある場合は即時のローカル通知を設定するバックグラウンド プロセスを開始することもできます。ただし、これをどのように行うことができるかわかりません。

于 2013-02-22T13:23:00.737 に答える