11

Nella nostra app, vogliamo scaricare una piccola quantità di dati in risposta a una notifica push. Finora, le notifiche push funzionano senza intoppi, avviando l'app in background e provocando la chiamata a RecectiveRemoteNotification.Il codice asincrono non viene eseguito finché l'applicazione non viene messa in primo piano nell'applicazione: didReceiveRemoteNotification: fetchCompletionHandler:

Il problema è che, dopo il ritorno di questo metodo, l'app non ottiene più tempo CPU fino a quando non viene nuovamente in primo piano, quindi non c'è alcuna possibilità di recuperare quei dati in modo asincrono in background.

Riducendo questo al caso più semplice, non riesco ancora a eseguire il codice asincrono.

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { 
    [application setApplicationIconBadgeNumber:1]; 

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     [application setApplicationIconBadgeNumber:9]; 

     completionHandler(UIBackgroundFetchResultNewData); 
    }); 
} 

In risposta a una spinta, l'applicazione lancia in background e il conteggio distintivo è impostato su 1, ma il numero distintivo non è impostato a 9 fino a quando l'applicazione viene avviata dalla schermata iniziale.

Non dovrebbe iOS continuare a eseguire l'app fino a quando non viene chiamato il gestore di completamento, fino a 30 secondi?

Info.plist ha specificato la modalità di background per la notifica remota, il payload push contiene 'content-available': '1' e non si esce dall'app passando il dito sullo switcher dell'app.

Per aggiungere, stiamo usando Analizza per inviare questa notifica push utilizzando il seguente JavaScript:

Parse.Push.send({ 
    where: installationQuery, 
    data: { 
     "content-available": 1, 
    } 
}, { success: function() {}, 
    error: function(error) {} 
}); 
+0

Hai provato questo: http://stackoverflow.com/questions/16295953/dispatch-after-is-limited-to-10-seconds? – anhtu

risposta

10

prima dare here e assicurarsi notifica push si attivato e ha aggiunto il campo Content-disponibili:

Utilizzo delle notifiche push per avviare un download Se il server invia notifiche push al dispositivo di un utente quando sono disponibili nuovi contenuti per l'app, è possibile chiedere al sistema di eseguire l'app in background in modo che possa iniziare il download il nuovo contenuto subito. L'intento di questa modalità in background è di ridurre al minimo la quantità di tempo che trascorre tra quando un utente visualizza una notifica push e quando la tua app è in grado di visualizzare il contenuto associato. Le app vengono generalmente riattivate all'incirca nello stesso momento in cui l'utente visualizza la notifica, ma ciò ti dà ancora più tempo di quello che potresti avere altrimenti. Per supportare questa modalità in background, abilitare l'opzione Notifiche remote dalla sezione Modalità sfondo della scheda Funzionalità del progetto Xcode. (È inoltre possibile abilitare questo supporto includendo la chiave UIBackgroundModes con il valore di notifica remota nel file Info.plist dell'app.) Per una notifica push per attivare un'operazione di download, il carico utile della notifica deve includere la chiave content-available con valore impostato su 1. Quando questa chiave è presente, il sistema riattiva l'app in background (o la avvia in background) e chiama l'applicazione del delegato dell'app: didReceiveRemoteNotification: fetchCompletionHandler: method. L'implementazione di tale metodo dovrebbe scaricare il contenuto pertinente e integrarlo nella tua app. Durante il download di qualsiasi contenuto, si consiglia di utilizzare la classe NSURLSession per avviare e gestire i download. Per informazioni su come utilizzare questa classe per gestire le attività di caricamento e download, consultare la guida alla programmazione del sistema di caricamento degli URL.

Quindi, c'è un motivo per cui si utilizza "dispatch_after" con ritardo di 2 secondi?

È possibile che dal momento in cui chiami "dispacth_after" alla fine del ciclo di esecuzione iOS "pensa" non ci sia lavoro da svolgere e mette il processo in stato di sospensione in modo tale che quando il blocco viene inviato nessuno sta ascoltando esso.

Sostituirlo con "dispatch_async" potrebbe risolvere il problema.

Infine, se si ha bisogno di ritardare, si dovrebbe dire iOS è necessario un certo tempo sullo sfondo, come questo -

UIApplication *application = [UIApplication sharedApplication]; 
UIBackgroundTaskIdentifier __block backgroundTaskId = [application beginBackgroundTaskWithExpirationHandler:^{ 
    if (backgroundTaskId != UIBackgroundTaskInvalid) { 
     [application endBackgroundTask:backgroundTaskId]; 
     backgroundTaskId = UIBackgroundTaskInvalid; 
    } 
}]; 

Poi fare il vostro lavoro di fondo. Non dimenticare di terminare l'attività quando il lavoro è finito. chiamare qualcosa di simile -

if (backgroundTaskId != UIBackgroundTaskInvalid) { 
     [application endBackgroundTask:backgroundTaskId]; 
     backgroundTaskId = UIBackgroundTaskInvalid; 
    } 
+0

Utilizzare un'attività in background è stato! Il dispatch_after 2 secondi era solo per ridurre il problema allo scenario più semplice possibile. In pratica, utilizzeremo un dispatch_async. Ma il problema principale qui era che iOS sembrava presumere che avessimo finito l'esecuzione quando il runloop principale finiva il suo ciclo, non quando veniva chiamato il gestore del completamento del fetch. Usando un'attività in background, iOS ci ha dato il tempo di eseguire e fare le operazioni asincrone necessarie. – jsmeez