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