HTTP和ASI框架的使用

作者: 光明程辉 | 来源:发表于2016-02-22 23:07 被阅读235次

    // 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. "使用框架--网络状态监控"

    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. 处理服务器数据
      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];
    }

    相关文章

      网友评论

        本文标题:HTTP和ASI框架的使用

        本文链接:https://www.haomeiwen.com/subject/iaubkttx.html