HTTP和ASI框架的使用
// Xcode的插件安装路径: /Users/用户名/Library/Application Support/Developer/Shared/Xcode/Plug-ins
访问网络的基本流程:5点
构造NSURL实例(地址)--》生成NSURLRequest请求 --》通过NSURLConnection发送请求--》通过NSURLResponse实例和NSError返回结果 --》 接受返回数据
HTTP协议
1.面试题: 聊一下HTTP协议(协议的完整的通信过程)
*HTTP协议的全称:超文本传输协议,定制传输数据的规范(客户端和服务器之间的数据传输规范)
*描述HTTP协议完整的通信过程
2.通信过程
1> 请求
- 客户端 --> 服务器
- 请求的内容
a. "请求行":(请求方法\请求资源路径\HTTP协议版本)
b. "请求头":(描述客户端的信息,如手机的系统版本、语言等)
c. "请求体":(POST请求才需要有, 存放具体数据,如用户名账号、密码等)
2> 响应
- 服务器 --> 客户端
- 响应的内容
a. "状态行"(HTTP协议版本\状态码\状态信息"响应行")
*HTTP/1.1 200 OK
b. "响应头"(服务器信息, 返回数据的类型, 返回数据的长度)
Server: Apache-Coyote/1.1
Content-Type: application/jsion;charset=UTF-8
Content-Lenngth: 248
c. "实体内容"( 返回给客户端的具体内容,"响应体")
*比如服务器返回的JSON对象
*比如服务器返回的文件数据
// --------------请求的方法-----get 和 post ----------------
3.HTTP请求的方法
1> GET
- 参数都拼接在URL后面
- 参数有限制
2> POST
- 参数都在请求体
- 参数没有限制
- 文件上传自能用POST
// -------------请求的手段------asyn 和 sync----------------
4.iOS中发送GET\POST请求的"手段3种"
1、> NSURLConnection(苹果原生态),使用起来比ASI\AFN复杂
2、> ASI (基于CFNetwork),提供了非常强大的功能,使用简单
3、> AFN (基于NSURLConnection),提供了常用功能,使用简单
"建议:"
- 为了提高开发效率和减少调试时间,尽量使用著名的第三方框架
- 因此使用HTTP请求,更建议使用ASI或AFN
1>" NSURLConnection"
// 1.发送一个同步请求
- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;
// 2.发送一个异步请求(block)
- (void)sendAsynchronousRequest:(NSURLRequest) request
queue:(NSOperationQueue) queue
completionHandler:(void (^)(NSURLResponse* response, NSData* data, NSError* connectionError)) handler;
// 3.代理的方法(异步需要实现该方法)
[NSURLConnection connectionWithRequest:request delegate:self];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[conn start];
2>文件下载(大文件下载)
1"实现方案":边下载边写入(写入沙河里)
2"具体实现步骤":
a.在接收到服务器的"响应时"
*.创建一个空的文件到沙盒中--NSFileManager
*.创建写数据的文件句柄对象--NSFileHandle
b.在接收到服务器的"数据时"
// 利用句柄对象将服务器返回的数据写到文件的末尾
// 移动文件的尾部
[self.writeHandle seekToEndOfFile];
// 从当前移动的位置(文件尾部)开始写入数据
[self.writeHandle writeData:data];
c.在接收完毕服务器返回的数据时
// 关闭句柄
[self.writeHandle closeFile];
self.writeHandle = nil;
3*"断点下载"
1> 关键技术点
*设置"请求头Range",告诉服务器下载哪一段数据(开始、结束),其它的和文件下载没区别
4*文件上传
1>明确
*只能用POST请求
*请求参数都在请求体(文件参数、非文件参数类型的普通参数)
2>实现步骤
a.拼接请求体(文件参数、非文件参数类型的普通参数)
*文件参数
//参数的开始标记(分隔线)
--heima\r\n
//参数描述(参数名。。。)
Content-Disposition: form-data; name="参数名"; filename="文件名"\r\n
//文件类型
Content-Type: 文件的类型MIMEType\r\n
// 文件的二进制数据(参数值)
\r\n
// 文件的二进制数据
\r\n
*非文件参数(普通参数)
//参数的开始标记(分隔线)
--heima\r\n
//参数描述(参数名。。。)
Content-Disposition: form-data; name="参数名"\r\n
// 参数值
\r\n
//参数值
\r\n
// * 所有参数结束的标记(分隔线)
--heima--\r\n
b.设置请求头
*请求体的长度
Content-Lenngth : 请求体的长度
*请求数据的类型
Content-Type:
// 普通POSTQQ:application/x-www-from-urlencoded
// 上传文件的POST请求:multipart/form-data; boundary=--heima
3> "文件上传"
二、> ASI
1、缓存
1> 缓存单个请求
// 1、 获得全局的缓存对象(决定着缓存存储路径)
ASIDownloadCache *cache = [ASIDownloadCache sharedCache];
// 设置系统默认的缓存加载策略
cache.defaultCachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;
// 2、设置请求对象的缓存对象(使用哪个缓存对象)
request.downloadCache = cache;
// 3、设置请求对象的缓存---加载策略
request.cachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;
// 4、设置请求对象的缓存---存储策略(存储的时长)
request.cacheStoragePolicy = ASICachePermanentlyCacheStoragePolicy; //Permanently(永久的),但是默认是ASICacheForSessionDurationCacheStoragePolicy
------------------------------------------------
// ----注意:缓存加载策略的优先级:request.cachePolicy > cache.defaultCachePolicy----
-----------------------------------------------
2> 缓存所有请求
// 1.获得全局的缓存对象(决定缓存的存储路径,存储到什么位置)
ASIDownloadCache *cache = [ASIDownloadCache sharedCache];
// 设置默认的缓存加载策略(默认的缓存加载策略 : 不读取缓存)
cache.defaultCachePolicy = ASIOnlyLoadIfNotCachedCachePolicy;
// 2、设置全局缓存对象
[ASIHTTPRequest setDefaultCache:cache];
// 判断数据是否从缓存读取: BOOL useCache[request didUseCachedResponse];
// 或者设置7天,但这个要用ASI框架: [request setSecondsToCache:60 * 60 * 24 * 7];
三. > "NSURLCache"
缓存的使用步骤
// 获得全局(默认是全局)的缓存对象
NSURLCache *cache = [NSURLCache sharedURLCache];
//设置缓存容量
cache.memoryCapacity = 4 * 1024 * 1024;
cache.diskCapacity = 20 * 1024 * 1024;
//设置缓存策略(有缓存就用缓存,没有缓存就重新请求)
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
"使用框架---发送请求":
1> "同步请求"
[request startSynchronous];
2> "异步请求"
[request startAsynchronous];
3> "GET\POST"
1.GET请求
// ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
2.POST请求
// ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
// 添加普通参数(非文件参数)
[request setPostValue:@"zhangsan" forKey:@"username"];
[request setPostValue:@"124" forKey:@"pwd"];
4> "文件下载"
// 文件的存储路径(文件下载到什么地方)
request.downloadDestinationPath = filepath;
// 3.设置下载代理--进度
request.downloadProgressDelegate = self.circleView; //圆形条
// request.downloadProgressDelegate = self.progressView;// 进度条
// 4.支持断点下载
request.allowResumeForFileDownloads = YES;
5> "文件上传"
// 添加文件参数(file:需要上传文件的路径)
[request setFile:file forKey:@"file"];
[request setFile:file withFileName:@"123.txt" andContentType:@"text/plain" forKey:@"file"];
[request setData:data withFileName:@"minion.png" andContentType:@"image/png" forKey:@"file"]; // (二进制,直接上传到服务器,适用于相机)
// 设置上传代理(监听上传进度)
request.uploadProgressDelegate = self.circleView;
6>------------------------ "监听请求的过程" --3种方式(代理、SEL、block) ----------------------------------------
1> 代理方法(1)
// 设置代理
request.delegate = self;
// 遵守协议
ASIHTTPRequestDelegate
// 实现代理的方法
- (void)requestStarted:(ASIHTTPRequest *)request;
- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data;
- (void)requestFinished:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
2> SEL(方法2,---该方式较少用)
// 设置代理
request.delegate = self;
[request setDidStartSelector(start)]; // 开始发送请求,就会调用代理的start的方法
// 实现方法
- (void)start
{
// ....
}
3> block(方法3)
[request setStartedBlock:^{
NSLog(@"setStartedBlock");
}];
[request setDataReceivedBlock:^{
NSLog(@"setDataReceivedBlock");
}];
[request setCompletionBlock:^{
NSLog(@"setCompletionBloc");
}];
[request setFailedBlock:^{
NSLog(@"setFailedBlock---");
}];
7> 通过request对象获得服务器返回的数据(响应)
1> 获得响应头信息
@property (atomic, retain) NSDictionary *responseHeaders;
2> 获得响应体(实体内容)
- (NSData *)responseData; // 直接返回服务器的二进制数据
- (NSString *)responseString; // 将二进制的数据转成字符串(方便调试)
二、"框架AFN --- "
1、GET\POST
1."GET请求"
// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// 2.封装请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"123";
params[@"pwd"] = @"123";
// 3.发送GET请求
mgr GET:@"http://192.168.1.200:8080/MJServer/login" parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) { // responseObject : 在这种情况下是字典
NSLog(@"请求成功---%@", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"请求失败---%@", error);
}];
------------------
2."POST请求"
// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// 2.封装请求参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"123";
params[@"pwd"] = @"123";
// 3.发送POST请求
mgr POST:@"http://192.168.1.200:8080/MJServer/login" parameters:params
success:^(AFHTTPRequestOperation *operation, id responseObject) { // responseObject : 在这种情况下是字典
NSLog(@"请求成功---%@", responseObject);
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"请求失败---%@", error);
}];
2."文件上传"
// 1.获得请求管理者
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// 2.发送请求(做文件上传)
// parameters : 只能放非文件参数
NSMutableDictionary *params = [NSMutableDictionary dictionary];
params[@"username"] = @"zhangsan";
[mgr POST:@"http://192.168.1.200:8080/MJServer/upload" parameters:params
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// 一定要在这个block中添加文件参数
// 加载文件数据
NSString *file = [[NSBundle mainBundle] pathForResource:@"test.txt" ofType:nil];
NSData *data = [NSData dataWithContentsOfFile:file];
// 拼接文件参数
[formData appendPartWithFileData:data name:@"file" fileName:@"123.txt" mimeType:@"text/plain"];
}
success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"上传成功----%@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"上传失败----%@", error);
}];
- "使用框架--网络状态监控"
1 >"使用框架Reachability"
// 使用通知--监听网络状态改变的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkStateChange) name:kReachabilityChangedNotification object:nil];
// 创建Reachability
self.conn = [Reachability reachabilityForInternetConnection];
// 开始监控网络(一旦网络状态发生改变,就会发出kReachabilityChangedNotification)
[self.conn startNotifier];
// 处理网络状态改变
-
(void)checkNetworkState
{
// 1.检测wifi状态
Reachability *wifi = [Reachability reachabilityForLocalWiFi];// 2.检测手机是否能上网络(WIFI\3G\2.5G)
Reachability *conn = [Reachability reachabilityForInternetConnection];// 3.判断网络状态
if ([wifi currentReachabilityStatus] != NotReachable) { // 有wifi
NSLog(@"有wifi");} else if ([conn currentReachabilityStatus] != NotReachable) { // 没有使用wifi, 使用手机自带网络进行上网
NSLog(@"使用手机自带网络进行上网");} else { // 没有网络
NSLog(@"没有网络");
}
2 >"使用框架AFN"
// 1.获得网络监控的管理者
AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager];// 单例
// 2.设置网络状态改变后的处理
[mgr setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
// 当网络状态改变了, 就会调用这个block
switch (status) {
case AFNetworkReachabilityStatusUnknown: // 未知网络
NSLog(@"未知网络");
break;
case AFNetworkReachabilityStatusNotReachable: // 没有网络(断网)
NSLog(@"没有网络(断网)");
break;
case AFNetworkReachabilityStatusReachableViaWWAN: // 手机自带网络
NSLog(@"手机自带网络");
break;
case AFNetworkReachabilityStatusReachableViaWiFi: // WIFI
NSLog(@"WIFI");
break;
}
}];
// 3.开始监控
[mgr startMonitoring];
// 主动判断当前的网络是否可以用(方法)
// mgr.isReachableViaWiFi
// mgr.isReachableViaWWAN
-----------------------面试:--------------------------
面试:
ASI和AFN的区别?
// 回答角度:
1.性能
2.发送请求
3.处理服务器数据
4.ASI的特色
5.AFN的特色
1.性能
*ASI基于底层CFNetwork框架
*AFN基于NSURLConnection
*运行性能:ASI > AFN
- 处理服务器数据
1> AFN : 根据服务器返回的数据,进行自动解析(智能)
*服务器返回的是JSON数据,自动转换为NSDictionary 或者NSArray
*服务器返回的是XML数据,自动转换为NSXMLParser
*
2> ASI : 并没有对服务器的数据进行解析,直接返回NSData二进制数据
3.处理请求的过程
1> AFN : success 和 failure 两个block
2> ASI : 有3种方式处理请求过程(代理方法、SEL、block)
4.ASI的特色(重点)
1> 缓存
2> 下载和上传
*轻松监听请求进度
*轻松实现断点下载(ASI没有断点上传功能,断点上传功能得使用socket技术)
3> 提供了很多扩展接口(比如做数据压缩)
*
"ASIDataCompressor.h"
"ASIDataDecompressor.h"
*
4> ASIHTTPRequest继承自NSOperation
*能用队列统一管理所有请求
*请求之间能依赖
5> ASINetworkQueue
*统一管理所有请求
*如5个下载或上传请求 --> ASINetworkQueue : 监听5个请求的总进度
*监听所有请求的开始\失败\完毕
*shouldCancelAllRequestsOnFailure
*YES : 队列中某个请求失败了,其它所有请求都消失
*NO :队列中的某个请求失败了,其它请求不受影响,继续请求
5.AFN的特色(重点)
1、使用简单
2、方便
//-------------------// 封装方法 --重构----------------
- (void)viewDidLoad
{
// 封装前的效果 -- 代码庸于
[super viewDidLoad];
// // 设置导航栏按钮
// UIButton *leftButton = [[UIButton alloc] init];
// [leftButton setBackgroundImage:[UIImage imageWithName:@"navigationbar_friendsearch"] forState:UIControlStateNormal];
// [leftButton setBackgroundImage:[UIImage imageWithName:@"navigationbar_friendsearch_highlighted"] forState:UIControlStateHighlighted];
// // 设置按钮的尺寸为背景图片的尺寸
// leftButton.size = leftButton.currentBackgroundImage.size;
// // 监听按钮点击
// [leftButton addTarget:self action:@selector(friendSearch) forControlEvents:UIControlEventTouchUpInside];
// self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftButton];
//
// // 设置右边按钮
// UIButton *rightButton = [[UIButton alloc] init];
// [rightButton setBackgroundImage:[UIImage imageWithName:@"navigationbar_pop"] forState:UIControlStateNormal];
// [rightButton setBackgroundImage:[UIImage imageWithName:@"navigationbar_pop_highlighted"] forState:UIControlStateHighlighted];
// // 设置按钮的尺寸为背景图片的尺寸
// rightButton.size = rightButton.currentBackgroundImage.size;
// // 监听按钮点击
// [rightButton addTarget:self action:@selector(pop) forControlEvents:UIControlEventTouchUpInside];
// self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightButton];
// 封装后的效果,// 注意:target:(id)target
// 设置导航栏按钮
self.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_friendsearch" highImageName:@"navigationbar_friendsearch_highlighted" target:self action:@selector(friendSearch)];
self.navigationItem.rightBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_pop" highImageName:@"navigationbar_pop_highlighted" target:self action:@selector(pop)];
}
// 封装方法 // 注意:target:(id)target
- (UIBarButtonItem *)itemWithImageName:(NSString *)imageName highImageName:(NSString *)highImageName target:(id)target action:(SEL)action
{
UIButton *button = [[UIButton alloc] init];
[button setBackgroundImage:[UIImage imageWithName:imageName] forState:UIControlStateNormal];
[button setBackgroundImage:[UIImage imageWithName:highImageName] forState:UIControlStateHighlighted];
// 设置按钮的尺寸为背景图片的尺寸
button.size = button.currentBackgroundImage.size;
// 监听按钮点击
[button addTarget:self action:@selector(pop) forControlEvents:UIControlEventTouchUpInside];
return [[UIBarButtonItem alloc] initWithCustomView:button];
}
// ---------------------------// 封装方法--------------------
- (void)viewDidLoad
{
[super viewDidLoad];
// 封装前的效果
// UIViewController *home = [[UIViewController alloc] init];
// home.tabBarItem.title = @"首页";
// home.tabBarItem.image = [UIImage imageWithName:@"tabbar_home"];
//...
//...
// 封装后的效果
// 添加子控制器
HMHomeViewController *home = [[HMHomeViewController alloc] init];
[self addOneChlildVc:home title:@"首页" imageName:@"tabbar_home" selectedImageName:@"tabbar_home_selected"];
HMMessageViewController *message = [[HMMessageViewController alloc] init];
[self addOneChlildVc:message title:@"消息" imageName:@"tabbar_message_center" selectedImageName:@"tabbar_message_center_selected"];
HMDiscoverViewController *discover = [[HMDiscoverViewController alloc] init];
[self addOneChlildVc:discover title:@"发现" imageName:@"tabbar_discover" selectedImageName:@"tabbar_discover_selected"];
HMProfileViewController *profile = [[HMProfileViewController alloc] init];
[self addOneChlildVc:profile title:@"我" imageName:@"tabbar_profile" selectedImageName:@"tabbar_profile_selected"];
}
/**
* 添加一个子控制器
*
* @param childVc 子控制器对象
* @param title 标题
* @param imageName 图标
* @param selectedImageName 选中的图标
*/
- (void)addOneChlildVc:(UIViewController *)childVc title:(NSString *)title imageName:(NSString *)imageName selectedImageName:(NSString *)selectedImageName
{
childVc.view.backgroundColor = HMRandomColor;
// 设置标题
// 相当于同时设置了tabBarItem.title和navigationItem.title
childVc.title = title;
// childVc.tabBarItem.title = title; // tabbar标签上
// childVc.navigationItem.title = title; // 导航栏
// 设置图标
childVc.tabBarItem.image = [UIImage imageWithName:imageName];
// 设置选中的图标
UIImage *selectedImage = [UIImage imageWithName:selectedImageName];
if (iOS7) {
// 声明这张图片用原图(别渲染)
selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}
childVc.tabBarItem.selectedImage = selectedImage;
// 添加为tabbar控制器的子控制器
HMNavigationController *nav = [[HMNavigationController alloc] initWithRootViewController:childVc];
[self addChildViewController:nav];
}