MKNetworkKit: 網絡處理又一利器

沒有認識MK之前,即便ASI已經不再更新,也沒有啓用ASI。因爲ASI對於網絡的處理更偏向於底層,適合針對各種情形的擴展。

但是,今天我要開始使用 MKNetworkKit了,項目在github上,使用說明在作者的主頁上。

之所以推薦這個項目,除了它列舉的功能之外,更重要的在於它完全是MVC的設計思想。

相對於代碼,背後的設計思想更讓人值得回味。

另外,已經有一篇中文翻譯(如下),各位趕緊使用吧~

轉自: http://t.cn/zQJWX2S

iOS Framework: Introducing MKNetworkKit (MKNetworkKit介紹,入門,翻譯)

這片文章也有塞爾維亞-克羅地亞語(由Jovana Milutinovich翻譯)和日語(由@noradaiko翻譯)

如果有個一個網絡庫能夠自動的爲你處理cache該有多好啊。

如果有一個網絡庫能夠在設備離線的時候自動的記住用戶的操作該有多酷啊。

當你離線的時候,你喜歡了一條微博或者把一條新聞標記爲已讀,然後網絡庫會在設備連網後自動執行這些操作,並且還不用寫一行多餘的代碼。下面我們就介紹MKNetworkKit可以做到這些。

 

什麼是 MKNetworkKit?

MKNetworkKit 是一個用objective-c寫的網絡庫,具有無縫連接,基於block,ARC支持以及易用等特點。

MKNetworkKit的靈感來自於其他兩個流行的網絡庫:ASIHTTPRequest和AFNetworking,結合了兩個庫的共同特點,並且有一些新的特性。除此之外,MKNetworkKit可能會比其他網絡庫而言爲了代碼的清晰性,要求你寫一丁點多的代碼。用了MKNetworkKit,你很難寫出醜陋的網絡代碼。

特性

超輕量級

The complete kit is just 2 major classes and some category methods. This means, adopting MKNetworkKit should be super easy.

完整的庫只有2個主類和一些category方法。也就是說,採用MKNetworkKit是非常容易的。

整個應用共享單一隊列

嚴重依賴網絡連接的應用應該優化他們的網絡併發連接數。十分不幸的是,現在還沒有網絡庫可以正確的完成這些功能。讓我來舉個例子說明如果你不去優化或者控制網絡的併發連接數會發生什麼。

Let’s assume that you are uploading a bunch of photos (think Color or Batch) to your server. Most

假如你正在上傳一系列的圖片(比如Color和 Batch)到服務器。大多數的移動網絡(3G)不允許一個給定的IP地址超過兩個的併發的http請求。這就是說,在你的設備上,3G網絡下,你不能同時打開超過兩個的併發HTTP請求。EDGE網絡就更差了,大多數情況下你甚至不能打開超過一個的連接。這個限制在傳統的wifi的情況下是相當高的(6個)。但是,你的設備並不總是連接到Wifi下,你應該爲受限制的網絡環境考慮。在最普通的情況下你的設備都是連接到3G網絡,就是說你被限制同時只能上傳2張圖片。現在問題的關鍵不是上傳兩張圖片時很慢,而是當你上傳圖片時再打開一個新的View,這個view在加載圖片的縮略圖的時候。當你不去通過app控制正確的隊列大小時,你的縮略圖加載操作就會超時,這種現象可不是正確的。正確的做法是把縮略圖的加載排好優先級,或者等待上傳完成後再加載縮略圖。這就要求你的app有一個全局的隊列。MKNetworkKit自動的保證你的app的每一個隊列的實例使用單一的共享隊列。雖然MKNetworkKit自己不是單例的,但是他的共享隊列是。

正確的顯示網絡連接的標誌

現在有許多第三方的類使用記錄網絡調用的次數的方式來控制網絡鏈接標誌的顯示。但MKNetworkKit使用的是單一共享隊列原則來控制網絡標誌的顯示,即通過KVO註冊共享隊列里正在運行的操作。作爲一個開發者,媽媽再也不用擔心手動設置網絡連接標誌的問題了。

  if (object == _sharedNetworkQueue && [keyPath isEqualToString:@"operationCount"]) {

        [UIApplication sharedApplication].networkActivityIndicatorVisible =
        ([_sharedNetworkQueue.operations count] = 0);
    }

Auto queue sizing 自動隊列大小

我們繼續前一個討論。我說了移動網絡不允許同時超過兩個併發網絡請求。所以當3G網絡時你的隊列大小應該設爲2,MKNetworkKit自動爲你處理這些。當網絡進入3G/EDGE/GPRS時,它會更改併發連接的數目爲2,並且在連接wifi時自動設置回6。有了這個特性,你會看到當你通過3G網絡從服務器加載縮略圖(或者多個小的請求)時有巨大的性能提升

Auto caching 自動緩存

MKNetworkKit可以自動緩存你的所有GET請求。當你發起同樣一個請求的時候,MKNetworkKit會立即用緩存的響應(如果有)來調用完成方法,也會向遠程服務器發出一個請求。當服務器的數據返回以後,會用取到的新的相應再次調用完成方法。這就是說,你不需要手動的處理緩存,你需要做的,就只有調用這個方法:

[[MKNetworkEngine sharedEngine] useCache];

當然,你也可以在你的MKNetworkEngine子類中定製緩存的目錄和內存緩存的消耗量。

Operation freezing 操作凍結

用了MKNetworkKit,你就擁有了凍結網絡操作的能力。當你凍結一個操作的時候,爲了防止網絡連接丟失,操作會被自動序列化並且在設備聯網之後自動執行。想想微博客戶端的草稿箱功能。

當你發送一條微博時,把這個網絡操作標記爲凍結,MKNetworkKit會自動的處理凍結和解凍工作!然後這條微博你不用寫一行代碼就會自動稍後發送。你可以使用在比如標記一條微博爲喜歡或者從google reader客戶端分享一篇文章或者添加一個連接到instpaper等類似場景中。

Image Caching 圖片緩存

MKNetworkKit可以無縫的緩存縮略圖。通過重寫幾個方法,你可以設置緩存多少張圖片在內存緩存以及緩存的目錄。重寫這些方法完全是可選的。

Performance 性能

一個詞:速度。MKNetworkKit的緩存是無縫的,就像NSCache一樣工作,除此之外,當內存警告的時候,內存緩存會寫入到緩存目錄中。

Full support for Objective-C ARC 全面支持Ojbective-C ARC

一般你會爲新的項目選擇一個新的網絡庫。MKNetworkKit不是爲了取代你現有的(當然你也可以取代,但是可能會很麻煩)。在新的項目中,你會很想要使用ARC功能。MKNetworkKit可能是唯一全面支持ARC的網絡庫。基於ARC管理的代碼速度可能比非ARC的代碼是數量級的差距。

How to use 如何使用

OK,不自吹了,讓我們來看看如何使用這個framework。

Adding the MKNetworkKit  添加MKNetworkKit

  1. 拖拽MKNetworkKit的目錄到你的工程裏
  2. 添加 CFNetwork.Framework, SystemConfiguration.framework and Security.framework 依賴庫
  3. 在PCH文件中包含 MKNetworkKit.h
  4. 刪除 NSAlert+MKNetworkKitAdditions.h如果你開發的是iOS程序。
  5. 刪除 UIAlertView+MKNetworkKitAdditions.h如果你開發的是Mac程序。

就是這樣子,僅僅5個核心文件,媽媽再也不用擔心網絡請求了。

 MKNetworkKit 中的類

  1. MKNetworkOperation
  2. MKNetworkEngine
  3. 混雜的幫助類 (蘋果的 Reachability) 以及一些cateogory方法。

我相信簡潔就是美。蘋果已經把實際網絡操作的繁重工作都做了,所以第三方的網絡庫應該提供的就是一個優雅的隊列和可選的緩存。我堅信,任何第三方的庫都應該少於10個類(不管是網絡庫還是UIkit的替代庫)。多過10個,就是過度。Three 20庫就是一個臃腫的例子,ShareKit也是,也許這兩個庫不錯,但是仍然是龐大而且臃腫的。ASIHttpRequest或者AFNetworking比RESTKit而言都是輕量級的,JSONKit比起TouchJSON(或者其他任何TouchCode庫)就是輕量級的。也許就我一個人喜歡這樣子,但我就是不能忍受我的app裏有第三方的庫比我的代碼還要多。

巨大的庫的問題就在於很難去理解他的內在的工作機制,而且很難去根據自己的需求定製。我的添加IAP的MKStoreKit庫就是超級易用的而且我相信MKNetWorkKit也是如此。使用MKNetworkKit,你只需要知道MKNetworkOperation 和 MKNetworkEngine兩個類所暴露出來的方法。MKNetworkOperation類似於ASIHttpRequest類,是一個NSOperation的子類並且封裝了請求相應類。你需要爲你的app的每一個網絡請求創建一個MKNetworkOperation。

MKNetworkEngine是一個假單例的類,負責管理你的app的網絡隊列。因此,簡單的請求時,你應該直接使用MKNetworkEngine的方法。在更爲複雜的定製中,你應該集成並子類化它。每一個MKNetworkEngine的子類都有他自己的Reachability對象來通知服務器的連通情況。你應該考慮爲你的每一個特別的REST服務器請求子類化MKNetworkEngine。因爲是假單例模式,每一個單獨的子類的請求,都會通過僅有的隊列發送。

你可以在應用的delegate裏面retain MKNetworkEngine的實例,就像CoreDatademanagedObjectContext類。當你使用MKNetworkKit的時候,你創建一個MKNetworkEngine的子類來從邏輯上分組你的網絡請求。就是說,Yahoo相關的請求都在一個類中,Facebook相關的請求都在另一個類中。我們會看到3個不同的使用庫的例子。

 

例1:

讓我們創建一個YahooEngine來獲取雅虎財經的貨幣交換率。

第一步: 創建一個YahooEngine 繼承自 MKNetworkEngine. MKNetworkEngine的初始化方法需要主機名和自定義的header(如果有)。自定義的頭是可選的而且可以爲nil,如果你正在編寫自己的REST服務器(不是現在的情況)你可能需要考慮添加客戶端版本以及其他類似客戶端標識的元數據。

    NSMutableDictionary *headerFields = [NSMutableDictionary dictionary];
    [headerFields setValue:@"iOS" forKey:@"x-client-identifier"];

    self.engine = [[YahooEngine alloc] initWithHostName:@"download.finance.yahoo.com"
                       customHeaderFields:headerFields];

注意,雅虎不需要你發送x-client-identifider在header裏面,所以上面的例子僅僅是爲了演示這個功能特性。

既然代碼都是ARC的,那麼是否對Engine的實例進行strong引用取決於開發者。

當你創建一個MKNetworkEngine子類,Reachability的會自動實現。這樣當服務器宕機或者其他不可預料的情況下時,請求會被自動的隊列或者凍結。想要獲取更多關於凍結操作的信息,請閱讀後面凍結操作相關的章節。

步驟 2:設計 Engine 類 (分離考慮)

讓我們開始在YahooEngine裏面編寫獲取貨幣交換率的代碼。Engine裏面的方法會在ViewController裏面調用。一個好的設計實踐就是確保你的Engine類不會暴露URL和http的請求頭給調用的類。你的View層不應該知道url路徑以及需要的參數,就是對應着YahooEngine裏面的貨幣及以及貨幣單位。返回的數據可以爲double值表示貨幣交換率或者時間戳。因爲請求都是異步的,你應該在block裏面返回這些值,比如:

-(MKNetworkOperation*) currencyRateFor:(NSString*) sourceCurrency
                   	    inCurrency:(NSString*) targetCurrency
			  onCompletion:(CurrencyResponseBlock) completion
        	               onError:(ErrorBlock) error;

MKNetworkEngine父類定義了block的類型如下:

typedef void (^ProgressBlock)(double progress);
typedef void (^ResponseBlock)(MKNetworkOperation* operation);
typedef void (^ErrorBlock)(NSError* error);

In our YahooEngine, we are using a new kind of block, CurrencyResponseBlock that returns the exchange rate. The definition looks like this.

在YahooEngine裏面,我們使用一種新的block,CurrencyResponseBlock來返回交換率的值,定義如下:

typedef void (^CurrencyResponseBlock)(double rate);

在任何其他app裏面,你都應該丁一你的block的方法類似於CurrencyResponseBlock這樣的來給viewController傳回值。

步驟 3: 處理返回數據

處理數據,就是轉換你從服務器取回的數據,不論時Json還是XML或者二進制plist,都應該在Engine裏面完成。再次聲明,不要讓你的Viewcontroller做這件事,你的Engine應該僅僅發回正確的model對象或者model對象的數組。在Engine中轉換json或者xml。再次聲明,爲了確保分類原則,你的viewController應該不知道從json裏面獲取單個元素的key。
這就是設計Engine的準則了,大部分的網絡庫不強迫你遵循一些接口分離的原則,但我do,因爲我愛你。:)

步驟4: 方法實現

現在我們討論一下計算貨幣交換率的方法的具體實現細節

從yahoo獲取交換率,就是一個簡單的GET請求,我寫了一個宏來定義獲取貨幣率的url請求的格式

#define YAHOO_URL(__C1__, __C2__) [NSString stringWithFormat:@"d/quotes.csv?e=.csv&f=sl1d1t1&s=%@%@=X", __C1__, __C2__]

你寫的方法應該按照如下順序執行:

  1. 準備好url和請求參數
  2. 創建一個請求的MKNetworkOperation對象.
  3. 設置方法參數
  4. 添加完成和錯誤處理方法,(完成方法就是你處理你響應到相應的model類的地方)
  5. 可選的,還有請求操作的進度指示。(或者在viewController裏面處理)
  6. 如果你的操作時下載文件,那麼設置一個下載的流(通常是一個文件)給他,這也是可選的
  7. 當請求操作完成時,處理結果並且調用block方法來向調用方法返回數據。

下面是實例代碼:

        MKNetworkOperation *op = [self operationWithPath:YAHOO_URL(sourceCurrency, targetCurrency)
                                                  params:nil
                                             httpMethod:@"GET"];

    [op onCompletion:^(MKNetworkOperation *completedOperation)
     {
         DLog(@"%@", [completedOperation responseString]);

		 // do your processing here
         completionBlock(5.0f);

     }onError:^(NSError* error) {

         errorBlock(error);
     }];

    [self enqueueOperation:op];

    return op;

上面的代碼構造url然後創建了MKNetworkOperation,設置完完成方法和錯誤處理方法之後就把他通過調用父類的enqueueOperation方法放入隊列中,並且返回一個引用。你的viewController應該持有這個操作並且在viewController彈出視圖體系的時候取消網絡操作。所以你可以在viewDidAppear裏面調用engine的方法,並且在viewWillDisappear裏面取消操作。取消操作會釋放隊列從而使隊列繼續其他操作(記住,在移動網絡中僅僅允許2個併發請求,取消不在需要的操作可以提升性能,加速你的app)

你的ViewController也可以(可選的)添加進度處理,並且更新界面,下面就是如何做

    [self.uploadOperation onUploadProgressChanged:^(double progress) {

        DLog(@"%.2f", progress*100.0);
        self.uploadProgessBar.progress = progress;
    }];

MKNetworkEngine也可以很方便的根據url來創建請求,所以下面代碼可以寫爲:

        MKNetworkOperation *op = [self operationWithPath:YAHOO_URL(sourceCurrency, targetCurrency)];

注意請求的url是自動在初始化engine的代碼裏面加上提供的域名。

創建一個POST,DELETE或者PUT的方法一樣簡單,就是更換http方法的參數。MKNetworkEngine也有更多給你多的便利指出比如讀取header文件

示例 2:

上傳圖片到服務器(比如TwitPic)

現在我們看一個如何上傳圖片到服務器的例子,上傳圖片明顯需要操作把data編碼爲表格請求。MKNetworkKit遵循一系列的類似ASIHttpRequest的請求。

你可以調用addFile:forKey:在MKNetworkOperation裏面來添加文件附件作爲請求表格的數據。很簡單。

MKNetworkOperation 也有一個簡單的方法來從NSData指針中添加圖片,就是調用addData:forKey: 方法來直接從NSData上傳圖片。 (比如直接從相機添加圖片).

例子 3:

下載文件到本地目錄(緩存)

用MKNetworkKit從遠程服務器下載文件並且保存到用戶的iPhone的某個地方是超級簡單的
僅僅需要設置MKNetworkOperation的outputStream即可:

[operation setDownloadStream:[NSOutputStream
	outputStreamToFileAtPath:@"/Users/mugunth/Desktop/DownloadedFile.pdf"
			  append:YES]];

你可以設置多個輸出流到單個請求操作中來保存相同的文件到不同的位置。(比如你想既保存到緩存目錄又想保存到工作目錄)

例子 4:

圖片縮略圖緩存。

爲了下載圖片,你可能需要提供絕對的url而不是一個相對目錄

MKNetworkEngine有一個便捷的方法。只需要調用operationWithURLString:params:httpMethodMKNetworkEngine 來用絕對url創建個一個請求,MKNetworkEngine是智能的,它會合並多個get請求到同一個url並且當操作完成時通知所有的block,這極大的提高了獲取縮略圖的速度。

子類 MKNetworkEngine 並且重寫圖片緩存目錄和緩存級別,如果你不想自定義這兩個參數,你只需要調用MKNetworkEngine的方法來下載圖片即可,實際上我也比較推薦這種方法,。

緩存操作

MKNetworkKit默認緩存所有的請求。你需要做的僅僅是在你的Engine上打開緩存。當GET請求執行時,如果響應之前被緩存過,你的完成處理代碼幾乎是立即會被調用並傳遞緩存過的相應,要知道請求是否被緩存,調用isCachedResponse方法,比如下面

    [op onCompletion:^(MKNetworkOperation *completedOperation)
     {
         if([completedOperation isCachedResponse]) {
             DLog(@"Data from cache");
         }
         else {
             DLog(@"Data from server");
         }

         DLog(@"%@", [completedOperation responseString]);
     }onError:^(NSError* error) {

         errorBlock(error);
     }];

凍結操作

可以確定的, MKNetworkKit的最又去的功能就是內置的凍結操作的功能。所有你需要做的就是設置請求操作爲freezable,不用費任何力氣!

[op setFreezable:YES];

凍結的操作會在網絡不通時自動的被序列化並且上線後自動執行。想一下在離線時標記一條微博爲喜歡然後稍後上線之後操作會自動執行。凍結的操作也會被持久化到磁盤當app進入後臺之後。並且會在稍後app恢復之後自動執行。

 MKNetworkOperation中的便捷方法

MKNetworkOperation 提供了一些如下便捷方法來方便你格式化你的響應數據

  1. responseData
  2. responseString
  3. responseJSON (Only on iOS 5)
  4. responseImage
  5. responseXML
  6. error

從網絡請求獲取響應很方便,當返回格式錯誤時,這些方法返回nil,比如試圖從響應爲html中獲取image會返回nil,唯一可以確保返回正確結果的方法時responseData,如果你確定返回類型,可以用其他方法。

方便宏定義

宏定義Dlog和ALog是我不知羞恥的從stackoverflow上偷的,而且找不到源文件了,如果時你寫的,請讓我知道

關於GCD

我故意沒用gcd,因爲網絡請求需要隨時停止和安排優先級,gcd打給你然比NSOperationQueue更高效,但是不能做到上面兩點。我不推薦在網絡請求的隊列中使用gcd。

關於文檔

頭文件都加了註釋,而且我正在嘗試整理,於此同事,你可以隨時玩弄代碼。

源代碼

源代碼和Demo工程都在Github,連接:MKNetworkKit on Github

關於新增特性

請不要email添加新特性,最好的方式是在github上添加issue

許可證

MKNetworkKit is licensed under MIT License

All of my source code can be used free of charge in your app, provided you add the copyright notices to your app. A little mention on one of your most obscure “about” page will do.

Attribution free licensing available upon request. Contact me at [email protected]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章