下載任務(wù)的暫停和繼續(xù)在網(wǎng)絡(luò)請求中是一個(gè)常見需求,尤其在處理大文件或者長時(shí)間下載時(shí)。在 iOS 中,URLSession
提供了支持暫停和繼續(xù)下載的機(jī)制,稱為 后臺(tái)下載任務(wù)。
雖然上述的線程池設(shè)計(jì)可以用來調(diào)度任務(wù)并控制任務(wù)的暫停/繼續(xù),但網(wǎng)絡(luò)請求的暫停和繼續(xù)需要結(jié)合 URLSession
的 downloadTask(with:)
和 resume()
/ suspend()
方法來實(shí)現(xiàn)。
我們可以使用 URLSession
來創(chuàng)建一個(gè)下載任務(wù),利用其支持的暫停和恢復(fù)功能,來實(shí)現(xiàn)下載任務(wù)的暫停和繼續(xù)。
1. 使用 URLSession
實(shí)現(xiàn)下載暫停和繼續(xù)
要支持下載任務(wù)的暫停和繼續(xù),我們需要:
- 創(chuàng)建一個(gè)
URLSessionDownloadTask
。 - 使用
suspend()
方法暫停下載,使用resume()
方法繼續(xù)下載。 - 維護(hù)任務(wù)的狀態(tài),比如當(dāng)前的下載進(jìn)度,以便在恢復(fù)下載時(shí)可以從上次暫停的地方繼續(xù)。
2. 示例實(shí)現(xiàn)
下面是一個(gè)基于 URLSession
的下載器實(shí)現(xiàn),可以暫停、繼續(xù)和取消下載任務(wù):
import Foundation
/// 下載器
class ImageDownloader: NSObject, URLSessionDownloadDelegate {
private var downloadTask: URLSessionDownloadTask?
private var urlSession: URLSession!
private var resumeData: Data? // 用于恢復(fù)下載的數(shù)據(jù)
private var isDownloading = false
private var taskId: UUID?
override init() {
super.init()
// 創(chuàng)建 URLSession 配置并設(shè)置下載代理
let configuration = URLSessionConfiguration.default
urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}
/// 開始下載圖片
func startDownload(from url: URL, completion: @escaping (UIImage?) -> Void) -> UUID {
let taskId = UUID() // 為每個(gè)任務(wù)生成唯一的 ID
self.taskId = taskId
// 創(chuàng)建下載任務(wù)
downloadTask = urlSession.downloadTask(with: url)
// 開始下載
downloadTask?.resume()
isDownloading = true
return taskId
}
/// 暫停下載
func pauseDownload() {
downloadTask?.suspend()
isDownloading = false
}
/// 繼續(xù)下載
func resumeDownload() {
if let resumeData = resumeData {
// 恢復(fù)下載任務(wù)
downloadTask = urlSession.downloadTask(withResumeData: resumeData)
downloadTask?.resume()
isDownloading = true
} else {
// 如果沒有保存的暫停數(shù)據(jù),重新開始下載
downloadTask?.resume()
isDownloading = true
}
}
/// 取消下載
func cancelDownload() {
downloadTask?.cancel()
isDownloading = false
}
// URLSessionDownloadDelegate 方法:處理下載進(jìn)度和完成
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
guard let taskId = taskId else { return }
// 下載完成后的處理
DispatchQueue.main.async {
if let data = try? Data(contentsOf: location), let image = UIImage(data: data) {
print("Download completed for task \(taskId)")
// 調(diào)用 UI 更新代碼
} else {
print("Download failed for task \(taskId)")
}
}
}
// 下載進(jìn)度更新
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
print("Download progress: \(progress * 100)%")
}
// 下載暫停/恢復(fù)
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
print("Download resumed at offset \(fileOffset) of total \(expectedTotalBytes) bytes")
}
// 保存暫停數(shù)據(jù)
func urlSession(_ session: URLSession, task: URLSessionTask, didReceive resumeData: Data) {
self.resumeData = resumeData
}
}
3. 使用示例
let imageDownloader = ImageDownloader()
// 圖片 URL 示例
let url = URL(string: "https://example.com/largeimage.jpg")!
// 開始下載并獲取任務(wù) ID
let taskId = imageDownloader.startDownload(from: url) { image in
if let image = image {
print("Image downloaded successfully!")
} else {
print("Failed to download image.")
}
}
// 暫停下載
imageDownloader.pauseDownload()
// 繼續(xù)下載
imageDownloader.resumeDownload()
// 取消下載
imageDownloader.cancelDownload()
關(guān)鍵點(diǎn):
-
暫停和恢復(fù):通過
URLSessionDownloadTask
的suspend()
和resume()
方法,可以控制下載任務(wù)的暫停和恢復(fù)。resumeData
用于存儲(chǔ)暫停點(diǎn)的下載數(shù)據(jù),并且可以通過downloadTask(withResumeData:)
恢復(fù)下載任務(wù)。 -
下載進(jìn)度:
URLSessionDownloadTask
提供了進(jìn)度回調(diào)方法didWriteData
,我們可以通過totalBytesWritten
和totalBytesExpectedToWrite
來計(jì)算下載進(jìn)度,并顯示給用戶。 -
任務(wù)管理:每個(gè)任務(wù)都有唯一的
taskId
,用于標(biāo)識(shí)任務(wù),以便在后續(xù)操作(暫停、繼續(xù)、取消)時(shí)使用。
4. 關(guān)于暫停和繼續(xù)下載的注意事項(xiàng):
-
暫停和恢復(fù)時(shí)的恢復(fù)數(shù)據(jù):
URLSessionDownloadTask
在暫停后會(huì)自動(dòng)生成一個(gè)恢復(fù)數(shù)據(jù)(resumeData
),這個(gè)數(shù)據(jù)是下載文件的斷點(diǎn)信息。你需要存儲(chǔ)這個(gè)數(shù)據(jù),并在恢復(fù)時(shí)重新使用downloadTask(withResumeData:)
方法。 - 恢復(fù)點(diǎn)的有效性:并非所有下載任務(wù)都能順利恢復(fù)。只有部分下載任務(wù)(比如通過 HTTP 協(xié)議下載)支持?jǐn)帱c(diǎn)續(xù)傳,對于不支持的協(xié)議或任務(wù),可能需要重新開始下載。
-
取消下載:通過
cancel()
方法,可以取消下載任務(wù)。取消后不會(huì)生成恢復(fù)數(shù)據(jù),如果需要恢復(fù),必須重新開始下載。
總結(jié):
通過 URLSessionDownloadTask
的暫停和繼續(xù)機(jī)制,我們可以很方便地在 iOS 中實(shí)現(xiàn)下載任務(wù)的暫停、繼續(xù)和取消功能。使用 suspend()
和 resume()
方法結(jié)合 resumeData
,可以在下載中斷后從上次停止的位置恢復(fù)下載,提供更好的用戶體驗(yàn)。
結(jié)合《Swift 實(shí)現(xiàn):三級(jí)下載隊(duì)列》,是否可以實(shí)現(xiàn)優(yōu)雅一點(diǎn)的預(yù)下載呢?
http://www.lxweimin.com/p/82e88ffb1e67