詳解在IOS后臺執(zhí)行
在IOS后臺執(zhí)行是本文要介紹的內(nèi)容,大多數(shù)應(yīng)用程序進(jìn)入后臺狀態(tài)不久后轉(zhuǎn)入暫停狀態(tài)。在這種狀態(tài)下,應(yīng)用程序不執(zhí)行任何代碼,并有可能在任意時候從內(nèi)存中刪除。應(yīng)用程序提供特定的服務(wù),用戶可以請求后臺執(zhí)行時間,以提供這些服務(wù)。
判斷是否支持多線程
- UIDevice* device = [UIDevice currentDevice];
 - BOOL backgroundSupported = NO;
 - if ([device respondsToSelector:@selector(isMultitaskingSupported)])
 - backgroundSupported = device.multitaskingSupported;
 
聲明你需要的后臺任務(wù)
Info.plist中添加UIBackgroundModes鍵值,它包含一個或多個string的值,包括
audio:在后臺提供聲音播放功能,包括音頻流和播放視頻時的聲音
location:在后臺可以保持用戶的位置信息
voip:在后臺使用VOIP功能
前面的每個value讓系統(tǒng)知道你的應(yīng)用程序應(yīng)該在適當(dāng)?shù)臅r候被喚醒。例如,一個應(yīng)用程序,開始播放音樂,然后移動到后臺仍然需要執(zhí)行時間,以填補(bǔ)音頻輸出緩沖區(qū)。添加audio鍵用來告訴系統(tǒng)框架,需要繼續(xù)播放音頻,并且可以在合適的時間間隔下回調(diào)應(yīng)用程序;如果應(yīng)用程序不包括此項,任何音頻播放在移到后臺后將停止運(yùn)行。
除了添加鍵值的方法,IOS還提供了兩種途徑使應(yīng)用程序在后臺工作:
Task completion—應(yīng)用程序可以向系統(tǒng)申請額外的時間去完成給定的任務(wù)
Local notifications—應(yīng)用程序可以預(yù)先安排時間執(zhí)行l(wèi)ocal notifications 傳遞
實現(xiàn)長時間的后臺任務(wù)
應(yīng)用程序可以請求在后臺運(yùn)行以實現(xiàn)特殊的服務(wù)。這些應(yīng)用程序并不連續(xù)的運(yùn)行,但是會被系統(tǒng)框架在合適的時間喚醒,以實現(xiàn)這些服務(wù)
1、 追蹤用戶位置:略
2、在后臺播放音頻:
添加UIBackgroundModes中audio值,注冊后臺音頻應(yīng)用。這個值使得應(yīng)用程序可以在后臺使用可聽的背景,如音樂播放或者音頻流應(yīng)用。對于支持音頻和視頻功能的應(yīng)用程序也可以添加該值以保證可以繼續(xù)持續(xù)的運(yùn)行流。
當(dāng)audio值設(shè)置后,當(dāng)你的應(yīng)用程序進(jìn)入后臺后,系統(tǒng)的多媒體框架會自動阻止它被掛斷,但是,如果應(yīng)用程序停止播放音頻或者視頻,系統(tǒng)將掛斷應(yīng)用程序。
當(dāng)你的應(yīng)用程序在后臺時,你可以執(zhí)行任意的系統(tǒng)音頻框架去初始化后臺音頻。你的應(yīng)用程序在后臺時應(yīng)該限制自身,使其執(zhí)行與工作相關(guān)的代碼,不能執(zhí)行任何與播放內(nèi)容無關(guān)的任務(wù)
由于有多個應(yīng)用程序支持音頻,前臺的應(yīng)用程序始終允許播放音頻,后臺的應(yīng)用程序也被允許播放一些音頻內(nèi)容,這取決于audio session object的設(shè)置。應(yīng)用程序應(yīng)該始終設(shè)置它們的audio session object,并小心的處理其他類型的音頻相關(guān)notifications和中斷。詳見audio session programming guide。
3、實現(xiàn)VOIP應(yīng)用:
VOIP程序需要穩(wěn)定的網(wǎng)絡(luò)去連接和它相關(guān)的服務(wù),這樣它才能接到來電和其他相關(guān)的數(shù)據(jù)。系統(tǒng)允許VOIP程序被掛斷并提供組件去監(jiān)聽它們的sockets,而不是在任意時候都處于喚醒狀態(tài)。設(shè)置VOIP應(yīng)用程序如下:
A、 添加UIBackgroundModes中的VOIP鍵值
B、 為VOIP設(shè)置一個應(yīng)用程序socket
C、 在移出后臺之前,調(diào)用setKeepAliveTimeout:handler:方法去建立一個定期執(zhí)行的handler,你的應(yīng)用程序可以運(yùn)行這個handler來保持服務(wù)的連接。
D、 設(shè)置你的audio session去處理這種切換
釋義:
A、大多數(shù)VOIP應(yīng)用需要設(shè)置后臺audio 應(yīng)用去傳遞音頻,因此你應(yīng)該設(shè)置audio 和voip兩個鍵值。
B、為了使應(yīng)用程序在后臺時保持穩(wěn)定的連接,你必須tag你的主通訊socket專門應(yīng)用于VOIP,tagging這個socket來告訴系統(tǒng),它必須在你的應(yīng)用程序中斷時接管這個socket。這個切換本身對于你的應(yīng)用程序時透明的,當(dāng)新的數(shù)據(jù)到達(dá)socket的時候,系統(tǒng)會喚醒應(yīng)用程序,并將socket的控制權(quán)返回給應(yīng)用程序,這樣應(yīng)用程序就可以處理新來的數(shù)據(jù)。
你只需要tag用于voip服務(wù)的socket,這個socket用來接收來電或者其他相關(guān)的數(shù)據(jù)來保持你的VOIP服務(wù)的連接。根據(jù)收到的信息,這個socket要決定下一步的動作。比如一個來電,你會想彈出一個本地的通知來告知用戶;對于其他不是那么關(guān)鍵的數(shù)據(jù),你可能會想悄悄的處理這些數(shù)據(jù)并讓系統(tǒng)將應(yīng)用程序重新中斷。
在IOS中,sockets是用流或者更高級的結(jié)構(gòu),設(shè)置一個VOIP的socket,你只需要在通常的設(shè)置中添加一個特殊的key來標(biāo)明這個接口是用于連接VOIP服務(wù)的,下表列出了流的接口和設(shè)置:
設(shè)置流接口用于voip
 
接口
設(shè)置
- NSInputStream 和NSOutputStream
 
對于 Cocoa streams, 使用 setProperty:forKey: 方法添加
- NSStreamNetworkServiceType
 - 屬性給
 - stream.
 - 改屬性的值設(shè)為
 - NSStreamNetworkServiceTypeVoIP.
 - NSURLRequest
 
對于 URL loading system, 使用 setNetworkServiceType:
- method of your NSMutableURLRequest object to set the network service
 - type of the request. The service type should be set to
 - NSURLNetworkServiceTypeVoIP.
 
CFReadStreamRef和CFWriteStreamRef
- For Core Foundation streams, use the CFReadStreamSetProperty or
 - CFWriteStreamSetProperty function to add the kCFStreamNetwork-
 - ServiceType property to the stream. The value for this property should be
 - set to kCFStreamNetworkServiceTypeVoIP.
 
(注意:當(dāng)設(shè)置socket的時候,你需要在你的主信號通道中設(shè)置合適的service type key。當(dāng)設(shè)置聲道時,不需要設(shè)置這個key)
由于,VOIP應(yīng)用程序需要一直運(yùn)行以確保收到來電,所以如果程序通過一個非零的exit code退出,系統(tǒng)將自動重啟這個應(yīng)用程序(這種退出方式可以發(fā)生在內(nèi)存壓力大時終止程序運(yùn)行)。盡管如此,中斷應(yīng)用程序會release所有的sockets,包括那個用于連接voip 服務(wù)的socket。因此,當(dāng)程序運(yùn)行時,它需要一直從頭創(chuàng)建socket。
C、 為了防止斷連,voip程序需要定期被喚醒去檢查它的服務(wù)。為了容易實現(xiàn)這個行為,IOS通過使用(UIApplication setKeepAliveTimeout:handler:)方法建立一個特殊的句柄。你可以在applicationDidEnterBackground方法中建立該句柄。一旦建立,系統(tǒng)至少會在超時之前調(diào)用該句柄一次,來喚醒你的應(yīng)用程序。
這個keep-alive handler在后臺執(zhí)行,必須盡快的返回參數(shù),它有最多30秒的時間來執(zhí)行所需的任務(wù),如果這段時間內(nèi)句柄沒有返回,那么系統(tǒng)將終止應(yīng)用程序。
當(dāng)你建立了handler之后,確定應(yīng)用程序所需的最大超時。系統(tǒng)保證會在最大超時之前調(diào)用handler,但是這個時間是不確定的,所以你的handler必須在你申明的超時之前做好執(zhí)行程序的準(zhǔn)備。
D、設(shè)置audio session,詳見Audio Session Programming Guide.
在后臺完成有限長度的任務(wù)
在被終止之前的任意時間,應(yīng)用程序會調(diào)用beginBackgroundTaskWithExpirationHandler:方法讓系統(tǒng)給出額外的時間來完成一些需要在后臺長時間執(zhí)行的任務(wù)。(UIApplication的backgroundTimeRemaining屬性包含程序運(yùn)行的總時間)
可以使用task completion去保證那些比較重要但是需要長時間運(yùn)行的程序不會由于用戶切入后臺而突然關(guān)閉。比如,你可以用這項功能來將用戶的信息保存到disk上或者從網(wǎng)絡(luò)下載一個重要的文件。有兩種方式來初始化這樣的任務(wù):
1、將長時間運(yùn)行的重要任務(wù)用beginBackgroundTaskWithExpirationHandler:和endBackgroundTask:包裝。這樣就在程序突然切入后臺的時候保護(hù)了這些任務(wù)不被中斷。
2、當(dāng)你的應(yīng)用程序委托applicationDidEnterBackground:方法被調(diào)用時再啟動任務(wù)
中的兩個方法必須是一一對應(yīng)的,endBackgroundTask:方法告訴系統(tǒng)任務(wù)已經(jīng)完成,程序在此時可以被終止。由于應(yīng)用程序只有有限的時間去完成后臺任務(wù),你必須在超時或系統(tǒng)將要終止這個程序之前調(diào)用這個方法。為了避免被終止,你也可以在一個任務(wù)開始的時候提供一個expiration handler和endBackgroundTask:方法。(可以查看backgroundTimeRemaining屬性來確定還剩多少時間)。
一個程序可以同時提供多個任務(wù),每當(dāng)你啟動一個任務(wù)的時候,beginBackgroundTaskWithExpirationHandler:方法將返回一個獨(dú)一無二的handler去識別這個任務(wù)。你必須在endBackgroundTask:方法中傳遞相同的handler來終止該任務(wù)。
- Listing 4-2 Starting a background task at quit time
 - - (void)applicationDidEnterBackground:(UIApplication *)application
 - {
 - UIApplication* app = [UIApplication sharedApplication];
 - bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
 - [app endBackgroundTask:bgTask];
 - bgTask = UIBackgroundTaskInvalid;
 - }];
 - // Start the long-running task and return immediately.
 - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
 - 0), ^{
 - // Do the work associated with the task.
 - [app endBackgroundTask:bgTask];
 - bgTask = UIBackgroundTaskInvalid;
 - });
 - }
 
上述例子中,bgTask變量是一個類的成員變量,存儲著指向該后臺任務(wù)標(biāo)示的指針。
在expriation handler中,可以添加關(guān)閉任務(wù)所需的代碼。盡管如此,加入的代碼不能執(zhí)行太長的時間,當(dāng)expriation handler被調(diào)用的時候,該程序已經(jīng)非常接近被關(guān)閉,所以只有極短的時間來清除狀態(tài)信息并終止任務(wù)。
安排Local Notification的傳遞
UILocalNotification類提供了一種方法來傳遞local notifications。和push notifications需要設(shè)置remote server不同,local notifications 在程序中安排并在當(dāng)前的設(shè)備上執(zhí)行。滿足如下條件可以使用該能力:
1、一個基于時間的程序,可以在將來特定的時間讓程序post 一個alert,比如鬧鐘
2、一個在后臺運(yùn)行的程序,post 一個local notification去引起用戶的注意
為了安排local notification 的傳遞,需要創(chuàng)建一個UILocalNotification的實例,并設(shè)置它,使用UIApplication類方法來安排它。Local notification對象包含了所要傳遞的類型(sound,alert,或者badge)和時間何時呈現(xiàn)) 。UIApplication類方法提供選項去確定是立即傳遞還是在指定的時間傳遞。
- Listing 4-3 Scheduling an alarm notification
 - - (void)scheduleAlarmForDate:(NSDate*)theDate
 - {
 - UIApplication* app = [UIApplication sharedApplication];
 - NSArray* oldNotifications = [app scheduledLocalNotifications];
 - // Clear out the old notification before scheduling a new one.
 - if ([oldNotifications count] > 0)
 - [app cancelAllLocalNotifications];
 - // Create a new notification.
 - UILocalNotification* alarm = [[[UILocalNotification alloc] init] autorelease];
 - if (alarm)
 - {
 - alarm.fireDate = theDate;
 - alarm.timeZone = [NSTimeZone defaultTimeZone];
 - alarm.repeatInterval = 0;
 - alarm.soundName = @"alarmsound.caf";
 - alarm.alertBody = @"Time to wake up!";
 - [app scheduleLocalNotification:alarm];
 - }
 - }
 
(可以最多包含128個 local notifications active at any given time, any of which can be configured to repeat at a specified interval.)如果在調(diào)用該notification的時候,程序已經(jīng)處于前臺,那么application:didReceiveLocalNotification:方法將取而代之。
小結(jié):關(guān)于詳解在IOS后臺執(zhí)行的內(nèi)容介紹完了,希望本文對你有所幫助!















 
 
 








 
 
 
 