5.NSURLSession的基本使用
- 5.1 涉及知識點
(1)使用步驟
使用NSURLSession創建task,然后執行task
(2)關于task
a.NSURLSessionTask是一個抽象類,本身不能使用,只能使用它的子類
b.NSURLSessionDataTask\NSURLSessionUploadTask\NSURLSessionDownloadTask
(3)發送get請求
//1.創建NSURLSession對象(可以獲取單例對象)
NSURLSession *session = [NSURLSession sharedSession];
//2.根據NSURLSession對象創建一個Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=ss&pwd=ss&type=JSON"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//方法參數說明
/*
注意:該block是在子線程中調用的,如果拿到數據之后要做一些UI刷新操作,那么需要回到主線程刷新
第一個參數:需要發送的請求對象
block:當請求結束拿到服務器響應的數據時調用block
block-NSData:該請求的響應體
block-NSURLResponse:存放本次請求的響應信息,響應頭,真實類型為NSHTTPURLResponse
block-NSErroe:請求錯誤信息
*/
NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
//拿到響應頭信息
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
//4.解析拿到的響應數據
NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
}];
//3.執行Task
//注意:剛創建出來的task默認是掛起狀態的,需要調用該方法來啟動任務(執行任務)
[dataTask resume];
(4)發送get請求的第二種方式
//注意:該方法內部默認會把URL對象包裝成一個NSURLRequest對象(默認是GET請求)
//方法參數說明
/*
//第一個參數:發送請求的URL地址
//block:當請求結束拿到服務器響應的數據時調用block
//block-NSData:該請求的響應體
//block-NSURLResponse:存放本次請求的響應信息,響應頭,真實類型為NSHTTPURLResponse
//block-NSErroe:請求錯誤信息
*/
- (nullable NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error))completionHandler;
(5)發送POST請求
//1.創建NSURLSession對象(可以獲取單例對象)
NSURLSession *session = [NSURLSession sharedSession];
//2.根據NSURLSession對象創建一個Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//創建一個請求對象,并這是請求方法為POST,把參數放在請求體中傳遞
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
//拿到響應頭信息
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
//解析拿到的響應數據
NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
}];
//3.執行Task
//注意:剛創建出來的task默認是掛起狀態的,需要調用該方法來啟動任務(執行任務)
[dataTask resume];
6.NSURLSession下載文件-代理
- 6.1 涉及知識點
(1)創建NSURLSession對象,設置代理(默認配置)
//1.創建NSURLSession,并設置代理
/*
第一個參數:session對象的全局配置設置,一般使用默認配置就可以
第二個參數:誰成為session對象的代理
第三個參數:代理方法在哪個隊列中執行(在哪個線程中調用),如果是主隊列那么在主線程中執行,如果是非主隊列,那么在子線程中執行
*/
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
(2)根據Session對象創建一個NSURLSessionDataTask任務(post和get選擇)
//創建task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];
//注意:如果要發送POST請求,那么請使用dataTaskWithRequest,設置一些請求頭信息
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url];
(3)執行任務(其它方法,如暫停、取消等)
//啟動task
//[dataTask resume];
//其它方法,如取消任務,暫停任務等
//[dataTask cancel];
//[dataTask suspend];
(4)遵守代理協議,實現代理方法(3個相關的代理方法)
/*
1.當接收到服務器響應的時候調用
session:發送請求的session對象
dataTask:根據NSURLSession創建的task任務
response:服務器響應信息(響應頭)
completionHandler:通過該block回調,告訴服務器端是否接收返回的數據
*/
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSURLResponse *)response completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler
/*
2.當接收到服務器返回的數據時調用
該方法可能會被調用多次
*/
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveData:(nonnull NSData *)data
/*
3.當請求完成之后調用該方法
不論是請求成功還是請求失敗都調用該方法,如果請求失敗,那么error對象有值,否則那么error對象為空
*/
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
(5)當接收到服務器響應的時候,告訴服務器接收數據(調用block)
//默認情況下,當接收到服務器響應之后,服務器認為客戶端不需要接收數據,所以后面的代理方法不會調用
//如果需要繼續接收服務器返回的數據,那么需要調用block,并傳入對應的策略
/*
NSURLSessionResponseCancel = 0, 取消任務
NSURLSessionResponseAllow = 1, 接收任務
NSURLSessionResponseBecomeDownload = 2, 轉變成下載
NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3, 轉變成流
*/
completionHandler(NSURLSessionResponseAllow);
7.NSURLSessionDownloadTask實現大文件下載
- 7.1 涉及知識點
(1)使用NSURLSession和NSURLSessionDownload可以很方便的實現文件下載操作
/*
第一個參數:要下載文件的url路徑
第二個參數:當接收完服務器返回的數據之后調用該block
location:下載的文件的保存地址(默認是存儲在沙盒中tmp文件夾下面,隨時會被刪除)
response:服務器響應信息,響應頭
error:該請求的錯誤信息
*/
//說明:downloadTaskWithURL方法已經實現了在下載文件數據的過程中邊下載文件數據,邊寫入到沙盒文件的操作
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL * __nullable location, NSURLResponse * __nullable response, NSError * __nullable error)
(2)downloadTaskWithURL內部默認已經實現了變下載邊寫入操作,所以不用開發人員擔心內存問題
(3)文件下載后默認保存在tmp文件目錄,需要開發人員手動的剪切到合適的沙盒目錄
(4)缺點:沒有辦法監控下載進度
8.使用NSURLSessionDownloadTask實現大文件下載-監聽下載進度
- 8.1 涉及知識點
(1)創建NSURLSession并設置代理,通過NSURLSessionDownloadTask并以代理的方式來完成大文件的下載
//1.創建NSULRSession,設置代理
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//2.創建task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
self.downloadTask = [self.session downloadTaskWithURL:url];
//3.執行task
[self.downloadTask resume];
(2)常用代理方法的說明
/*
1.當接收到下載數據的時候調用,可以在該方法中監聽文件下載的進度
該方法會被調用多次
totalBytesWritten:已經寫入到文件中的數據大小
totalBytesExpectedToWrite:目前文件的總大小
bytesWritten:本次下載的文件數據大小
*/
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
/*
2.恢復下載的時候調用該方法
fileOffset:恢復之后,要從文件的什么地方開發下載
expectedTotalBytes:該文件數據的總大小
*/
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
/*
3.下載完成之后調用該方法
*/
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location
/*
4.請求完成之后調用
如果請求失敗,那么error有值
*/
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
(3)實現斷點下載相關代碼
//如果任務,取消了那么以后就不能恢復了
// [self.downloadTask cancel];
//如果采取這種方式來取消任務,那么該方法會通過resumeData保存當前文件的下載信息
//只要有了這份信息,以后就可以通過這些信息來恢復下載
[self.downloadTask cancelByProducingResumeData:^(NSData * __nullable resumeData) {
self.resumeData = resumeData;
}];
-----------
//繼續下載
//首先通過之前保存的resumeData信息,創建一個下載任務
self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
[self.downloadTask resume];
(4)計算當前下載進度
//獲取文件下載進度
self.progress.progress = 1.0 * totalBytesWritten/totalBytesExpectedToWrite;
(5)局限性
01 如果用戶點擊暫停之后退出程序,那么需要把恢復下載的數據寫一份到沙盒,代碼復雜度更
02 如果用戶在下載中途未保存恢復下載數據即退出程序,則不具備可操作性
9.使用NSURLSessionDataTask實現大文件離線斷點下載(完整)
- 9.1 涉及知識點
(1)關于NSOutputStream的使用
//1. 創建一個輸入流,數據追加到文件的屁股上
//把數據寫入到指定的文件地址,如果當前文件不存在,則會自動創建
NSOutputStream *stream = [[NSOutputStream alloc]initWithURL:[NSURL fileURLWithPath:[self fullPath]] append:YES];
//2. 打開流
[stream open];
//3. 寫入流數據
[stream write:data.bytes maxLength:data.length];
//4.當不需要的時候應該關閉流
[stream close];
(2)關于網絡請求請求頭的設置(可以設置請求下載文件的某一部分)
//1. 設置請求對象
//1.1 創建請求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
//1.2 創建可變請求對象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//1.3 拿到當前文件的殘留數據大小
self.currentContentLength = [self FileSize];
//1.4 告訴服務器從哪個地方開始下載文件數據
NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentContentLength];
NSLog(@"%@",range);
//1.5 設置請求頭
[request setValue:range forHTTPHeaderField:@"Range"];
(3)NSURLSession對象的釋放
-(void)dealloc
{
//在最后的時候應該把session釋放,以免造成內存泄露
// NSURLSession設置過代理后,需要在最后(比如控制器銷毀的時候)調用session的invalidateAndCancel或者resetWithCompletionHandler,才不會有內存泄露
// [self.session invalidateAndCancel];
[self.session resetWithCompletionHandler:^{
NSLog(@"釋放---");
}];
}
(4)優化部分
01 關于文件下載進度的實時更新
02 方法的獨立與抽取
10.NSURLSession實現文件上傳
- 10.1 涉及知識點
(1)實現文件上傳的方法
/*
第一個參數:請求對象
第二個參數:請求體(要上傳的文件數據)
block回調:
NSData:響應體
NSURLResponse:響應頭
NSError:請求的錯誤信息
*/
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error)
(2)設置代理,在代理方法中監聽文件上傳進度
/*
調用該方法上傳文件數據
如果文件數據很大,那么該方法會被調用多次
參數說明:
totalBytesSent:已經上傳的文件數據的大小
totalBytesExpectedToSend:文件的總大小
*/
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
NSLog(@"%.2f",1.0 * totalBytesSent/totalBytesExpectedToSend);
}
(3)關于NSURLSessionConfiguration相關
01 作用:可以統一配置NSURLSession,如請求超時等
02 創建的方式和使用
//創建配置的三種方式
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(10_10, 8_0);
//統一配置NSURLSession
-(NSURLSession *)session
{
if (_session == nil) {
//創建NSURLSessionConfiguration
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
//設置請求超時為10秒鐘
config.timeoutIntervalForRequest = 10;
//在蜂窩網絡情況下是否繼續請求(上傳或下載)
config.allowsCellularAccess = NO;
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
11.AFN框架基本使用
- 11.1 AFN內部結構
AFN結構體
- NSURLConnection
+ AFURLConnectionOperation
+ AFHTTPRequestOperation
+ AFHTTPRequestOperationManager(封裝了常用的 HTTP 方法)
* 屬性
* baseURL :AFN建議開發者針對 AFHTTPRequestOperationManager 自定義個一個單例子類,設置 baseURL, 所有的網絡訪問,都只使用相對路徑即可
* requestSerializer :請求數據格式/默認是二進制的 HTTP
* responseSerializer :響應的數據格式/默認是 JSON 格式
* operationQueue
* reachabilityManager :網絡連接管理器
* 方法
* manager :方便創建管理器的類方法
* HTTPRequestOperationWithRequest :在訪問服務器時,如果要告訴服務器一些附加信息,都需要在 Request 中設置
* GET
* POST
- NSURLSession
+ AFURLSessionManager
+ AFHTTPSessionManager(封裝了常用的 HTTP 方法)
* GET
* POST
* UIKit + AFNetworking 分類
* NSProgress :利用KVO
- 半自動的序列化&反序列化的功能
+ AFURLRequestSerialization :請求的數據格式/默認是二進制的
+ AFURLResponseSerialization :響應的數據格式/默認是JSON格式
- 附加功能
+ 安全策略
* HTTPS
* AFSecurityPolicy
+ 網絡檢測
* 對蘋果的網絡連接檢測做了一個封裝
* AFNetworkReachabilityManager
建議:
可以學習下AFN對 UIKit 做了一些分類, 對自己能力提升是非常有幫助的
- 11.2 AFN的基本使用
(1)發送GET請求的兩種方式(POST同)
-(void)get1
{
//1.創建AFHTTPRequestOperationManager管理者
//AFHTTPRequestOperationManager內部是基于NSURLConnection實現的
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//2.發送請求
/*
http://120.25.226.186:32812/login?username=ee&pwd=ee&type=JSON
第一個參數:NSString類型的請求路徑,AFN內部會自動將該路徑包裝為一個url并創建請求對象
第二個參數:請求參數,以字典的方式傳遞,AFN內部會判斷當前是POST請求還是GET請求,以選擇直接拼接還是轉換為NSData放到請求體中傳遞
第三個參數:請求成功之后回調Block
第四個參數:請求失敗回調Block
*/
NSDictionary *param = @{
@"username":@"520it",
@"pwd":@"520it"
};
//注意:字符串中不能包含空格
[manager GET:@"http://120.25.226.186:32812/login" parameters:param success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) {
NSLog(@"請求成功---%@",responseObject);
} failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
NSLog(@"失敗---%@",error);
}];
}
-(void)get2
{
//1.創建AFHTTPSessionManager管理者
//AFHTTPSessionManager內部是基于NSURLSession實現的
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.發送請求
NSDictionary *param = @{
@"username":@"520it",
@"pwd":@"520it"
};
//注意:responseObject:請求成功返回的響應結果(AFN內部已經把響應體轉換為OC對象,通常是字典或數組)
[manager GET:@"http://120.25.226.186:32812/login" parameters:param success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
NSLog(@"請求成功---%@",[responseObject class]);
} failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
NSLog(@"失敗---%@",error);
}];
}
(2)使用AFN下載文件
-(void)download
{
//1.創建一個管理者
AFHTTPSessionManager *manage = [AFHTTPSessionManager manager];
//2.下載文件
/*
第一個參數:請求對象
第二個參數:下載進度
第三個參數:block回調,需要返回一個url地址,用來告訴AFN下載文件的目標地址
targetPath:AFN內部下載文件存儲的地址,tmp文件夾下
response:請求的響應頭
返回值:文件應該剪切到什么地方
第四個參數:block回調,當文件下載完成之后調用
response:響應頭
filePath:文件存儲在沙盒的地址 == 第三個參數中block的返回值
error:錯誤信息
*/
//2.1 創建請求對象
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]];
//2.2 創建下載進度,并監聽
NSProgress *progress = nil;
NSURLSessionDownloadTask *downloadTask = [manage downloadTaskWithRequest:request progress:&progress destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
//拼接文件全路徑
NSString *fullpath = [caches stringByAppendingPathComponent:response.suggestedFilename];
NSURL *filePathUrl = [NSURL fileURLWithPath:fullpath];
return filePathUrl;
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nonnull filePath, NSError * _Nonnull error) {
NSLog(@"文件下載完畢---%@",filePath);
}];
//2.3 使用KVO監聽下載進度
[progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];
//3.啟動任務
[downloadTask resume];
}
//獲取并計算當前文件的下載進度
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(NSProgress *)progress change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSLog(@"%zd--%zd--%f",progress.completedUnitCount,progress.totalUnitCount,1.0 * progress.completedUnitCount/progress.totalUnitCount);
}