före iOS 7 var Utvecklare ganska begränsade i vad de kunde göra när deras appar lämnade förgrunden. Bortsett från VOIP och platsbaserade funktioner var det enda sättet att köra kod i bakgrunden att använda bakgrundsuppgifter, begränsade till att köra i några minuter. Om du vill ladda ner en stor video för offlinevisning eller säkerhetskopiera en användares foton till din server kan du bara slutföra en del av arbetet.
iOS 7 lägger till två nya API: er för att uppdatera appens användargränssnitt och innehåll i bakgrunden. Den första, Bakgrundshämtning, låter dig hämta nytt innehåll från nätverket med jämna mellanrum. Den andra, fjärrmeddelanden, är en ny funktion som utnyttjar Push-meddelanden för att meddela en app när en händelse har inträffat. Båda dessa nya mekanismer hjälper dig att hålla appens gränssnitt uppdaterat och kan schemalägga arbetet med den nya Bakgrundsöverföringstjänsten, som låter dig utföra nätverksöverföringar utanför processen (nedladdningar och uppladdningar).
Bakgrundshämtning och fjärrmeddelanden är enkla applikationsdelegatkrokar med 30 sekunders väggklocktid för att utföra arbete innan din app är avstängd. De är inte avsedda för CPU-intensivt arbete eller långvariga uppgifter, snarare är de för att köa upp långvariga nätverksförfrågningar, som en stor filmnedladdning eller utföra snabba innehållsuppdateringar.
från en användares perspektiv är den enda uppenbara förändringen till multitasking den nya app switcher, som visar en ögonblicksbild av varje apps användargränssnitt som det var när det lämnade förgrunden. Men det finns en anledning att visa ögonblicksbilderna – du kan nu uppdatera appens ögonblicksbild när du har slutfört bakgrundsarbetet och visar en förhandsgranskning av nytt innehåll. Sociala nätverk, nyheter eller väderappar kan nu visa det senaste innehållet utan att användaren behöver öppna appen. Vi får se hur du uppdaterar ögonblicksbilden senare.
Bakgrundshämtning
Bakgrundshämtning är en typ av smart omröstningsmekanism som fungerar bäst för appar som har frekventa innehållsuppdateringar, som sociala nätverk, nyheter eller väderappar. Systemet väcker appen baserat på användarens beteende och syftar till att utlösa bakgrundshämtningar innan användaren startar appen. Till exempel, om användaren alltid använder en app klockan 1, lär sig systemet och anpassar sig och utför hämtningar före användningsperioder. Bakgrundshämtningar samlas över appar av enhetens radio för att minska batterianvändningen, och om du rapporterar att nya data inte var tillgängliga under en hämtning kan iOS anpassa sig med hjälp av denna information för att undvika hämtningar vid tysta tider.
det första steget i att aktivera Bakgrundshämtning är att ange att du ska använda funktionen i UIBackgroundModes
– tangenten i din info plist. Det enklaste sättet att göra detta är att använda fliken nya funktioner i Xcode 5: s projektredigerare, som innehåller ett avsnitt för bakgrundslägen för enkel konfiguration av multitasking-alternativ.
Alternativt kan du redigera nyckeln manuellt:
<key>UIBackgroundModes</key><array> <string>fetch</string></array>
berätta sedan för iOS hur ofta du vill hämta:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ ; return YES;}
standardhämtningsintervallet är aldrig, så du måste ställa in ett tidsintervall eller appen kommer aldrig att ringas i bakgrunden. Värdet UIApplicationBackgroundFetchIntervalMinimum
ber systemet att hantera när din app vaknar, så ofta som möjligt, men du bör ange ditt eget tidsintervall om detta är onödigt. Till exempel kan en väderapp bara uppdatera villkoren varje timme. iOS väntar åtminstone det angivna tidsintervallet mellan bakgrundshämtningar.
om din ansökan tillåter en användare att logga ut, och du vet att det inte kommer att finnas några nya data, kanske du vill ställa in minimumBackgroundFetchInterval
tillbaka till UIApplicationBackgroundFetchIntervalNever
att vara en god medborgare och att spara resurser.
det sista steget är att implementera följande metod i din applikationsdelegat:
- (void) application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSURLSessionConfiguration *sessionConfiguration = ; NSURLSession *session = ; NSURL *url = initWithString:@"http://yourserver.com/data.json"]; NSURLSessionDataTask *task = ; // Start the task ;}
det är här du kan utföra arbete när du vaknar av systemet. Kom ihåg att du bara har 30 sekunder på dig att avgöra om nytt innehåll är tillgängligt, bearbeta det nya innehållet och uppdatera användargränssnittet. Det borde vara tillräckligt med tid att hämta data från nätverket och hämta några miniatyrer för ditt användargränssnitt, men inte mycket mer. När dina nätverksförfrågningar är klara och ditt användargränssnitt har uppdaterats bör du ringa slutförandehanteraren.
kompletteringshanteraren tjänar två syften. Först mäter systemet kraften som används av din process och registrerar om nya data var tillgängliga baserat på argumentet UIBackgroundFetchResult
som du passerade. För det andra, när du ringer färdigställningshanteraren, tas en ögonblicksbild av ditt användargränssnitt och appomkopplaren uppdateras. Användaren kommer att se det nya innehållet när han eller hon byter appar. Denna färdigställandehanterare snapshotting beteende är vanligt för alla färdigställandehanterare i de nya multitasking API: erna.
i en verklig applikation bör du skicka completionHandler
till underkomponenter i din applikation och ringa den när du har bearbetat data och uppdaterat ditt användargränssnitt.
vid denna tidpunkt kanske du undrar hur iOS kan snapshot din app UI när den körs i bakgrunden och hur applikationens livscykel fungerar med Bakgrundshämtning. Om din app för närvarande är avstängd, kommer systemet att väcka den innan du ringer application: performFetchWithCompletionHandler:
. Om din app inte körs startar systemet det och kallar de vanliga delegatmetoderna, inklusive application: didFinishLaunchingWithOptions:
. Du kan tänka på det som appen körs exakt på samma sätt som om användaren hade lanserat det från Springboard, förutom UI är osynlig, återges utanför skärmen.
i de flesta fall utför du samma arbete när programmet startar i bakgrunden som i förgrunden, men du kan upptäcka bakgrundslanseringar genom att titta på egenskapen applicationState
för UIApplication:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ NSLog(@"Launched in background %d", UIApplicationStateBackground == application.applicationState); return YES;}
testa Bakgrundshämtning
det finns två sätt att simulera en bakgrundshämtning. Den enklaste metoden är att köra din applikation från Xcode och klicka på Simulera Bakgrundshämtning under Xcodes Felsökningsmeny medan din app körs.
Alternativt kan du använda ett schema för att ändra hur Xcode kör din app. Under menyalternativet Xcode produkt, Välj schema och sedan hantera scheman. Härifrån, redigera eller lägga till ett nytt schema och kontrollera lanseringen på grund av en bakgrund Hämta händelse kryssrutan som visas nedan.
fjärrmeddelanden
fjärrmeddelanden låter dig meddela din app när viktiga händelser inträffar. Du kan ha nya snabbmeddelanden att leverera, bryta nyheter varningar för att skicka, eller den senaste episoden av användarens favorit TV-show redo för honom eller henne att ladda ner för visning offline. Fjärrmeddelanden är bra för sporadiskt men omedelbart viktigt innehåll, där förseningen mellan bakgrundshämtningar kanske inte är acceptabel. Fjärrmeddelanden kan också vara mycket effektivare än Bakgrundshämtning, eftersom din applikation bara startar vid behov.
en Fjärrmeddelande är egentligen bara en vanlig Push-anmälan med content-available
flagguppsättning. Du kan skicka ett tryck med ett varningsmeddelande som informerar användaren om att något har hänt, medan du uppdaterar användargränssnittet i bakgrunden. Men fjärrmeddelanden kan också vara tysta, utan varningsmeddelande eller ljud, som bara används för att uppdatera appens gränssnitt eller utlösa bakgrundsarbete. Du kan sedan lägga upp ett lokalt meddelande när du har laddat ner eller bearbetat det nya innehållet.
tysta push-meddelanden är hastighetsbegränsade, så var inte rädd för att skicka så många som din ansökan behöver. iOS och APN-servrarna kommer att styra hur ofta de levereras, och du kommer inte att få problem för att skicka för många. Om dina push-meddelanden stryps kan de försenas till nästa gång enheten skickar ett keep-alive-paket eller tar emot ett annat meddelande.
skicka Fjärraviseringar
för att skicka en fjärravisering, Ställ in flaggan innehåll tillgängligt i en push notification nyttolast. Den innehålls tillgängliga flaggan är samma nyckel som används för att meddela tidningskiosk apps, så de flesta push-skript och bibliotek stöder redan fjärrmeddelanden. När du skickar en Fjärravisering kanske du också vill inkludera vissa data i anmälnings nyttolasten, så att din applikation kan referera till händelsen. Detta kan spara några nätverksförfrågningar och öka appens lyhördhet.
jag rekommenderar att du använder Nomad CLIs Houston-verktyg för att skicka push-meddelanden medan du utvecklar, men du kan använda ditt favoritbibliotek eller skript.
du kan installera Houston som en del av nomad-CLI ruby gem:
gem install nomad-cli
och sedan skicka ett meddelande med apn-verktyget som ingår i Nomad
# Send a Push Notification to your Deviceapn push <device token> -c /path/to/key-cert.pem -n -d content-id=42
här anger -n
flaggan att den innehålls tillgängliga nyckeln ska inkluderas, och -d
tillåter oss att lägga till våra egna datanycklar till nyttolasten.
den resulterande anmälan nyttolast ser ut så här:
{ "aps" : { "content-available" : 1 }, "content-id" : 42}
iOS 7 lägger till en ny applikationsdelegeringsmetod, som anropas när en push-avisering med den tillgängliga innehållsnyckeln tas emot:
- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSLog(@"Remote Notification userInfo is %@", userInfo); NSNumber *contentID = userInfo; // Do something with the content ID completionHandler(UIBackgroundFetchResultNewData);}
återigen lanseras appen i bakgrunden och ges 30 sekunder för att hämta nytt innehåll och uppdatera användargränssnittet innan du ringer till färdigställningshanteraren. Vi kunde utföra en snabb nätverksförfrågan som vi gjorde i bakgrunden Hämta exempel, men låt oss använda den kraftfulla nya Bakgrundsöverföringstjänsten för att köa en stor nedladdningsuppgift och se hur vi kan uppdatera vårt användargränssnitt när det är klart.
Nsurlsession och Bakgrundsöverföringstjänst
medan NSURLSession
är en ny klass i iOS 7, hänvisar den också till den nya tekniken i Grundnätverk. Avsedda att ersätta NSURLConnection
bevaras kända begrepp och klasser som NSURL
, NSURLRequest
och NSURLResponse
. Du kommer att arbeta med NSURLConnection
ersättning, NSURLSessionTask
, för att göra nätverksförfrågningar och hantera deras svar. Det finns tre typer av sessionsuppgifter-data, nedladdning och Uppladdning – som alla lägger till syntaktiskt socker till NSURLSessionTask
, så du bör använda den lämpliga för ditt användningsfall.
An NSURLSession
koordinerar en eller flera av dessa NSURLSessionTask
s och beter sig enligt NSURLSessionConfiguration
som den skapades med. Du kan skapa flera NSURLSession
s för att gruppera relaterade uppgifter med samma konfiguration. För att interagera med Bakgrundsöverföringstjänsten skapar du en sessionskonfiguration med . Uppgifter som läggs till i en bakgrundssession körs i en extern process och fortsätter även om din app är avstängd, kraschar eller dödas.
NSURLSessionConfiguration
låter dig ställa in standard HTTP-rubriker, konfigurera cachepolicyer, begränsa användningen av mobilnät och mer. Ett alternativ är flaggan discretionary
, som gör att systemet kan schemalägga uppgifter för optimal prestanda. Vad detta betyder är att dina överföringar bara går över Wifi när enheten har tillräcklig ström. Om batteriet är lågt eller bara en mobilanslutning är tillgänglig körs inte din uppgift. Flaggan discretionary
har bara en effekt om sessionskonfigurationsobjektet har konstruerats genom att anropa metoden backgroundSessionConfiguration:
och om bakgrundsöverföringen initieras medan din app är i förgrunden. Om överföringen initieras från bakgrunden kommer överföringen alltid att köras i diskretionärt läge.
nu vet vi lite om NSURLSession
, och hur en bakgrundssession fungerar, låt oss återgå till vårt exempel på Fjärrmeddelande och lägga till lite kod för att hämta en nedladdning på bakgrundsöverföringstjänsten. När nedladdningen är klar meddelar vi användaren att filen är tillgänglig för användning.
NSURLSessionDownloadTask
först och främst, låt oss hantera en Fjärrmeddelande och fråga en NSURLSessionDownloadTask
på bakgrundsöverföringstjänsten. I backgroundURLSession
skapar vi en NURLSession
med en bakgrundssessionskonfiguration och lägger till vår applikationsdelegat som sessionsdelegat. Dokumentationen avråder från att starta flera sessioner med samma identifierare, så vi använder dispatch_once
för att undvika potentiella problem:
- (NSURLSession *)backgroundURLSession{ static NSURLSession *session = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *identifier = @"io.objc.backgroundTransferExample"; NSURLSessionConfiguration* sessionConfig = ; session = ]; }); return session;}- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ NSLog(@"Received remote notification with userInfo %@", userInfo); NSNumber *contentID = userInfo; NSString *downloadURLString = ]; NSURL* downloadURL = ; NSURLRequest *request = ; NSURLSessionDownloadTask *task = downloadTaskWithRequest:request]; task.taskDescription = ]; ; completionHandler(UIBackgroundFetchResultNewData);}
Vi skapar en nedladdningsuppgift med klassmetoden NSURLSession
och konfigurerar dess begäran och ger en beskrivning för användning senare. Du måste komma ihåg att ringa för att faktiskt starta uppgiften, eftersom alla sessionsuppgifter börjar i suspenderat tillstånd.
nu måste vi implementera NSURLSessionDownloadDelegate
– metoderna för att ta emot återuppringning när nedladdningen är klar. Du kan också behöva implementera NSURLSessionDelegate
eller NSURLSessionTaskDelegate
metoder om du behöver hantera autentisering eller andra händelser i sessionens livscykel. Du bör konsultera Apples dokumentlivscykel för en URL-Session med anpassade delegater, vilket förklarar hela livscykeln för alla typer av sessionsuppgifter.
ingen av de NSURLSessionDownloadDelegate
delegera metoder är valfria, men den enda där vi behöver vidta åtgärder i detta exempel är . När uppgiften är klar med nedladdningen får du en tillfällig URL till filen på disken. Du måste flytta eller kopiera filen till appens lagring, eftersom den kommer att tas bort från tillfällig lagring när du återvänder från den här delegeringsmetoden.
#Pragma Mark - NSURLSessionDownloadDelegate- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{ NSLog(@"downloadTask:%@ didFinishDownloadingToURL:%@", downloadTask.taskDescription, location); // Copy file to your app's storage with NSFileManager // ... // Notify your UI}- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{}- (void) URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{}
om din app fortfarande körs i förgrunden när bakgrundssessionsuppgiften är klar kommer ovanstående kod att vara tillräcklig. I de flesta fall kommer din app dock inte att köras, eller den kommer att stängas av i bakgrunden. I dessa fall måste du implementera två application delegates-metoder så att systemet kan väcka din ansökan. Till skillnad från tidigare återuppringningar av delegater anropas applikationsdelegaten två gånger, eftersom din session och uppgiftsdelegater kan få flera meddelanden. Appdelegeringsmetoden application: handleEventsForBackgroundURLSession:
anropas innan dessa NSURLSession
delegatmeddelanden skickas och URLSessionDidFinishEventsForBackgroundURLSession
anropas efteråt. I den tidigare metoden lagrar du en bakgrund completionHandler
, och i den senare kallar du den för att uppdatera ditt användargränssnitt:
- (void) application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{ // You must re-establish a reference to the background session, // or NSURLSessionDownloadDelegate and NSURLSessionDelegate methods will not be called // as no delegate is attached to the session. See backgroundURLSession above. NSURLSession *backgroundSession = ; NSLog(@"Rejoining session with identifier %@ %@", identifier, backgroundSession); // Store the completion handler to update your UI after processing session events ;}- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{ NSLog(@"Background URL session %@ finished events.\n", session); if (session.configuration.identifier) { // Call the handler we stored in -application:handleEventsForBackgroundURLSession: ; }}- (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString *)identifier{ if () { NSLog(@"Error: Got multiple handlers for a single session identifier. This should not happen.\n"); } ;}- (void)callCompletionHandlerForSession: (NSString *)identifier{ CompletionHandlerType handler = ; if (handler) { ; NSLog(@"Calling completion handler for session %@", identifier); handler(); }}
denna tvåstegsprocess är nödvändig för att uppdatera appgränssnittet om du inte redan är i förgrunden när bakgrundsöverföringen är klar. Dessutom, om appen inte körs alls när bakgrundsöverföringen är klar, startar iOS den i bakgrunden, och de föregående applikations-och sessionsdelegeringsmetoderna anropas efter application:didFinishLaunchingWithOptions:
.
konfiguration och begränsning
Vi har kort berört kraften i bakgrundsöverföringar, men du bör utforska dokumentationen och titta på alternativen NSURLSessionConfiguration
som bäst stöder ditt användningsfall. Till exempel, NSURLSessionTasks
support resurs timeouts genom NSURLSessionConfiguration
’s timeoutIntervalForResource
egenskap. Du kan använda detta för att ange hur länge du vill tillåta en överföring för att slutföra innan du ger upp helt. Du kan använda detta om ditt innehåll endast är tillgängligt under en begränsad tid, eller om det inte går att ladda ner eller ladda upp resursen inom den angivna tidsintervallet indikerar att användaren inte har tillräcklig Wifi-bandbredd.
förutom nedladdningsuppgifter stöder NSURLSession
helt uppladdningsuppgifter, så du kan ladda upp en video till din server i bakgrunden och försäkra din användare om att han eller hon inte längre behöver lämna appen igång, vilket kan ha gjorts i iOS 6. En fin touch skulle vara att ställa in egenskapen sessionSendsLaunchEvents
för din NSURLSessionConfiguration
till NO
, om din app inte behöver startas i bakgrunden när överföringen är klar. Effektiv användning av systemresurser håller både iOS och användaren nöjd.
slutligen finns det ett par begränsningar i att använda bakgrundssessioner. Eftersom en delegat krävs kan du inte använda de enkla blockbaserade återuppringningsmetoderna på NSURLSession
. Att starta din app i bakgrunden är relativt dyrt, så HTTP-omdirigeringar tas alltid. Bakgrundsöverföringstjänsten stöder bara HTTP och HTTPS och du kan inte använda anpassade protokoll. Systemet optimerar överföringar baserat på tillgängliga resurser och du kan inte tvinga din överföring till framsteg i bakgrunden hela tiden.
Observera också att NSURLSessionDataTasks
inte stöds i bakgrundssessioner alls, och du bör bara använda dessa uppgifter för kortlivade, små förfrågningar, inte för nedladdningar eller uppladdningar.
sammanfattning
de kraftfulla nya multitasking-och nätverks-API: erna i iOS 7 öppnar en hel rad möjligheter för både nya och befintliga appar. Tänk på användningsfall i din app som kan dra nytta av Out-of-process nätverksöverföringar och färska data, och göra det mesta av dessa fantastiska nya API: er. I allmänhet implementera bakgrundsöverföringar som om din applikation körs i förgrunden, gör lämpliga UI-uppdateringar, och det mesta av arbetet är redan gjort för dig.
-
Använd lämpligt nytt API för appens innehåll.
-
var effektiv och ring färdigställandehanterare så tidigt som möjligt.
-
slutförande hanterare uppdatera appens UI snapshot.
Vidare läsning
-
WWDC 2013 session ”Vad är nytt med Multitasking”
-
WWDC 2013 session ”Vad är nytt i Foundation Networking”
-
URL laddar Systemprogrammeringsguide