iOS:ASIHttpRequest雖不更新,但仍值得詳細了解


一、使用ASIHTTPRequest可以很方便的進行一下操作:同步/異步方式下載數據,定義下載隊列,讓隊列中的任務按指定的並發數來下載(隊列下載必須是異步的),提交表單,文件上傳,處理cookie,設置代理,上下載進度條,重定向處理,請求與響應的GZIP,驗證與授權。

使用ASIHTTPRequest可以很方便的進行一下操作:

  • 同步/異步方式下載數據
  • 定義下載隊列,讓隊列中的任務按指定的並發數來下載(隊列下載必須是異步的)
  • 提交表單,文件上傳
  • 處理cookie
  • 設置代理
  • 上下載進度條
  • 重定向處理
  • 請求與響應的GZIP
  • 驗證與授權

等等,只要跟HTTP有關,只有你想不到的,沒有她做不到的~

配置方法:

  • ASIHTTPRequestConfig.h
  • ASIHTTPRequestDelegate.h
  • ASIProgressDelegate.h
  • ASICacheDelegate.h
  • ASIHTTPRequest.h
  • ASIHTTPRequest.m
  • ASIDataCompressor.h
  • ASIDataCompressor.m
  • ASIDataDecompressor.h
  • ASIDataDecompressor.m
  • ASIFormDataRequest.h
  • ASIInputStream.h
  • ASIInputStream.m
  • ASIFormDataRequest.m
  • ASINetworkQueue.h
  • ASINetworkQueue.m
  • ASIDownloadCache.h
  • ASIDownloadCache.m

iPhone 工程還需要:

  • ASIAuthenticationDialog.h
  • ASIAuthenticationDialog.m
  • Reachability.h (在External/Reachability 目錄下)
  • Reachability.m (在 External/Reachability 目錄下)

庫引用:

CFNetwork.framework

SystemConfiguration.framework

MobileCoreServices.framework

CoreGraphics.framework

和libz.dylib

另外,還需要libxml2.dylib(libxml2還需要設置連接選項-lxml2 和頭文件搜索路徑/usr/include/libxml2)

 

二、ASIHttpRequest創建和執行request

iOS開發中ASIHttpRequest如何創建和執行request,其中包括同步請求,異步請求,使用Block,使用隊列,取消異步請求等等內容。

創建NSOperationQueue,這個Cocoa架構的執行任務(NSOperation)的任務隊列。我們通過ASIHTTPRequest.h的源碼可以看到,此類本身就是一個NSOperation的子類。也就是說它可以直接被放到任務隊列中並被執行。

同步請求

同步請求會在當前線程中執行,使用error屬性來檢查結束狀態(要下載大文件,則需要設定downloadDestinationPath來保存文件到本地):

- (IBAction)grabURL:(id)sender { NSURL *url = [NSURL URLWithString:@"http://www.kenjiao.com"]; ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request startSynchronous]; NSError *error = [request error]; if (!error) { NSString *response = [request responseString]; } } 

同步請求會阻塞主線程的執行,這導致用戶界面不響應用戶操作,任何動畫都會停止渲染。

異步請求

下面是最簡單的異步請求方法,這個request會在全局的NSOperationQueue中執行,若要進行更復雜的操作,我們需要自己創建NSOperationQueue或者ASINetworkQueue,后面會講到。

- (IBAction)grabURLInBackground:(id)sender { NSURL *url = [NSURL URLWithString:@"http://www.kenjiao.com"]; ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request setDelegate:self]; [request startAsynchronous]; } - (void)requestFinished:(ASIHTTPRequest *)request { // Use when fetching text data 
       NSString *responseString = [request responseString]; // Use when fetching binary data 
       NSData *responseData = [request responseData]; } - (void)requestFailed:(ASIHTTPRequest *)request { NSError *error = [request error]; } 

使用block

在平台支持情況下,ASIHTTPRequest1.8以上支持block。

- (IBAction)grabURLInBackground:(id)sender { NSURL *url = [NSURL URLWithString:@"http://www.kenjiao.com"]; __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request setCompletionBlock:^{ // Use when fetching text data 
          NSString *responseString = [request responseString]; // Use when fetching binary data 
          NSData *responseData = [request responseData]; }]; [request setFailedBlock:^{ NSError *error = [request error]; }]; [request startAsynchronous]; } 

注意,聲明request時要使用__block修飾符,這是為了告訴block不要retain request,以免出現retain循環,因為request是會retain block的。

使用隊列

創建NSOperationQueue或者ASINetworkQueue隊列,我們還可以設定最大並發連接數:maxConcurrentOperationCount

- (IBAction)grabURLInTheBackground:(id)sender { if (![self queue]) { [self setQueue:[[[NSOperationQueue alloc] init] autorelease]]; [self queue].maxConcurrentOperationCount = 4; } NSURL *url = [NSURL URLWithString:@"http://www.kenjiao.com"]; ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request setDelegate:self]; [request setDidFinishSelector:@selector(requestDone:)]; [request setDidFailSelector:@selector(requestWentWrong:)]; [[self queue] addOperation:request]; //queue is an NSOperationQueue 
} - (void)requestDone:(ASIHTTPRequest *)request { NSString *response = [request responseString]; } - (void)requestWentWrong:(ASIHTTPRequest *)request { NSError *error = [request error]; } 

如果不設定selector,那么系統會使用默認的requestFinished: 和 requestFailed:方法

如果需要對隊列里面的每個request進行區分,那么可以設定request的userInfo屬性,它是個NSDictionary,或者更簡單的方法是設定每個request的tag屬性,這兩個屬性都不會被發送到服務器。

不要使用request的URL來區分每個request,因為URL可能會改變(例如重定向),如果需要使用request的URL,使用[request originalURL],這個將永遠返回第一個url。

對於ASINetworkQueue

ASINetworkQueue是NSOperationQueue的子類,提供更高級的特性(ASINetworkQueue的代理函數):

  • requestDidStartSelector
    當一個request開始執行時,這個代理函數會被調用。
  • requestDidReceiveResponseHeadersSelector
    當隊列中的request收到服務器返回的頭信息時,這個代理函數會被調用。對於下載很大的文件,這個通常比整個request的完成要早。
  • requestDidFinishSelector
    當每個request完成時,這個代理函數會被調用。
  • requestDidFailSelector
    當每個request失敗時,這個代理函數會被調用。
  • queueDidFinishSelector
    當隊列完成(無論request失敗還是成功)時,這個代理函數會被調用。

ASINetworkQueues與NSOperationQueues稍有不同,加入隊列的request不會立即開始執行。如果隊列打開了進度開關,那么隊列開始時,會先對所有GET型request進行一次HEAD請求,獲得總下載大小,然后真正的request才被執行。

向一個已經開始進行的ASINetworkQueue 加入request會怎樣?

如果你使用ASINetworkQueue來跟蹤若干request的進度,只有當新的request開始執行時,總進度才會進行自適應調整(向后移動)。ASINetworkQueue不會為隊列開始后才加入的request進行HEAD請求,所以如果你一次向一個正在執行的隊列加入很多request,那么總進度不會立即被更新。

如果隊列已經開始了,不需要再次調用[queue go]。

當ASINetworkQueue中的一個request失敗時,默認情況下,ASINetworkQueue會取消所有其他的request。要禁用這個特性,設置 [queue setShouldCancelAllRequestsOnFailure:NO]。

ASINetworkQueues只可以執行ASIHTTPRequest操作,二不可以用於通用操作。試圖加入一個不是ASIHTTPRequest的NSOperation將會導致拋出錯誤。

取消異步請求

取消一個異步請求(無論request是由[request startAsynchronous]開始的還是從你創建的隊列中開始的),使用[request cancel]即可。注意同步請求不可以被取消。

注意,如果你取消了一個request,那么這個request將會被視為請求失敗,並且request的代理或者隊列的代理的失敗代理函數將被調用。如果你不想讓代理函數被調用,那么將delegate設置為nil,或者使用clearDelegatesAndCancel方法來取消request。

clearDelegatesAndCancel 將會首先清除所有的代理和block。

當使用ASINetworkQueue時,如果取消了隊列中的一個request,那么隊列中其他所有request都會被取消,可以設置shouldCancelAllRequestsOnFailure的值為NO來避免這個現象。

安全地控制delegate防止request完成之前代理被釋放

request並不retain它們的代理,所以有可能你已經釋放了代理,而之后request完成了,這將會引起崩潰。大多數情況下,如果你的代理即將被釋放,你一定也希望取消所有request,因為你已經不再關心它們的返回情況了。如此做:

// 代理類的dealloc函數 
- (void)dealloc { [request clearDelegatesAndCancel]; [request release]; ... [super dealloc]; } 

三、ASIHttpRequest發送數據

ASIHttpRequest發送數據的內容,其中包括設定request頭,使用ASIFormDataRequest POST表單,PUT請求、自定義POST請求等等內容。

設定request頭

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request addRequestHeader:@"Referer" value:@"http://www.kenjiao.com/"]; 

使用ASIFormDataRequest POST表單

通常數據是以’application/x-www-form-urlencoded’格式發送的,如果上傳了二進制數據或者文件,那么格式將自動變為‘multipart/form-data’ 。

文件中的數據是需要時才從磁盤加載,所以只要web server能處理,那么上傳大文件是沒有問題的。

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; [request setPostValue:@"Ben" forKey:@"first_name"]; [request setPostValue:@"Copsey" forKey:@"last_name"]; [request setFile:@"/Users/ben/Desktop/ben.jpg" forKey:@"photo"]; 

數據的mime頭是自動判定的,但是如果你想自定義mime頭,那么這樣:

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; // Upload a file on disk 
[request setFile:@"/Users/ben/Desktop/ben.jpg" withFileName:@"myphoto.jpg" andContentType:@"image/jpeg" forKey:@"photo"]; // Upload an NSData instance 
[request setData:imageData withFileName:@"myphoto.jpg" andContentType:@"image/jpeg" forKey:@"photo"];

你可以使用addPostValue方法來發送相同name的多個數據(夢維:服務端會以數組方式呈現):

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; [request addPostValue:@"Ben" forKey:@"names"]; [request addPostValue:@"George" forKey:@"names"]; [request addFile:@"/Users/ben/Desktop/ben.jpg" forKey:@"photos"]; [request addData:imageData withFileName:@"george.jpg" andContentType:@"image/jpeg" forKey:@"photos"]; 

PUT請求、自定義POST請求

如果你想發送PUT請求,或者你想自定義POST請求,使用appendPostData: 或者 appendPostDataFromFile:

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request appendPostData:[@"This is my data" dataUsingEncoding:NSUTF8StringEncoding]]; // Default becomes POST when you use appendPostData: / appendPostDataFromFile: / setPostBody: 
[request setRequestMethod:@"PUT"]; 

四、ASIHTTPRequest進度追蹤

ASIHTTPRequest進度追蹤的內容,其中包括追蹤單個request的下載進度,追蹤一系列request的下載進度,追蹤單個request的上傳進度,追蹤一系列request的上傳進度,精確進度條vs簡單進度條,自定義進度追蹤等等內容。

每個ASIHTTPRequest有兩個delegate用來追蹤進度:

downloadProgressDelegate (下載) 和 uploadProgressDelegate (上載)。

進度delegate可以是NSProgressIndicators (Mac OS X) 或者 UIProgressViews (iPhone).ASIHTTPRequest會自適應這兩個class的行為。你也可以使用自定義class作為進度delegate,只要它響應setProgress:函數。

  • 如果你執行單個request,那么你需要為該request設定upload/download進度delegate
  • 如果你在進行多個請求,並且你想要追蹤整個隊列中的進度,你必須使用ASINetworkQueue並設置隊列的進度delegate
  • 如果上述兩者你想同時擁有,恭喜你,0.97版以后的ASIHTTPRequest,這個可以有 ^ ^

IMPORTANT:如果你向一個要求身份驗證的網站上傳數據,那么每次授權失敗,上傳進度條就會被重置為上一次的進度值。因此,當與需要授權的web服務器交互時,建議僅當useSessionPersistence為YES時才使用上傳進度條,並且確保你在追蹤大量數據的上傳進度之前,先使用另外的request來進行授權。 

追蹤小於128KB的數據上傳進度目前無法做到,而對於大於128kb的數據,進度delegate不會收到第一個128kb數據塊的進度信息。這是因為CFNetwork庫API的限制。我們曾向apple提交過bug報告(bug id 6596016),希望apple能修改CFNetwork庫以便實現上述功能。

2009-6-21:Apple的哥們兒們真棒!iPhone 3.0 SDK里,buffer大小已經被減小到32KB了,我們的上傳進度條可以更精確了。

追蹤單個request的下載進度

這個例子中, myProgressIndicator是個 NSProgressIndicator.

ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [request setDownloadProgressDelegate:myProgressIndicator]; [request startSynchronous]; NSLog(@"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]); 

追蹤一系列request的下載進度

在這個例子中, myProgressIndicator 是個 UIProgressView, myQueue是個 ASINetworkQueue.

- (void)fetchThisURLFiveTimes:(NSURL *)url { [myQueue cancelAllOperations]; [myQueue setDownloadProgressDelegate:myProgressIndicator]; [myQueue setDelegate:self]; [myQueue setRequestDidFinishSelector:@selector(queueComplete:)]; int i; for (i=0; i<5; i++) { ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; [myQueue addOperation:request]; } [myQueue go]; } - (void)queueComplete:(ASINetworkQueue *)queue { NSLog(@"Value: %f", [myProgressIndicator progress]); } 

這個例子中,我們已經為ASINetworkQueues調用過[myQueue go]了。

追蹤單個request的上傳進度

在這個例子中, myProgressIndicator 是個 UIProgressView。

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url]; [request setPostValue:@"Ben" forKey:@"first_name"]; [request setPostValue:@"Copsey" forKey:@"last_name"]; [request setUploadProgressDelegate:myProgressIndicator]; [request startSynchronous]; NSLog(@"Value: %f",[myProgressIndicator progress]); 

追蹤一系列request的上傳進度

這個例子中, myProgressIndicator是個 NSProgressIndicator, myQueue是個ASINetworkQueue.

- (void)uploadSomethingFiveTimes:(NSURL *)url { [myQueue cancelAllOperations]; [myQueue setUploadProgressDelegate:myProgressIndicator]; [myQueue setDelegate:self]; [myQueue setRequestDidFinishSelector:@selector(queueComplete:)]; int i; for (i=0; i<5; i++) { ASIHTTPRequest *request = [ASIFormDataRequest requestWithURL:url]; [request setPostBody:[@"Some data" dataUsingEncoding:NSUTF8StringEncoding]]; [myQueue addOperation:request]; } [myQueue go]; } - (void)queueComplete:(ASINetworkQueue *)queue { NSLog(@"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]); } 

精確進度條vs簡單進度條

ASIHTTPRequest提供兩種進度條顯示,簡單進度條和精確進度條,使用ASIHTTPRequests 和ASINetworkQueues的showAccurateProgress 來控制。為一個request設置showAccurateProgress只會對該request有效。如果你為一個隊列設置showAccurateProgress,那么會影響隊列里所有的request。

簡單進度條

當使用簡單進度條時,進度條只會在一個request完成時才更新。對於單個request,這意味着你只有兩個進度狀態:0%和100%。對於一個有5個request的隊列來說,有五個狀態:0%,25%,50%,75%,100%,每個request完成時,進度條增長一次。

簡單進度條(showAccurateProgress = NO)是ASINetworkQueue的默認值,適用於大量小數據請求。

精確進度條

當使用精確進度條時,每當字節被上傳或下載時,進度條都會更新。它適用於上傳/下載大塊數據的請求,並且會更好的顯示已經發送/接收的數據量。

使用精確進度條追蹤上傳會輕微降低界面效率,因為進度delegate(一般是UIProgressViews 或NSProgressIndicators)會更頻繁地重繪。

使用精確進度條追蹤下載會更影響界面效率,因為隊列會先為每個GET型request進行HEAD請求,以便統計總下載量。強烈推薦對下載大文件的隊列使用精確進度條,但是要避免對大量小數據請求使用精確進度條。

精確進度條(showAccurateProgress = YES)是以同步方式執行的ASIHTTPRequest的默認值。

自定義進度追蹤

ASIProgressDelegate 協議定義了所有能更新一個request進度的方法。多數情況下,設置你的uploadProgressDelegate或者 downloadProgressDelegate為NSProgressIndicator或者UIProgressView會很好。但是,如果你想進行更復雜的追蹤,你的進度delegate實現下列函數要比 setProgress: (iOS) 或者 setDoubleValue: / setMaxValue: (Mac)好:

這些函數允許你在實際量的數據被上傳或下載時更新進度,而非簡單方法的0到1之間的數字。

downloadProgressDelegates方法

  • request:didReceiveBytes: 每次request下載了更多數據時,這個函數會被調用(注意,這個函數與一般的代理實現的 request:didReceiveData:函數不同)。
  • request:incrementDownloadSizeBy: 當下載的大小發生改變時,這個函數會被調用,傳入的參數是你需要增加的大小。這通常發生在request收到響應頭並且找到下載大小時。

uploadProgressDelegates方法

  • request:didSendBytes: 每次request可以發送更多數據時,這個函數會被調用。注意:當一個request需要消除上傳進度時(通常是該request發送了一段數據,但是因為授權失敗或者其他什么原因導致這段數據需要重發)這個函數會被傳入一個小於零的數字。

五、ASIHTTPRequest斷點續傳(下載)

ASIHTTPRequest斷點續傳(下載)的內容,其中包括ASIHTTPRequest可以恢復中斷的下載,設置一個臨時下載路徑,斷點續傳的工作原理等等內容。

從0.94版本開始,ASIHTTPRequest可以恢復中斷的下載。

這個特性只對下載數據到文件中有效,你必須為一下情況的request設置allowResumeForFileDownloads 為YES:

  • 任何你希望將來可以斷點續傳的下載(否則,ASIHTTPRequest會在取消或者釋放內存時將臨時文件刪除)
  • 任何你要進行斷點續傳的下載

另外,你必須自己設置一個臨時下載路徑(setTemporaryFileDownloadPath),這個路徑是未完成的數據的路徑。新的數據將會被添加到這個文件,當下載完成時,這個文件將被移動到downloadDestinationPath 。

- (IBAction)resumeInterruptedDownload:(id)sender { NSURL *url = [NSURL URLWithString: @"http://www.dreamingwish.com/wp-content/uploads/2011/10/asihttprequest-auth.png"]; ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; NSString *downloadPath = @"/Users/ben/Desktop/asi.png"; //當request完成時,整個文件會被移動到這里 
 [request setDownloadDestinationPath:downloadPath]; //這個文件已經被下載了一部分 
  [request setTemporaryFileDownloadPath:@"/Users/ben/Desktop/asi.png.download"]; [request setAllowResumeForFileDownloads:YES]; [request startSynchronous]; //整個文件將會在這里 
  NSString *theContent = [NSString stringWithContentsOfFile:downloadPath]; } 

斷點續傳的工作原理是讀取temporaryFileDownloadPath的文件的大小,並使用Range: bytes=x HTTP頭來請求剩余的文件內容。

ASIHTTPRequest並不檢測是否存在Accept-Ranges頭(因為額外的HEAD頭請求會消耗額外的資源),所以只有確定服務器支持斷點續傳下載時,再使用這個特性。

六、ASIHttpRequest請求HTTPS

//一種方法 ASIHTTPRequest *request = [ASIHTTPRequestrequestWithURL:[NSURLURLWithString:bodyString]]; [request setDelegate:self]; request.timeOutSeconds = 20; [request setRequestMethod:@"POST"]; [request addRequestHeader:@"Content-Type"value:@"application/xml;charset=UTF-8;"]; [request setValidatesSecureCertificate:NO];//請求https的時候,就要設置這個屬性 [request startAsynchronous];          //二種方法 ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:[NSURL URLWithString:bodyString]]; [request setDelegate:self]; request.timeOutSeconds = 20; [request setValidatesSecureCertificate:NO];//請求https的時候,就要設置這個屬性
 [request setRequestMethod:@"POST"]; [request addRequestHeader:@"Content-Type" value:@"application/xml;charset=UTF-8"]; request.defaultResponseEncoding = NSUTF8StringEncoding;

 

參考原址:http://mobile.51cto.com/iphone-405168.htm


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM