1.AFN框架基本使用
- 1 AFN內部結構
AFN結構體-
NSURLConnection(iOS9.0廢棄了)
- 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 做了一些分類, 對自己能力提升是非常有幫助的
- 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":@"",
@"pwd":@""
};
//注意:字符串中不能包含空格
[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":@"",
@"pwd":@""
};
//注意: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);
}
2.AFN文件上傳
1.文件上傳拼接數據的第一種方式
[formData appendPartWithFileData:data name:@"file" fileName:@"xxoo.png" mimeType:@"application/octet-stream"];
2.文件上傳拼接數據的第二種方式
[formData appendPartWithFileURL:fileUrl name:@"file" fileName:@"xx.png" mimeType:@"application/octet-stream" error:nil];
3.文件上傳拼接數據的第三種方式
[formData appendPartWithFileURL:fileUrl name:@"file" error:nil];
4.【注】在資料中已經提供了一個用于文件上傳的分類。
/文件上傳相關的代碼如下/
-(void)upload {
//1.創建一個請求管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.發送POST請求上傳數據
/*
第一個參數:請求路徑:NSString類型
第二個參數:要上傳的非文件參數
第三個參數:block回調
在該回調中,需要利用formData拼接即將上傳的二進制數據
第三個參數:上傳成功的block回調
task:dataTask(任務)
responseObject:服務器返回的數據
第四個參數:上傳失敗的block回調
error:錯誤信息,如果上傳文件失敗,那么error里面包含了錯誤的描述信息
*/
NSDictionary *dict = @{
@"username":@""
};
[manager POST:@"http://120.25.226.186:32812/upload" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
//把本地的圖片轉換為NSData類型的數據
UIImage *image = [UIImage imageNamed:@"123"];
NSData *data = UIImagePNGRepresentation(image);
/*
//拼接二進制文件數據
第一個參數:要上傳的文件的二進制數據
第二個參數:服務器接口規定的名稱
第三個參數:這個參數上傳到服務器之后用什么名字來進行保存
第四個參數:上傳文件的MIMEType類型
*/
[formData appendPartWithFileData:data name:@"file" fileName:@"xxoo.png" mimeType:@"application/octet-stream"];
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
NSLog(@"請求成功---%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
NSLog(@"請求失敗--%@",error);
}];
}
-(void)upload2 {
NSLog(@"%s",__func__);
//1.創建一個請求管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//2.發送POST請求上傳數據
/*
第一個參數:請求路徑:NSString類型
第二個參數:要上傳的非文件參數
第三個參數:block回調
在該回調中,需要利用formData拼接即將上傳的二進制數據
第三個參數:上傳成功的block回調
task:dataTask(任務)
responseObject:服務器返回的數據
第四個參數:上傳失敗的block回調
error:錯誤信息,如果上傳文件失敗,那么error里面包含了錯誤的描述信息
*/
NSDictionary *dict = @{
@"username":@""
};
[manager POST:@"http://120.25.226.186:32812/upload" parameters:dict constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
//本地文件的url
NSURL *fileUrl = [NSURL fileURLWithPath:@"/Users/username/Desktop/KF[WTI`AQ3T`A@3R(B96D89.gif"];
/*
//拼接二進制文件數據
第一個參數:要上傳文件的url路徑
第二個參數:服務器要求的參數名稱
第三個參數:這個文件上傳到服務器之后叫什么名稱
第四個參數:文件的mimetype類型
第五個參數:錯誤信息
*/
// [formData appendPartWithFileURL:fileUrl name:@"file" fileName:@"xx.png" mimeType:@"application/octet-stream" error:nil];
//另外一種上傳文件的方式
/*
說明:該方法和上面的方法等價,不過該方法更加簡單其內部會自動的的根據url路徑確定文件保存名稱,并通過內部方法獲取上傳文件的mimetype類型
*/
[formData appendPartWithFileURL:fileUrl name:@"file" error:nil];
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
NSLog(@"請求成功---%@",responseObject);
} failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
NSLog(@"請求失敗--%@",error);
}];
}
3.使用AFN進行序列化處理
/*
1.AFN它內部默認把服務器響應的數據當做json來進行解析,所以如果服務器返回給我的不是JSON數據那么請求報錯,這個時候需要設置AFN對響應信息的解析方式。AFN提供了三種解析響應信息的方式,分別是:
1)AFXMLParserResponseSerializer----XML
- AFHTTPResponseSerializer---------默認二進制響應數據
3)AFJSONResponseSerializer---------JSON
2.還有一種情況就是服務器返回給我們的數據格式不太一致(開發者工具Content-Type:text/xml),那么這種情況也有可能請求不成功。解決方法:
1) 直接在源代碼中修改,添加相應的Content-Type
2) 拿到這個屬性,添加到它的集合中
3.相關代碼
-(void)serialized {
//1.創建請求管理者,內部基于NSURLSession
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
/* 知識點1:設置AFN采用什么樣的方式來解析服務器返回的數據*/
//如果返回的是XML,那么告訴AFN,響應的時候使用XML的方式解析
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
//如果返回的就是二進制數據,那么采用默認二進制的方式來解析數據
//manager.responseSerializer = [AFHTTPResponseSerializer serializer];
//采用JSON的方式來解析數據
//manager.responseSerializer = [AFJSONResponseSerializer serializer];
/*知識點2 告訴AFN,再序列化服務器返回的數據的時候,支持此種類型
[AFJSONResponseSerializer serializer].acceptableContentTypes = [NSSet setWithObject:@"text/xml"];
//2.把所有的請求參數通過字典的方式來裝載,GET方法內部會自動把所有的鍵值對取出以&符號拼接并最后用?符號連接在請求路徑后面
NSDictionary *dict = @{
@"username":@"223",
@"pwd":@"ewr",
@"type":@"XML"
};
//3.發送GET請求
[manager GET:@"http://120.25.226.186:32812/login" parameters:dict success:^(NSURLSessionDataTask * _Nonnull task, id _Nonnull responseObject) {
//4.請求成功的回調block
NSLog(@"%@",[responseObject class]);
} failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
//5.請求失敗的回調,可以打印error的值查看錯誤信息
NSLog(@"%@",error);
}];
}
4.使用AFN來檢測網絡狀態
/*
說明:可以使用AFN框架中的AFNetworkReachabilityManager來監聽網絡狀態的改變,也可以利用蘋果提供的Reachability來監聽。建議在開發中直接使用AFN框架處理。
*/
//使用AFN框架來檢測網絡狀態的改變
-(void)AFNReachability {
//1.創建網絡監聽管理者
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
//2.監聽網絡狀態的改變
/*
AFNetworkReachabilityStatusUnknown = 未知
AFNetworkReachabilityStatusNotReachable = 沒有網絡
AFNetworkReachabilityStatusReachableViaWWAN = 3G
AFNetworkReachabilityStatusReachableViaWiFi = WIFI
*/
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusUnknown:
NSLog(@"未知");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"沒有網絡");
break;
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"3G");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"WIFI");
break;
default:
break;
}
}];
//3.開始監聽
[manager startMonitoring];
}
//使用蘋果提供的Reachability來檢測網絡狀態,如果要持續監聽網絡狀態的概念,需要結合通知一起使用。
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//1.注冊一個通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChange) name:kReachabilityChangedNotification object:nil];
//2.拿到一個對象,然后調用開始監聽方法
Reachability *r = [Reachability reachabilityForInternetConnection];
[r startNotifier];
//持有該對象,不要讓該對象釋放掉
self.r = r;
}
//當控制器釋放的時候,移除通知的監聽
-(void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void)networkChange
{
//獲取當前網絡的狀態
if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus == ReachableViaWWAN)
{
NSLog(@"當前網絡狀態為3G");
return;
}
if ([Reachability reachabilityForLocalWiFi].currentReachabilityStatus == ReachableViaWiFi)
{
NSLog(@"當前網絡狀態為wifi");
return;
}
NSLog(@"當前沒有網絡");
}
5.AFN使用技巧
1.在開發的時候可以創建一個工具類,繼承自我們的AFN中的請求管理者,再控制器中真正發請求的代碼使用自己封裝的工具類。
2.這樣做的優點是以后如果修改了底層依賴的框架,那么我們修改這個工具類就可以了,而不用再一個一個的去修改。
3.該工具類一般提供一個單例方法,在該方法中會設置一個基本的請求路徑。
4.該方法通常還會提供對GET或POST請求的封裝。
5.在外面的時候通過該工具類來發送請求
6.單例方法:
+ (instancetype)shareNetworkTools {
static XMGNetworkTools *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 注意: BaseURL中一定要以/結尾
instance = [[self alloc] initWithBaseURL:[NSURL URLWithString:@"http://120.25.226.186:32812/"]];
});
return instance;
}