對于客戶端開發(fā)來說韵卤,同網絡請求相關的代碼總是相對復雜的,因為它牽扯到request的序列化啡专,response的解析,繁多的異常錯誤處理和多線程的調度……
很多iOS的開發(fā)人員制圈,或許不知道NSURLConnection(現在官方已經不鼓勵使用)们童、NSURLSession、NSURLSessionTask……但肯定知道AFNetworking(OC)與Alamofire(Swift)鲸鹦。在iOS端幾乎所有與網絡請求相關的處理慧库,都會牽扯到這兩個開源庫。公司的網絡層馋嗜,會根據實際的業(yè)務的需要齐板,在AFNetworking/Alamofire的基礎上繼續(xù)封裝。
這里說一點兒題外話,之前我還不是特別理解為何要做過度封裝甘磨,最近有所感受橡羞,主要原因有兩點:
- 同一套底層設計,由于不同的業(yè)務需求會表現出明顯的差異济舆,因此需要在第三庫的基礎上卿泽,進一步封裝成方便業(yè)務層代碼調用的API。
- 無法保證第三庫會有持續(xù)的維護滋觉,它可能會出現性能問題签夭、bug或者版本的更迭。若自己封裝過一層椎侠,那么在這些情況出現的時候第租,只需要對中間層進行維護,讓業(yè)務層無感知我纪。
先來想一想如果我們自己要寫一個網絡請求的庫慎宾,它需要具備哪些功能:
- request的序列化:URL的拼接,參數解析并拼接成query宣羊,根據網絡請求的方法(get璧诵、post、head……)生成正確的request
- 監(jiān)聽網絡狀況:當前的網絡環(huán)境(WiFi還是cellular)仇冯,網絡安全狀況(證書問題)
- 監(jiān)聽網絡請求中的各個環(huán)節(jié)之宿。例如:請求是否完成,客戶端是否收到數據苛坚,是否發(fā)生錯誤等
- 針對與網絡請求中的各個環(huán)節(jié)比被,要有相應的默認處理,最重要的是對各種異常錯誤的處理泼舱,保證到最后用戶拿到的是想要的數據
- 對response的解析等缀,各種格式JSON、XML等
針對第3點娇昙,看一看在AFNetworking是怎么實現尺迂。
這里不對代碼中的一些細節(jié)作解釋,若想一行一行地深究細節(jié)冒掌,推薦涂耀輝 - 簡書大神寫的AF解讀系列噪裕。
AFNetworking是圍繞著NSURLSession做了一層封裝,先來看看NSURLSession的作用:
With the NSURLSession API, your app creates one or more sessions, each of which coordinates a group of related data transfer tasks. For example, if you are writing a web browser, your app might create one session per tab or window, or one session for interactive use and another session for background downloads. Within each session, your app adds a series of tasks, each of which represents a request for a specific URL (following HTTP redirects if necessary).
NSURLSession
同我們在做web開發(fā)時的session聯(lián)系了起來股毫,它是用來管理一系列的NSURLSessionTask膳音,而我們的每一個request都會經由NSURLSession生成一個NSURLSessionTask。這樣就明白了铃诬,NSURLSession就是用來管理一系列request的祭陷,至于如何管理苍凛,就不用管啦。事實上兵志,蘋果發(fā)布的NSURLSession的概念應該是從AF2.x借鑒過來的醇蝴。當年的AF2.x是圍繞著NSURLConnection做封裝,結合NSOperation實現了網絡請求的資源分配和線程調度想罕。而如今哑蔫,這一切都被內置的NSURLSession給實現了。而一個NSURLSession需要一個delegate弧呐,并實現一系列的NSURLSessionDelegate方法,這些方法就是在請求的各個環(huán)節(jié)被系統(tǒng)回調的嵌纲,也就對應著第4個功能點俘枫。
在AF中,最核心的類是AFURLSessionManager逮走,幾乎所有同網絡請求相關的核心操作都是由這個類完成或是調度完成的鸠蚪。我們常用的AFHTTPSessionManager是這個類的子類,子類只是封裝了一下父類中的方法师溅,其實并沒有干實事茅信。而在這個類中,就維護了一個NSURLSession類型的session墓臭。
先看看使用AF如何發(fā)出一個網絡請求:
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"example.com"] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
首先需要初始化一個AFURLSessionManager蘸鲸,也就在這個時候,內部的初始化函數創(chuàng)建了一個NSURLSession窿锉,而這個session的delegate也就是維護它的manager:
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
隨后酌摇,我們通過這個manager發(fā)出一個網絡請求:
[manager GET:@"schema" parameters:nil progress:nil success:nil failure:nil];
在這段代碼的背后,manager會通過傳進來的URL和parameters序列化一個request嗡载,更進一步窑多,manager中維護的session會為每一個request生成一個dataTask:
dataTask = [self.session dataTaskWithRequest:request];
最后只需要[dataTask resume]
就可以觸發(fā)這次網絡請求任務了。
那么最關鍵的問題就變成了manager如何管理這些dataTask了洼滚。
我個人覺得埂息,這就是AF中最關鍵的地方了:manager會為每一個dataTask配置一個AFURLSessionManagerTaskDelegate。
AFURLSessionManagerTaskDelegate中只不過是將NSURLSessionDelegate中的三個方法提取了出來遥巴,提供了完整的實現千康,這三個方法的簽名是:
- URLSession:task:didCompleteWithError:
- URLSession:dataTask:didReceiveData:
- (void)URLSession:downloadTask:didFinishDownloadingToURL:
AFURLSessionManager本可以實現NSURLSessionDelegate的全部接口,那么為什么偏偏要把這三個方法提取出來挪哄,提供另外一個代理接口吧秕?主要原因可能是這樣的:每一個dataTask都是很不一樣的,若對一個session管理的所有dataTask都設置成同一個delegate(即manager)迹炼,那么用戶如何取到不同dataTask回調回來的不同信息砸彬?因此颠毙,我們需要把那些會針對不同dataTask返回不同數據信息的回調函數單獨抽出來(因為不同dataTask返回的數據信息是不一樣的),為每一個dataTask都配置一個delegate砂碉,而對于一些公共環(huán)節(jié)的處理則交給manager做統(tǒng)一處理蛀蜜。
感覺干看上面這段話,挺難懂的增蹭,貼圖說明:
到這里為止滴某,manager已經實現了對每一個dataTask的監(jiān)聽功能。當然我的解釋很簡短滋迈,AF中的細節(jié)實現還有大大的可以深究的地方(大神寫的代碼確實牛逼)霎奢,但是細節(jié)方面需要粘貼大段的代碼,筆者不是提別喜歡這種方式饼灿,強烈推薦涂耀輝 - 簡書大神的《AFNetworking到底做了什么》系列幕侠。
本文章后續(xù)會有增修。