MCDownloader(iOS下載器)說明書

示例

前言

很多iOS應用中都需要下載數據,并對這些下載的過程和結果進行管理,因此我才有了寫這個MCDownloader的想法。在IOS 文件下載器-MCDownloadManager這篇文章中,我使用GCD和集合來實現了這個功能,基本上也能滿足需求,這一部分的實現原理主要參考AFNetworking的源碼,有興趣的同學可以看看我寫的AFNetworking 3.0 源碼解讀系列。

但是本篇文章中講的MCDownloader的實現原理和上邊提到的不一樣,是基于NSOperation來實現的,可以說是我對SDWebImage源碼解讀的一些額外的擴展,同樣,有興趣的同學可以看看我寫的SDWebImage源碼解讀系列。

MCDownloader目前的版本是1.0.0,可以在這里下載https://github.com/agelessman/MCDownloader

功能

MCDownloader1.0.0版本提供了以下幾個功能:

  1. 多線程異步下載,支持自定義并發線程數。在上圖的示例圖中,采用的并發數為3
  2. 邊下載變保存,這一條是該下載器最重要的思想,數據被實時的保存在本地,同時支持斷點下載
  3. 十分方便的數據獲取能力,通過MCDownloadReceipt來對下載的數據進行抽象,幾乎所有的信息都能在MCDownloadReceipt中獲取
  4. 提供了下載進度,可以通過接口函數的block監聽下載進度和完成回調,也可以通過給MCDownloadReceipt綁定block來監聽block回調
  5. 支持顯示當前的下載速度
  6. 支持批量下載,批量取消功能
  7. 支持任務的暫停,取消,刪除功能
  8. 支持下載順序定制,先入先出或者后入先出
  9. 支持后臺和鎖屏下載

如何使用?

開啟下載

每一個下載任務的唯一標識是url,因此我們使用下邊的代碼開始一個下載任務:

 [[MCDownloader sharedDownloader] downloadDataWithURL:[NSURL URLWithString:url] progress:^(NSInteger receivedSize, NSInteger expectedSize, NSInteger speed, NSURL * _Nullable targetURL) {
                
  } completed:^(MCDownloadReceipt * _Nullable receipt, NSError * _Nullable error, BOOL finished) {
      NSLog(@"==%@", error.description);
  }];

可以在上邊的progress和completed中自定義處理方法。進度和完成的block回調都在主線程觸發。

暫?;蛉∠?/h4>

MCDownloader的暫停和取消功能是一樣的,由于內部下載是基于NSOperation實現的,因此每一個任務就是一個NSOperation,然后再把他們添加到隊列之中。當取消或者暫停一個任務后,在重新恢復下載,實際上會重新把該任務添加到隊列中,這一點一定要注意。

使用下邊的代碼來暫?;蛉∠粋€下載任務:

[[MCDownloader sharedDownloader] cancel:receipt completed:^{
            [self.button setTitle:@"Start" forState:UIControlStateNormal];
        }];

由于取消不是發生在主線程,所以需要一個completed來捕獲取消成功事件,然后在主線程調用。

移除數據

通過下邊的方法來移除保存在本地的數據:

 [[MCDownloader sharedDownloader] remove:receipt completed:^{
            [self.tableView reloadData];
        }];

獲取數據信息

可以通過下邊的代碼來獲取數據的一些信息,這些信息既可以在下載過程中獲取,也可以在下載完成后獲取。

MCDownloadReceipt *receipt = [[MCDownloader sharedDownloader] downloadReceiptForURLString:self.url];

通過上邊的代碼可以看出來,url被當做數據的唯一標識。在上圖的例子中,我們是在cell中更新下載進度的,為了防止cell的復用問題,我為每個receipt綁定了progress和complete回調block:

 __weak typeof(receipt) weakReceipt = receipt;
    receipt.downloaderProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSInteger speed, NSURL * _Nullable targetURL) {
        __strong typeof(weakReceipt) strongReceipt = weakReceipt;
        if ([targetURL.absoluteString isEqualToString:self.url]) {
            [self.button setTitle:@"Stop" forState:UIControlStateNormal];
            self.bytesLable.text = [NSString stringWithFormat:@"%0.1fm/%0.1fm", receivedSize/1024.0/1024,expectedSize/1024.0/1024];
            self.progressView.progress = (receivedSize/1024.0/1024) / (expectedSize/1024.0/1024);
            self.speedLable.text = [NSString stringWithFormat:@"%@/s", strongReceipt.speed ?: @"0"];
        }
        
    };
    
    receipt.downloaderCompletedBlock = ^(MCDownloadReceipt *receipt, NSError * _Nullable error, BOOL finished) {
        if (error) {
            [self.button setTitle:@"Start" forState:UIControlStateNormal];
            self.nameLabel.text = @"Download Failure";
        }else {
            [self.button setTitle:@"Play" forState:UIControlStateNormal];
            self.nameLabel.text = @"Download Finished";
        }
        
    };

取消全部下載和刪除全部數據

在某種場景下需要取消全部的下載,比如說監聽到網絡狀態變成4G時,需要詢問用戶是否繼續下載。又或者在需要清空緩存的時候:

[[MCDownloader sharedDownloader] cancelAllDownloads];

[[MCDownloader sharedDownloader] removeAndClearAll];

上邊說的這些功能,在demo中都有演示。

核心思想

由于下載功能不算是特別復雜的功能,所以我就簡單的說說內部的實現原理。

在代碼設計之初,我最先寫的類就是MCDownloadReceipt。通過它來對數據進行抽象封裝,我們先不管它是如何獲取的,只關心它需要暴露多少信息。這個類很簡單,我就不把代碼弄上來了,但是需要注意下邊幾點:

  • 這些屬性被設計成只讀屬性,表明只在該類中獲取數據,不要修改其中的數據
  • receipt需要保存在本地,我采用的是歸檔的方法進行持久化
  • 文件名要做MD5處理
  • 每一個receipt都綁定了一個狀態屬性

完成了模型的搭建后,就要處理最基本的下載任務了,MCDownloadOperation繼承自NSOperation,因此在MCDownloadOperation中我們就不需要關心線程的問題。我們在這個類中只做了下邊這幾件事:

  • 開啟下載任務,在開啟任務的start方法中,我做了一些保證任務開始的必要措施,有興趣的可以去看看源碼
  • 接受數據和寫入數據
  • 處理下載過程中和完成后的回調函數
  • 處理下載狀態
  • 關心如何取消任務

接下來就到了最核心的地方,如何把MCDownloadReceipt和MCDownloadOperation組合在一起,也就是MCDownloader的內容。MCDownloader是暴露出來最核心的模塊,在設計上主要考慮下邊幾件事情:

  • 需要一個單利對象來管理全局的情況
  • 支持設置一些跟下載相關的額外信息,比如超時時間,請求頭和并發數等等
  • 提供一些控制下載的常用方法,如何開始,取消,移除等等

綜上所述,這基本上是寫任何一個框架的基本流程,在編碼之前先進行設計。 另外,在使用的過程中,如果有任何問題,可以給我留言,如果有新的需求,也可以給我留言。

由于水平有限,難免會出現錯誤,如果發現后,還望能夠告知一聲。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 多線程 在iOS開發中為提高程序的運行效率會將比較耗時的操作放在子線程中執行,iOS系統進程默認啟動一個主線程,用...
    郭豪豪閱讀 2,616評論 0 4
  • 什么是進程? 進程是指在系統中正在運行的一個應用程序。 每個進程之間是獨立的,每個進程均運行在其專用且受保護的內存...
    珍此良辰閱讀 1,276評論 1 5
  • 多線程 什么是多線程?多線程就是一個進程中可以開啟多條線程,每條線程可以并行執行不同的任務,提高執行效率;一個基本...
    西風頌閱讀 878評論 1 16
  • 前言 Pthread,NSThread,GCD和NSOperation是iOS中多線程的四種實現方案。 一.進程和...
    小李龍彪閱讀 674評論 1 4
  • 文/桐陽媽 有媽媽問:桐陽媽,我家孩子自控力特別差,該怎么辦呢?作業不著急寫,上課不集中。早上不起床,反復催也不管...
    桐陽媽閱讀 804評論 3 18