1. 程式人生 > >關於iOS的後臺下載和斷點續傳,說一說自己的理解

關於iOS的後臺下載和斷點續傳,說一說自己的理解

http://blog.csdn.net/openglnewbee/article/details/53887308


首先,後臺下載和斷點續傳是兩件事;這裡放在一起說是為了圖個方便,實際二者在技術實現上沒有什麼關聯。

目前我們的下載實現一般都是基於nsurlsession和iOS7+的,所以我們這裡不考慮iOS6和以前的老系統,主要技術實現也是基於nsurlsession.


先說後臺下載:

1. 在沒有特別關注的情況下,可能很多開發者使用afnetworking下載的姿勢並沒有考慮到後臺下載這一塊。在預設情況(不做特別設定)下,afnetworking並未啟用backgroundsession, 因此很可能你的app是不支援後臺下載的。

2. nsurlsession對後臺下載的支援主要通過 backgroundSessionConfiguration/backgroundSessionConfigurationWithIdentifier 型別的session來支援;通過該session來呼叫下載方法,實現幾個下載相關的回撥方法,也就可以實現基本的目的:應用切換到後臺時仍然在下載(但下載完成後得不到回撥和提醒)。

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
                              didFinishDownloadingToURL:(NSURL *)location;

@optional
/* Sent periodically to notify the delegate of download progress. */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
                                           didWriteData:(int64_t)bytesWritten
                                      totalBytesWritten:(int64_t)totalBytesWritten
                              totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;

/* Sent when a download has been resumed. If a download failed with an
 * error, the -userInfo dictionary of the error will contain an
 * NSURLSessionDownloadTaskResumeData key, whose value is the resume
 * data. 
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
                                      didResumeAtOffset:(int64_t)fileOffset
                                     expectedTotalBytes:(int64_t)expectedTotalBytes;


3. 如果需要下載完成後得到回撥和提醒,或者更高階一點(應用程式被系統幹掉等情況,還能夠通過下載任務拉起併發送回調和提醒,而且系統在後臺會生成一個快照,按home鍵檢視時就彷彿這個應用被啟動過一樣。這裡的內容是根據蘋果官方答覆和相關文件得到,系統幹掉的場景筆者並未模擬),我們就需要進行額外的設定了。

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
//  這裡返回了session的identifier和completionBlock;其中的identifier可以用於匹配session,而completionBlock我們需要儲存起來,到後面一個步驟需要呼叫之。
}

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
// 這裡需要執行上一步儲存的completionBlock:

根據蘋果員工的回覆,這裡蘋果需要用這個block實現兩個功能: 

a. 系統後臺生成快照

b. 釋放阻止應用掛起的斷言(讓應用繼續在後臺執行,節約系統資源)

  1. The system does two things in that completion handler:

    • It snapshots your UI for the benefit of the multitasking switcher

    • It releases the assertion that was preventing your app from being suspended

https://forums.developer.apple.com/thread/69825

}


在一次後臺下載中方法的執行時序為:a.handleEventsForBackgroundURLSession ->b. URLSession: downloadTask: didFinishDownloadingToURL->c. URLSessionDidFinishEventsForBackgroundURLSession.

建議傳送本地通知的動作統一放在b中,目前看放在a和c中應該也是可以的。


再來說一說斷點續傳:

1. 從協議層面來說,斷點續傳的實現都是通過http頭裡面的range來實現的。對於以前asi的實現和基於nsurlconnection的實現來說,我們都是通過手動儲存當前大小然後填充到range的策略來實現斷點續傳功能的。

2. nsurlsession內建了對於斷點續傳的api支援,主要是通過resumedata和tmp中間檔案。在nsurlsession暫停時,其中間檔案的相關資訊被儲存到resumedata,我們只要通過這個resumedata,即可從之前的下載進度中恢復。也就是說1中的細節被封裝在api內部了。

目前來看,基於nsurlsession的斷點續傳主要有兩個問題需要考慮:

a. 程式退出(手動kill等)時需要自動儲存resumedata,後續應用起來時再恢復之。

b. iOS10裡面的resumedata的儲存有點問題,需要特別處理,可以參考此demo:https://github.com/HustHank/BackgroundDownloadDemo.


其他細節暫時不再展開了,後面有機會再深入的說一說,大體方案就是這樣,大家可以儘量參考蘋果官方文件。


一些參考文件:

https://my.oschina.net/iOSliuhui/blog/469276  斷點下載流程講解
http://www.cocoachina.com/ios/20160503/16053.html  斷點續傳方案 ??? 不斷儲存?方案不可取! 程式被殺時主動儲存!這裡的ios應該為小寫,被自動格式化成大寫了 
https://code.tutsplus.com/tutorials/ios-7-sdk-background-transfer-service--mobile-20595 後臺傳輸服務
http://www.jianshu.com/p/1211cf99dfc3 下載相關,BackgroundDownloadDemo作者的分享
蘋果論壇相關討論
https://forums.developer.apple.com/thread/14854
https://forums.developer.apple.com/thread/69825