iOS開發-網絡-合理封裝請求接口


概述

如今大多App都會與網絡打交道,作為開發者,合理的對網絡后台請求接口進行封裝十分重要。本文要介紹的就是一種常見的采用回調函數(方法)的網絡接口封裝,也算的是一種構架吧。

這個構架主要的idea是這樣的,把所有的接口封裝成一個類,在工程中隨時可以調用。並且利用代理Delegate構建回調方法(callBack),工程中隨處可以通過回調方法監聽網絡請求的反饋,也就是說,一旦得到了服務器反饋的數據,回調函數中的代碼就(才)會被激活。網絡請求基於AFNetworking(AFNetworking,非常有名的網絡請求第三方類庫),請求均為異步。如此構架,非常靈活很容易擴展和復用。

 

講解

要想使用本文介紹的構架,你首先需要掌握代理(Delegate),如果你不熟悉代理,這個構架對你來說將會很不解。對於不熟悉代理的同學們,建議你們去看一下資料。網絡請求其實說白了就是和服務器做一個數據交互,App把請求數據發給服務器,服務器返回給App一個反饋數據。請先看一下這個構架的示意圖,如下:

如上圖,這個構架的主要節點有三個,封裝網絡請求的類(接口類)、使用網絡請求的類(圖中的ViewController)、和服務器。

Ok~故事是這樣的,一個夜黑風高的...醉醺醺ViewController走在湖邊,為了找回被關在雲端的Data,他苦練數載終於參透了《接口類》,天地無情,今天是時候做個了斷了。

於是乎他從懷中拿出了傳說中的“接口類”,使用內力,實例化了一個接口類的對象,接着口中念出“接口類實例.delegate=self”,拔出利劍在身旁實現了“接口類”中的一個代理方法。然后調用接口的方法,方法通過內嵌的AFNetworking,向服務器發出了一道請求。又是一陣夜風吹過,三兩枯葉瑟瑟落下。ViewController酣意漸濃閉上了眼睛,現在他能做的唯有等待...

鏡頭一轉,月色中,在天上,在雲端的服務器,ViewController剛才發出的請求正在興風作浪,雲端值夜班的眾神絲毫不敢怠慢各個健步如飛,從數據庫中搜索着能化解這道請求的神器。

此時,ViewController困意漸濃,眼皮似墜了千金重物,意識也漸漸模糊。突然雲端顯出異像,ViewController頓時醒了過來,隱約可以看到,雲端有數據絲絲縷縷的流動,而自己懷中的“接口類”內嵌的AFNetworking也變得熾熱起來,HTTP反饋block像是要爆炸一樣的顫動着。ViewController豆大的汗珠從額頭滾下,再也不能淡定,口中叨咕着,快了,快來了... 一個霹靂,剛才用劍實現的代理方法金光一閃刺得ViewController捂住了雙眼。

一切都回歸安靜后,ViewController睜開眼睛,發現Data安靜的躺在代理方法的里面...

 

代碼示例

下面通過一個例子,來介紹一下。

打開Xcode我建了一個SingleViewApp,然后把AFNetworking加載進工程,如下圖:

我們 OpenWeatherMap提供的天氣預報的API作為例子,簡單地利用上述構架,做一個天氣預報的App

我們來看一下這個接口怎么用,很簡單:

例子:http://api.openweathermap.org/data/2.5/weather?q=beijing

參數:q=城市名字

返回Json:

{"coord":{"lon":116.4,"lat":39.91},"sys":{"type":1,"id":7405,"message":0.013,"country":"CN","sunrise":1435870233,"sunset":1435924003},"weather":[{"id":800,"main":"Clear","description":"Sky is Clear","icon":"01d"}],"base":"stations","main":{"temp":305.43,"pressure":1008,"humidity":28,"temp_min":302.15,"temp_max":308.71},"visibility":10000,"wind":{"speed":2,"deg":0},"clouds":{"all":0},"dt":1435900364,"id":1816670,"name":"Beijing","cod":200}

為了簡單我們的Demo App就只顯示 天氣和溫度,UI如下圖:

簡單直觀,點擊不同城市名字命名的按鈕,在Label中顯示其天氣狀況,關於UI不是今天討論的重點,我們主要討論網絡和接口。

 

現在開始重頭戲:接口類

新建一個類我把它命名為“Net”類,繼承NSObject,並導入"AFNetworking.h"頭文件:

//
//  Net.h
//  NetInterface
//
//  Created by Oliver on 15/7/3.
//

#import <Foundation/Foundation.h>
#import "AFNetworking.h"

@interface Net : NSObject

@end

這個類就是我們一直提到的接口類,我們要吧所有的網絡接口都寫到這個類里面。現在寫一個天氣預報接口作為例子。為天氣預報接口在Net類里聲明一個實例方法,由於這個接口需要傳得參數只有一個城市名稱,在Net類的H文件所以方法聲明如下:

/**
 *  獲得某城市的天氣
 *
 *  @param cityName 城市名稱
 */
-(void)getWeatherInfoWithCity:(NSString *)cityName;

一起看起來都很美好對不對?那么現在我要提一點,可能會被大家忽略的因素。由於我們實際開發的App調用接口的次數可能會很多,而且調用接口的類也很多,所以,Net這個類將會被多次的實例化,那么很有可能App的網絡層會變得很亂更有甚者會出Bug。所以,像這樣的接口類,我們有必要將它做成單例的,整個App共享一個接口類的實例。Ok,下面就來介紹獲取單例的方法:

在H文件聲明獲取單例的方法:

/**
 *  獲取Net類的單例
 *
 *  @return Net類的單例 實例(對象)
 */
+(Net *)getInstance;

接下來我們在Net.m文件實現獲取單例方法:(因為所有的接口請求都是HPPT請求,會用到AFNetworking的AFHTTPRequestOperationManager,所以我在getInstace方法里面把Manager也單例了)

#import "Net.h"

__strong static AFHTTPRequestOperationManager *AFHTTPMgr;
__strong static Net *NetInstance=nil;
@implementation Net

+(Net *)getInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        NetInstance= [[Net alloc]init];//初始化實例
        
        //一下是AFHTTPOerrationManager的配置
        AFHTTPMgr=[AFHTTPRequestOperationManager manager];
        //申明返回的結果是json類型
        AFHTTPMgr.responseSerializer=[AFJSONResponseSerializer serializer];
        //申明請求的數據是json類型
        AFHTTPMgr.requestSerializer=[AFJSONRequestSerializer serializer];
        //如果報接受類型不一致請替換一致text/xml或別的
        //AFHTTPMgr.responseSerializer.acceptableContentTypes= [NSSet setWithObject:@"text/xml"];
        //設置超時時間
        AFHTTPMgr.requestSerializer.timeoutInterval=5;
    });
    return NetInstance;
}

@end

上面代碼中,因為很變量的操作是在Block中做的,而block中不能對block外的變量進行重新更改,所以在程序的實現之前,聲明了:

__strong static AFHTTPRequestOperationManager *AFHTTPMgr;

__strong static Net *NetInstance=nil;

以便在單例的Block里面對其進行更改。

接下啦,我們就可以繼續去實現接口的方法getWeatherInfoWithCity:

-(void)getWeatherInfoWithCity:(NSString *)cityName{
    //接口地址
    NSString *url=[NSString stringWithFormat:@"http://api.openweathermap.org/data/2.5/weather"];
    //參數
    NSDictionary *parameters=[[NSDictionary alloc]initWithObjectsAndKeys:cityName,@"q", nil];
    //發請求
    [AFHTTPMgr GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
        //請求成功Block
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        //請求失敗Blick
    }];
}

如上代碼所示,這就是我們獲取天氣預報的接口,AFNetworking的請求成功和請求失敗的回調Block我們暫且空着,因為,我們要設置了Delegate再用。為什么我們要用代理而不是直接把想做的事情放在AFNetworking的Block里面呢?

答案其實顯而易見,Block是輕量級的代碼塊,雖然使用簡單,但是非常的封閉,與外部(Block外)進行數據交換的能力非常的有限。比如我們天氣預報的例子,我們的ViewController類希望通過服務器返回的天氣信息,改變UILabel的信息,而這個數據又在Net這個類的Block里面,沒辦法傳遞給ViewController,這就讓局面變得非常尷尬。所以我們要使用代理Delegate。其實Delegate的核心的作用就是來實現類之間的數據傳遞。現在請你,再次看一下上面的那張架構示意圖,我想你會對其有更深的理解。

下面,聲明Net類的代理,H文件的代碼如下:

在導入頭文件聲明和@interface之間 用@protocol聲明代理

//  Net.h
//  NetInterface
#import <Foundation/Foundation.h>
#import "AFNetworking.h"
//代理
@protocol NetDelegate <NSObject>
/**
 *  代理回調方法
 *
 *  @param feedbackInfo 服務器返回的數據
 */
-(void)getWeatherInfoSuccessFeedback:(id)feedbackInfo;
-(void)getWeatherInfoFailFeedback:(id)failInfo;

@end

@interface Net : NSObject

@property (nonatomic,strong) id<NetDelegate> delegate;

/**
 *  獲取Net類的單例
 *
 *  @return Net類的單例 實例(對象)
 */
+(Net *)getInstance;


/**
 *  獲得某城市的天氣
 *
 *  @param cityName 城市名稱
 */
-(void)getWeatherInfoWithCity:(NSString *)cityName;

@end

 

 

如上代碼,這是Net類的完整地H文件,我們在代理部分,聲明了兩個方法,一個請求成功、一個請求失敗。在代理中申明的代理方法,我們不用去實現它,而是在M文件總直接使用它。如果自己要使用的代理我們需要將代理聲明為自己的成員變量:

@property (nonatomic,strong) id<NetDelegate> delegate;

OK,現在讓我們回到getWeatherInfoWithCirt:方法,在Block中使用代理方法。代碼如下:

-(void)getWeatherInfoWithCity:(NSString *)cityName{
    //接口地址
    NSString *url=[NSString stringWithFormat:@"http://api.openweathermap.org/data/2.5/weather"];
    //參數
    NSDictionary *parameters=[[NSDictionary alloc]initWithObjectsAndKeys:cityName,@"q", nil];
    //發請求
    [AFHTTPMgr GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
        //請求成功Block
        //將返回數據傳入代理方法
        [self.delegate getWeatherInfoSuccessFeedback:responseObject];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        //請求失敗Blick
        //將錯誤信息傳入代理方法
        [self.delegate getWeatherInfoFailFeedback:error];
    }];
}

OK, 如果你一路跟下來,恭喜你,你的方法類構建完成了。你的每一個接口都可以按照以上的方式,寫成接口類的方法,然后用代理把它傳遞給其他類。

那么其他類怎么接受通過代理傳遞過來的數據呢?

打開ViewController,導入“Net.h”文件,在繼承聲明后添加實現<NetDelegate>代理,如下代碼:

//  ViewController.h
//  NetInterface
#import <UIKit/UIKit.h>
#import "Net.h"
@interface ViewController : UIViewController <NetDelegate>

@end

為了使用方便我添加了一個Net類的成員變量,KYNet:

@property Net *KYNet;

接下來我們要在M文件中使用接口嘍~~~代碼如下:

//
//  ViewController.m
//  NetInterface

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _KYNet=[Net getInstance];//得到單例
  
_KYNet.delegate=self; //將KYNet的代理與ViewController連接
}
//北京按鈕
- (IBAction)beijingTouched:(id)sender {
    
    [_KYNet getWeatherInfoWithCity:@"Beijing"];
}
//上海按鈕
- (IBAction)shanghaiTouched:(id)sender {
    [_KYNet getWeatherInfoWithCity:@"Shanghai"];
}
@end

如上代碼,當我們按下按鈕,就會使用我們的接口類發送請求

慢着~怎么接收服務器反饋數據?!

對了,下面我們通過實現Net的代理方法來接受處理數據,並更新到UILabel上,在M文件實現,Delegate的兩個方法:

-(void)getWeatherInfoSuccessFeedback:(id)feedbackInfo{
  //當服務器返回成功數據后,下列代碼被激活
    NSLog(@"%@",[feedbackInfo class]);
    NSDictionary *dic=feedbackInfo;
    NSArray *weather1=[dic objectForKey:@"weather"];
    NSDictionary *main1=[dic objectForKey:@"main"];
    NSDictionary *weather=[weather1 objectAtIndex:0];
    NSString *temp=[NSString stringWithFormat:@"%@",[main1 objectForKey:@"temp"]];
    NSString *weatherInfo=[NSString stringWithFormat:@"%@",[weather objectForKey:@"description"]];

    _condition.text=weatherInfo;
    _tem.text=temp;
}

-(void)getWeatherInfoFailFeedback:(id)failInfo{
    NSLog(@"%@",failInfo);
}

完活~

 Hit Run~~~

 

總結

手指頭敲酸了...寫博客比寫代碼累多啦TT。Ok總結一下。

本文的核心思想是把所有的網路請求封裝成一個類,向外部提供各個接口的請求方法,以便使用者發送請求;而當服務器返回反饋數據后,外部通過實現代理方法來獲得數據。這樣的架構的好處是非常靈活,低耦合,擴展簡單。實現的代理方法會在服務器返回數據的是時候自動被調用,結合異步的AFNetworking,開發者不用去擔心線程問題。這樣一來,程序主線的邏輯設計也會變得很簡單。用此構架封裝好的類,可以輕松的打包成SDK給別人使用。

謝謝大家,希望你們有所收獲,一篇文章花了我整整一天時間,如果對你有所幫助請幫忙點贊。如有問題,歡迎評論。若要轉載,請注明出處。

 


免責聲明!

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



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