YTKNetwork是一個(gè)對AFNetworking封裝的一個(gè)框架寺滚,雖然二者底層原理相同,但使用方法和使用效果是大不相同的配猫。YTKNetwork 提供了以下更高級的功能:
1.支持按時(shí)間緩存網(wǎng)絡(luò)請求內(nèi)容
2.支持按版本號緩存網(wǎng)絡(luò)請求內(nèi)容
3.支持統(tǒng)一設(shè)置服務(wù)器和 CDN 的地址
4.支持檢查返回 JSON 內(nèi)容的合法性
5.支持文件的斷點(diǎn)續(xù)傳
6.支持 block 和 delegate 兩種模式的回調(diào)方式
7.支持批量的網(wǎng)絡(luò)請求發(fā)送流济,并統(tǒng)一設(shè)置它們的回調(diào)(實(shí)現(xiàn)在 YTKBatchRequest 類中)
支持方便地設(shè)置有相互依賴的網(wǎng)絡(luò)請求的發(fā)送,例如:發(fā)送請求 A蛮寂,根據(jù)請求 A 的結(jié)果,選擇性的發(fā)送請求 B
和 C易茬,再根據(jù) B 和 C 的結(jié)果共郭,選擇性的發(fā)送請求 D。(實(shí)現(xiàn)在 YTKChainRequest 類中)
支持網(wǎng)絡(luò)請求 URL 的 filter疾呻,可以統(tǒng)一為網(wǎng)絡(luò)請求加上一些參數(shù),或者修改一些路徑写半。
YTKNetwork包含了這幾個(gè)類:1岸蜗、YTKNetworkConfig (設(shè)置域名) 2、YTKRequest (網(wǎng)絡(luò)請求)3叠蝇、YTKBatchRequest (請求多個(gè)類 )4璃岳、YTKChainRequest (依賴請求)5、YTKBaseRequest(YTKRequest的父類)
YTKNetwork 的基本思想
YTKNetwork 的基本的思想是把每一個(gè)網(wǎng)絡(luò)請求封裝成對象悔捶。所以使用 YTKNetwork铃慷,你的每一個(gè)請求都需要繼承 YTKRequest 類,通過覆蓋父類的一些方法來構(gòu)造指定的網(wǎng)絡(luò)請求蜕该。
集約式和離散式API
集約式API
介紹:所有API的調(diào)用只有一個(gè)類犁柜,然后這個(gè)類接收API名字,API參數(shù)堂淡,以及回調(diào)著陸點(diǎn)馋缅,即項(xiàng)目中的每個(gè)請求都會走統(tǒng)一的入口扒腕,對外暴露了請求的 URL 和 Param 以及請求方式,入口一般都是通過單例 來實(shí)現(xiàn)萤悴,AFNetworking 的官方 demo 就是采用的集約式的方式對網(wǎng)絡(luò)請求進(jìn)行的封裝瘾腰,也是目前比較流行的網(wǎng)絡(luò)請求方式。
優(yōu)點(diǎn):使用便捷覆履,能實(shí)現(xiàn)快速開發(fā)
缺點(diǎn):
1.對每個(gè)請求的定制型不夠強(qiáng)
2.不方便后期業(yè)務(wù)拓展
我們常用的AFNetworking框架就是集約式蹋盆,在簡單程序中AFNetworking 將請求邏輯寫在 Controller 中比YTK更加方便,也不用一個(gè)個(gè)請求新建不同的request類硝全。而YTKNetworking則是離散式的
離散式API
介紹:離散型API調(diào)用是這樣的栖雾,一個(gè)API對應(yīng)于一個(gè)APIManager,然后這個(gè)APIManager只需要提供參數(shù)就能起飛柳沙,API名字岩灭、著陸方式都已經(jīng)集成入APIManager中。即每個(gè)網(wǎng)絡(luò)請求類都是一個(gè)對象赂鲤,它的 URL 以及請求方式和響應(yīng)方式 均不暴露給外部調(diào)用噪径。只能內(nèi)部通過 重載或?qū)崿F(xiàn)協(xié)議 的方式來指定,外部調(diào)用只需要傳 Param 即可数初,YTKNetwork就是采用的這種網(wǎng)絡(luò)請求方式找爱。
優(yōu)點(diǎn):URL 以及請求和響應(yīng)方式不暴露給外部,避免外部調(diào)用的時(shí)候?qū)戝e
業(yè)務(wù)方使用起來較簡單泡孩,業(yè)務(wù)使用者不需要去關(guān)心它的內(nèi)部實(shí)現(xiàn)
可定制性強(qiáng)车摄,可以為每個(gè)請求指定請求的超時(shí)時(shí)間以及緩存的周期
缺點(diǎn):
網(wǎng)絡(luò)層需要業(yè)務(wù)實(shí)現(xiàn)方去寫,變相的增加了部分工作量
文件增多仑鸥,程序包會變大一些
YTKNetworkConfig 類
YTKNetworkConfig 類有兩個(gè)作用:
統(tǒng)一設(shè)置網(wǎng)絡(luò)請求的服務(wù)器和 CDN 的地址吮播。
管理網(wǎng)絡(luò)請求的 YTKUrlFilterProtocol 實(shí)例
我們?yōu)槭裁葱枰y(tǒng)一設(shè)置服務(wù)器地址呢?因?yàn)椋?/p>
按照設(shè)計(jì)模式里的 Do Not Repeat Yourself 原則眼俊,我們應(yīng)該把服務(wù)器地址統(tǒng)一寫在一個(gè)地方意狠。
在實(shí)際業(yè)務(wù)中,我們的測試人員需要切換不同的服務(wù)器地址來測試疮胖。統(tǒng)一設(shè)置服務(wù)器地址到 YTKNetworkConfig 類中环戈,也便于我們統(tǒng)一切換服務(wù)器地址。
具體的用法是澎灸,在程序剛啟動的回調(diào)中院塞,設(shè)置好 YTKNetworkConfig 的信息,如下所示:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
YTKNetworkConfig *config = [YTKNetworkConfig sharedConfig];
config.baseUrl = @"http://yuantiku.com";
config.cdnUrl = @"http://fen.bi";
}
設(shè)置好之后性昭,所有的網(wǎng)絡(luò)請求都會默認(rèn)使用 YTKNetworkConfig 中 baseUrl 參數(shù)指定的地址拦止。
大部分企業(yè)應(yīng)用都需要對一些靜態(tài)資源(例如圖片、js糜颠、css)使用 CDN创泄。YTKNetworkConfig 的 cdnUrl 參數(shù)用于統(tǒng)一設(shè)置這一部分網(wǎng)絡(luò)請求的地址艺玲。
當(dāng)我們需要切換服務(wù)器地址時(shí),只需要修改 YTKNetworkConfig 中的 baseUrl 和 cdnUrl 參數(shù)即可鞠抑。
YTKRequest
YTK把每個(gè)請求實(shí)例化饭聚,管理它的生命周期,也可以管理多個(gè)請求搁拙,在github的基礎(chǔ)教程里面我們可以看到Y(jié)TK是把每個(gè)網(wǎng)絡(luò)請求都封裝成對象秒梳,每一種網(wǎng)絡(luò)請求繼承 YTKRequest 類后,需要用方法覆蓋(overwrite)的方式箕速,來指定網(wǎng)絡(luò)請求的具體信息酪碘。如下是一個(gè)示例:
假如我們要向網(wǎng)址 http://www.yuantiku.com/iphone/register 發(fā)送一個(gè) POST 請求,請求參數(shù)是 username 和 password盐茎。那么兴垦,這個(gè)類應(yīng)該如下所示:
// RegisterApi.h
#import "YTKRequest.h"
@interface RegisterApi : YTKRequest
- (id)initWithUsername:(NSString *)username password:(NSString *)password;
@end
// RegisterApi.m
#import "RegisterApi.h"
@implementation RegisterApi {
NSString *_username;
NSString *_password;
}
- (id)initWithUsername:(NSString *)username password:(NSString *)password {
self = [super init];
if (self) {
_username = username;
_password = password;
}
return self;
}
- (NSString *)requestUrl {
// “ http://www.yuantiku.com ” 在 YTKNetworkConfig 中設(shè)置,這里只填除去域名剩余的網(wǎng)址信息
return @"/iphone/register";
}
- (YTKRequestMethod)requestMethod {
return YTKRequestMethodPOST;
}
- (id)requestArgument {
return @{
@"username": _username,
@"password": _password
};
}
@end
在上面這個(gè)示例中字柠,我們可以看到:
- 我們通過覆蓋 YTKRequest 類的
requestUrl
方法探越,實(shí)現(xiàn)了指定網(wǎng)址信息。并且我們只需要指定除去域名剩余的網(wǎng)址信息窑业,因?yàn)橛蛎畔⒃?YTKNetworkConfig 中已經(jīng)設(shè)置過了钦幔。 - 我們通過覆蓋 YTKRequest 類的
requestMethod
方法,實(shí)現(xiàn)了指定 POST 方法來傳遞參數(shù)常柄。 - 我們通過覆蓋 YTKRequest 類的
requestArgument
方法鲤氢,提供了 POST 的信息。這里面的參數(shù)username
和password
如果有一些特殊字符(如中文或空格)西潘,也會被自動編碼卷玉。
調(diào)用 RegisterApi
在構(gòu)造完成 RegisterApi 之后,具體如何使用呢喷市?我們可以在登錄的 ViewController 中相种,調(diào)用 RegisterApi,并用 block 的方式來取得網(wǎng)絡(luò)請求結(jié)果:
- (void)loginButtonPressed:(id)sender {
NSString *username = self.UserNameTextField.text;
NSString *password = self.PasswordTextField.text;
if (username.length > 0 && password.length > 0) {
RegisterApi *api = [[RegisterApi alloc] initWithUsername:username password:password];
[api startWithCompletionBlockWithSuccess:^(YTKBaseRequest *request) {
// 你可以直接在這里使用 self
NSLog(@"succeed");
} failure:^(YTKBaseRequest *request) {
// 你可以直接在這里使用 self
NSLog(@"failed");
}];
}
}
注意:你可以直接在 block 回調(diào)中使用 self东抹,不用擔(dān)心循環(huán)引用。因?yàn)?YTKRequest 會在執(zhí)行完 block 回調(diào)之后沃测,將相應(yīng)的 block 設(shè)置成 nil缭黔。從而打破循環(huán)引用。
除了 block 的回調(diào)方式外蒂破,YTKRequest 也支持 delegate 方式的回調(diào):
- (void)loginButtonPressed:(id)sender {
NSString *username = self.UserNameTextField.text;
NSString *password = self.PasswordTextField.text;
if (username.length > 0 && password.length > 0) {
RegisterApi *api = [[RegisterApi alloc] initWithUsername:username password:password];
api.delegate = self;
[api start];
}
}
- (void)requestFinished:(YTKBaseRequest *)request {
NSLog(@"succeed");
}
- (void)requestFailed:(YTKBaseRequest *)request {
NSLog(@"failed");
}
驗(yàn)證服務(wù)器返回內(nèi)容
有些時(shí)候馏谨,由于服務(wù)器的 Bug,會造成服務(wù)器返回一些不合法的數(shù)據(jù)附迷,如果盲目地信任這些數(shù)據(jù)惧互,可能會造成客戶端 Crash哎媚。如果加入大量的驗(yàn)證代碼,又使得編程體力活增加喊儡,費(fèi)時(shí)費(fèi)力拨与。
使用 YTKRequest 的驗(yàn)證服務(wù)器返回值功能,可以很大程度上節(jié)省驗(yàn)證代碼的編寫時(shí)間艾猜。
例如买喧,我們要向網(wǎng)址 http://www.yuantiku.com/iphone/users 發(fā)送一個(gè) GET 請求,請求參數(shù)是 userId 匆赃。我們想獲得某一個(gè)用戶的信息淤毛,包括他的昵稱和等級,我們需要服務(wù)器必須返回昵稱(字符串類型)和等級信息(數(shù)值類型)算柳,則可以覆蓋 jsonValidator 方法低淡,實(shí)現(xiàn)簡單的驗(yàn)證。
- (id)jsonValidator {
return @{
@"nick": [NSString class],
@"level": [NSNumber class]
};
}
斷點(diǎn)續(xù)傳
要啟動斷點(diǎn)續(xù)傳功能瞬项,只需要覆蓋 resumableDownloadPath 方法蔗蹋,指定斷點(diǎn)續(xù)傳時(shí)文件的存儲路徑即可,文件會被自動保存到此路徑滥壕。如下代碼將剛剛的取圖片的接口改造成了支持?jǐn)帱c(diǎn)續(xù)傳:
@implementation GetImageApi {
NSString *_imageId;
}
- (id)initWithImageId:(NSString *)imageId {
self = [super init];
if (self) {
_imageId = imageId;
}
return self;
}
- (NSString *)requestUrl {
return [NSString stringWithFormat:@"/iphone/images/%@", _imageId];
}
- (BOOL)useCDN {
return YES;
}
- (NSString *)resumableDownloadPath {
NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *cachePath = [libPath stringByAppendingPathComponent:@"Caches"];
NSString *filePath = [cachePath stringByAppendingPathComponent:_imageId];
return filePath;
}
@end
按時(shí)間緩存內(nèi)容
剛剛我們寫了一個(gè) GetUserInfoApi 纸颜,這個(gè)網(wǎng)絡(luò)請求是獲得用戶的一些資料。
我們想像這樣一個(gè)場景绎橘,假設(shè)你在完成一個(gè)類似微博的客戶端胁孙,GetUserInfoApi 用于獲得你的某一個(gè)好友的資料,因?yàn)楹糜巡⒉粫敲搭l繁地更改昵稱称鳞,那么短時(shí)間內(nèi)頻繁地調(diào)用這個(gè)接口很可能每次都返回同樣的內(nèi)容涮较,所以我們可以給這個(gè)接口加一個(gè)緩存。
在如下示例中冈止,我們通過覆蓋 cacheTimeInSeconds 方法狂票,給 GetUserInfoApi 增加了一個(gè) 3 分鐘的緩存,3 分鐘內(nèi)調(diào)用調(diào) Api 的 start 方法熙暴,實(shí)際上并不會發(fā)送真正的請求闺属。
@implementation GetUserInfoApi {
NSString *_userId;
}
- (id)initWithUserId:(NSString *)userId {
self = [super init];
if (self) {
_userId = userId;
}
return self;
}
- (NSString *)requestUrl {
return @"/iphone/users";
}
- (id)requestArgument {
return @{ @"id": _userId };
}
- (id)jsonValidator {
return @{
@"nick": [NSString class],
@"level": [NSNumber class]
};
}
- (NSInteger)cacheTimeInSeconds {
// 3 分鐘 = 180 秒
return 60 * 3;
}
@end
該緩存邏輯對上層是透明的,所以上層可以不用考慮緩存邏輯周霉,每次調(diào)用 GetUserInfoApi 的 start 方法即可掂器。GetUserInfoApi 只有在緩存過期時(shí),才會真正地發(fā)送網(wǎng)絡(luò)請求俱箱。