IOS開(kāi)源網(wǎng)絡(luò)庫(kù)AFNetworking已經(jīng)成為了IOS程序開(kāi)發(fā)的首選、亦可以說(shuō)是必備,無(wú)數(shù)IOS
的“先哲”們撰文稱贊此庫(kù)良好的設(shè)計(jì)和功能的強(qiáng)大,以致后來(lái)的開(kāi)發(fā)者在項(xiàng)目中都不會(huì)去
考慮其它的網(wǎng)絡(luò)庫(kù),而直接選擇AFNetworking。這里就來(lái)總結(jié)一下使用它的一般程式,在
總結(jié)過(guò)程中學(xué)習(xí)和成長(zhǎng)。
HttpClient
我們?cè)谑褂肁FHTTPSessionManager的時(shí)候,一般均會(huì)對(duì)其進(jìn)行封裝,以滿足App的各種要求。
所以這里選擇對(duì)其進(jìn)行擴(kuò)展,設(shè)計(jì)如下:
@interface LNHttpClient : AFHTTPSessionManager
+ (instancetype)sharedClient;
+ (void)setTimeout:(NSTimeInterval)timeout;
+ (void)setResponseType:(LNHttpResponseType)type;
- (void)setHttpHeader;
@end
該繼承類的實(shí)現(xiàn)需要注意如下幾點(diǎn):
- 繼承AFHTTPSessionManager免不了對(duì)initWithBaseURL的覆寫(xiě),并在其中注冊(cè)一些通知,用于
檢測(cè)用戶的登陸和登出,以便Client做相應(yīng)的處理。 - setHttpHeader可以設(shè)置Http頭部,比如token、userId等等。
- 中間兩個(gè)方法使得開(kāi)發(fā)者可以控制每一次請(qǐng)求的timeout和responseType。
APIService
APIService是所有網(wǎng)絡(luò)請(qǐng)求的入口,所有Service的網(wǎng)絡(luò)調(diào)用均使用該類來(lái)完成,我們項(xiàng)目中
使用proto-buf來(lái)作為數(shù)據(jù)交換的類型,其設(shè)計(jì)力求簡(jiǎn)介:
typedef void (^APISuccessHandler)(id responseObject);
typedef void (^APIFailureHandler)(NSInteger code, NSString *msg);
@interface APIService : NSObject
+ (NSURLSessionTask *)POST:(NSString *)relativePath
protobuf:(NSData *)proto
modelClass:(Class)modelClass
success:(APISuccessHandler)success
failure:(APIFailureHandler)failure;
+ (NSURLSessionTask *)GET:(NSString *)relativePath
protobuf:(NSData *)proto
modelClass:(Class)modelClass
success:(APISuccessHandler)success
failure:(APIFailureHandler)failure;
該類的設(shè)計(jì)是對(duì)于AFHTTPSessionManager的封裝,是所有Service類的基類。實(shí)現(xiàn)要點(diǎn):
- 定義了兩個(gè)block,分別用來(lái)處理成功和失敗的調(diào)用。
- modelClass用來(lái)解析ContentType的數(shù)據(jù),此處是proto-buf。
- 此類派生的各個(gè)Service來(lái)處理不同的業(yè)務(wù)場(chǎng)景。
AFHTTPRequestSerializer覆寫(xiě)
在客戶端發(fā)送請(qǐng)求時(shí),我們有時(shí)需要設(shè)置request的content-Type,以便于服務(wù)端能夠根據(jù)
content-Type來(lái)處理不同格式的數(shù)據(jù),比如AFNetworking中自帶的AFJSONRequestSerializer,
就能夠把請(qǐng)求的數(shù)據(jù)轉(zhuǎn)化為JSON格式,并且把content-Type設(shè)置為application/json。這里
我們的請(qǐng)求數(shù)據(jù)格式為proto-buf,而AF庫(kù)并沒(méi)有給我們提供相關(guān)的默認(rèn)實(shí)現(xiàn),這時(shí)候就需要
我們自己來(lái)實(shí)現(xiàn)AFProtoRequestSerializer。
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
return [super requestBySerializingRequest:request withParameters:parameters error:error];
}
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
if (parameters) {
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:self.writingOptions error:error]];
}
return mutableRequest;
}
上面的代碼是AFJSONRequestSerializer的主要覆寫(xiě)方法。同理我們只需要仿照這個(gè)例子來(lái)
實(shí)現(xiàn)AFProtoRequestSerializer即可。
URL緩存
說(shuō)起HTTP請(qǐng)求,就不得不聊到緩存,每次去請(qǐng)求相同的URL的數(shù)據(jù)顯然是不劃算的,所以將
每次URL請(qǐng)求的數(shù)據(jù)緩存起來(lái),以后當(dāng)有相同的URL請(qǐng)求時(shí),直接使用緩存數(shù)據(jù)即可。使用
緩存一般有兩種選擇。
-
NSURLCache
系統(tǒng)提供的默認(rèn)緩存,使用該方式可以減少開(kāi)發(fā)的難度,但是在使用過(guò)程中需要注意的
是- 該緩存只能用在GET請(qǐng)求上,并不支持Post。
- 緩存方式盡量選擇NSURLRequestReturnCacheDataDontLoad,如果有緩存直接返回?cái)?shù)據(jù)
如果沒(méi)有緩存則不發(fā)送請(qǐng)求,返回nil,我們手工來(lái)再發(fā)一次請(qǐng)求。這樣做可以規(guī)避一
些蘋(píng)果實(shí)現(xiàn)緩存的坑。
-
**URLCache
自己實(shí)現(xiàn)的緩存,我們只需要擴(kuò)展NSURLCache即可,使用擴(kuò)展的cache來(lái)代替原生的實(shí)例。
這樣我們就可以人為控制緩存的URL范圍和數(shù)據(jù)存儲(chǔ)了,簡(jiǎn)單實(shí)現(xiàn)如下:@implementation LNURLCache - (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request { if ([self shouldManuallyCacheRequest:request]) { [[LNCache globalCache] setObject:cachedResponse forKey:request.URL.absoluteString withTimeoutInterval:kTimeOneYear]; } else { [super storeCachedResponse:cachedResponse forRequest:request]; } } - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request { if ([self shouldManuallyCacheRequest:request]) { return (NSCachedURLResponse *)[[LNCache globalCache] objectForKey:request.URL.absoluteString]; } else { return [super cachedResponseForRequest:request]; } } - (BOOL)shouldManuallyCacheRequest:(NSURLRequest *)request { return [request.URL.host hasSuffix:kCDNHostName]; } @end
##總結(jié)
通過(guò)以上講解,相信你可以從容地處理好網(wǎng)絡(luò)請(qǐng)求模塊的設(shè)計(jì)。