該框架是一個通用的網絡層,能夠供給不一樣app的業務層調用。該框架封裝了AFNetworking,並且有些地方借鑑了YTKNetwork的設計思路:以對象的形式封裝並管理請求。html
它在功能上支持:ios
GitHub連接:SJNetworkgit
項目裏面附有demo:SJNetworkDemo項目文件夾程序員
在看架構圖以前,先簡單介紹一下該框架裏每一個類的職責:github
類名 | 職責 |
---|---|
SJNetwork | 總頭文件,只須要引入該文件便可使用該框架全部功能 |
SJNetworkProtocol | 定製了請求結束後的處理方法,從此可能還會擴展 |
SJNetworkHeader | 定義了回調block和一些枚舉類型 |
SJNetworkManager | 與業務層直接對接的類,包含了除配置接口外全部關於網絡請求功能的接口 |
SJNetworkBaseEngine | 全部負責發送請求類的基類 |
SJNetworkRequestEngine | 發送(GET,POST,PUT,DELETE)請求的類:支持設置緩存有效期,讀,寫和清理緩存 |
SJNetworkUploadEngine | 發送上傳請求的類:支持設置圖片類型和壓縮上傳,批量上傳 |
SJNetworkDownloadEngine | 發送下載請求的類:支持斷點續傳和後臺下載 |
SJNetworkRequestModel | 請求對象類:持有某個網絡請求的一些數據;好比請求url,請求體等) |
SJNetworkCacheManager | 緩存處理類:緩存的寫入,讀取,刪除 |
SJNetworkConfig | 配置類:配置服務器地址,debug模式等 |
SJNetworkUtils | 工具類:能夠用於生成緩存路徑,app版本號等 |
SJNetworkRequestPool | 請求對象池:用於存放正在進行的請求對象 |
SJNetworkCacheInfo | 緩存元數據:記錄其對應緩存數據的信息(版本號,緩存過時時間) |
SJNetworkDownloadResumeDataInfo | 未下載完成數據的元數據:記錄未下載完成數據的信息(已經下載的比例,下載數據的總長度,已經下載數據的長度) |
從架構圖中能夠看出:objective-c
SJNetworkManager
的接口來發送請求(或進行操做請求等操做),而實際進行工做的類實際上是SJNetworkRequestEngine
,SJNetworkUploadEngine
,SJNetworkDownloadEngine
,SJNetworkCacheManager
這些類。SJNetworkRequestModel
實例來管理,其管理者爲SJNetworkRequestPool
。SJNetworkConfig
和SJNetworkUtils
是能夠在任意地方調用的,由於要常常獲取用戶所作的一些配置以及調用一些經常使用的工具類方法。Step1:下載與導入框架編程
經過Cocoa pods:api
pod 'SJNetwork'
數組
最新的版本號爲1.2.0緩存
或者 手動將 SJNetwork
文件夾拖入到工程裏面。
Step2:引入頭文件:
若是是使用了Cocoapods:
import <SJNetwork/SJNetwork.h>
複製代碼
若是是手動拖入:
#import "SJNetwork.h"
複製代碼
由於配置對象是一個單例(SJNetworkConfig
),因此能夠在項目任何地方來使用。看一下該框架支持哪些配置項:
[SJNetworkConfig sharedConfig].baseUrl = @"http://v.juhe.cn";
複製代碼
[SJNetworkConfig sharedConfig].defailtParameters = @{@"app_version":[SJNetworkUtils appVersionStr],
@"platform":@"iOS"};
複製代碼
默認參數會拼接在全部請求的請求體中;
若是是GET請求,則拼接在url裏面。
[SJNetworkConfig sharedConfig].timeoutSeconds = 30;
複製代碼
超時時間默認爲20s。
[SJNetworkConfig sharedConfig].debugMode = YES;//默認爲NO
複製代碼
若是設置debug模式爲YES,則會打印出不少詳細的log,便於調試。
若是設置爲NO,則沒有log。
[[SJNetworkConfig sharedConfig] addCustomHeader:@{@"token":@"2j4jd9s74bfm9sn3"}];
複製代碼
或者
[[SJNetworkManager sharedManager] addCustomHeader:@{@"token":@"2j4jd9s74bfm9sn3"}];//實際上調用了SJNetworkConfig的addCustomHeader方法
複製代碼
添加的請求頭鍵值對會自動添加到全部的請求頭中;
若是鍵值對原來不存在,則添加;若是原來存在,則替換原有的。
在這裏定義GET,POST,PUT,DELETE請求爲普通的網絡請求,是由SJNetworkRequestManager
實現的。全部的這些普通的網絡請求都支持寫入和讀取緩存,可是默認是不支持的,由用戶來決定是否寫入,讀取緩存。
發送一個不支持寫入和讀取緩存的POST請求:
[[SJNetworkManager sharedManager] sendPostRequest:@"toutiao/index"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
success:^(id responseObject) {
NSLog(@"request succeed:%@",responseObject);
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode) {
NSLog(@"request failed:%@",error);
}];
複製代碼
發送一個支持寫入有效時間爲180秒並在緩存有效時讀取緩存的POST請求:
[[SJNetworkManager sharedManager] sendPostRequest:@"toutiao/index"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
loadCache:YES
cacheDuration:180
success:^(id responseObject) {
NSLog(@"request succeed:%@",responseObject);
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode) {
NSLog(@"request failed:%@",error);
}];
複製代碼
cacheDuration:緩存有效的時間,單位爲秒。
- 若是大於0,則進行緩存。
- 若是小於等於0,則不進行緩存。
loadCache:若是設置爲YES,則在發起請求前,先查看是否緩存有效(若是設置爲NO,則不管有沒有緩存,都進行網絡請求):
- 若是緩存存在並有效,則返回緩存,不進行網絡請求;
- 若是緩存不存在,或者存在但失效(時間過時)則刪除緩存(若是緩存存在)並進行網絡請求。
完整的帶有緩存判斷的普通網絡請求的流程圖:
緩存管理是由SJNetworkCacheManager
的單例來實現的,功能分爲緩存的讀取,刪除和計算。先來看一下緩存的讀取:
該框架支持單個緩存的讀取和多個緩存的讀取:
若是知道這個緩存對應的請求url,method,請求體,就能嘗試獲取它所對應的緩存對象:
舉個例子,若是想獲取上面有寫入緩存的網絡請求的緩存,就能夠用以下API:
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
completionBlock:^(id _Nullable cacheObject) {
NSLog(@"%@",cacheObject);
}];
複製代碼
注意,在緩存的讀取過程當中會有如下幾種狀況:
若是這個請求對應的緩存不存在,則會從block裏傳過來nil。
若是這個請求對應的緩存存在,可是失效了(有效期過了),則這個緩存就會被清除掉,並會在block裏傳過來nil。
若是這個請求對應的緩存存在並有效,則會從block裏傳過來緩存對象。
若是有些請求使用的是同一個url(可是不一樣的請求方法或者參數)並作了緩存,那麼經過以下方法能夠獲取它們的緩存:
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
completionBlock:^(NSArray * _Nullable cacheArr) {
NSLog(@"%@",cacheArr);
}];
複製代碼
若是有些請求使用的是同一個url以及請求方法,可是請求參數不一樣,那麼經過以下方法能夠獲取它們的緩存(用數組保存):
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
method:@"POST"
completionBlock:^(NSArray * _Nullable cacheArr) {
NSLog(@"%@",cacheArr);
}];
複製代碼
如今咱們知道這個框架在緩存的讀取上支持單個與批量讀取,接下來看一下緩存的刪除:
一樣地,該框架也支持緩存的單個與批量刪除。
若是你想刪除屬於某個特定url,method,請求參數的請求的緩存,可使用下面這個API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
completionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
複製代碼
若是你想刪除使用的是同一個url(可是不一樣的請求方法或者參數)的請求的緩存,可使用下面這個API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
completionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
複製代碼
若是你想刪除使用同一個url和method,可是不一樣請求參數的的請求的緩存,可使用下面這個API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
method:@"POST"
withCompletionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
複製代碼
看完了緩存的讀取和刪除,咱們來看一下緩存的計算:
緩存的計算只提供了一個接口,在block回調的時候會回傳一個文件個數,全部緩存的大小,以及帶有KB或MB的字符串:
[[SJNetworkManager sharedManager] calculateCacheSizeWithCompletionBlock:^(NSUInteger fileCount, NSUInteger totalSize, NSString *totalSizeString) {
NSLog(@"file count :%lu and total size:%lu total size string:%@",(unsigned long)fileCount,(unsigned long)totalSize, totalSizeString);
}];
複製代碼
fileCount:緩存文件的個數,爲整數
total size:單位爲字節
totalSizeString:帶有KB和MB轉化的字符串:在1024*1024字節之內以KB爲單位;之外以MB爲單位。例如:
file count :5 and total size:1298609 total size string:1.2385 MB
**注意:**所計算的緩存包括全部普通請求的緩存以及未下載完成,之後須要繼續下載的數據。
上傳圖片的功能是由SJNetworkUploadManager`類的單例實現的:支持上傳單個與多個
UIImage``對象,能夠設置壓縮比率(不設置時默認爲1,不壓縮)。
上傳單個UIImage對象,上傳前不對圖片進行壓縮:
[[SJNetworkManager sharedManager] sendUploadImageRequest:@"api"
parameters:nil
image:image_1
name:@"color"
mimeType:@"png"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
複製代碼
上傳多個UIImage對象,壓縮比率爲0.5:
[[SJNetworkManager sharedManager] sendUploadImagesRequest:@"api"
parameters:nil
images:@[image_1,image_2]
compressRatio:0.5
name:@"images"
mimeType:@"jpg"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
複製代碼
這裏的mimeType能夠設置爲jpg/JPG, png/PNG, jpeg/JPEG,做爲圖片上傳到服務器時的類型。須要注意的是,若是mimeType爲png/PNG的時候,設置的壓縮比率就是無效的,將會必定以原圖大小上傳。
考慮到上傳圖片的服務器可能與普通請求的服務器不一樣,特地增長了一個參數:ignoreBaseUrl
。若是該布爾值設置爲YES,則在SJNetworkConfig
單例裏面設置的baseUrl
就會被忽略掉,用戶須要在請求的第一個參數裏面將完整的請求url寫進去:
[[SJNetworkManager sharedManager] sendUploadImagesRequest:@"http://uploads.im/api"
ignoreBaseUrl:YES
parameters:nil
images:@[image_1,image_2]
compressRatio:0.5
name:@"images"
mimeType:@"jpg"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
複製代碼
還有一個方法,就是強制更改SJNetworkConfig
單例設置的baseUrl
:
[SJNetworkConfig sharedConfig].baseUrl = @"http://uploads.im";
[[SJNetworkManager sharedManager] sendUploadImagesRequest:@"api"
ignoreBaseUrl:NO
parameters:nil
images:@[image_3,image_4]
compressRatio:0.5
name:@"color"
mimeType:@"png"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
複製代碼
雖然看上去不是很優雅,但卻也是可行的:在請求全部普通網絡請求以前將baseUrl再次改回去便可。
暫時該框架還沒法支持多個baseUrl的功能,之後若是有研究到的話就會添加上去。
下載功能是由SJNetworkDownloadManager
的單例來實現的,支持斷點續傳以及後臺下載。
NSURLSessionDownloadTask
。在手機退出前臺,進入後臺後後仍然能夠下載。NSURLSessionDataTask
。在手機退出前臺後沒法繼續下載,可是經過框架內部的自動恢復下載機制,在回到前臺後就會繼續以前的下載。並且結合了NSOutputStream
實例,將下載下來的數據一點一點的寫在沙盒裏面,減小了內存的壓力,也就是支持大文件下載。綜上會是由四種狀況:
支持斷點續傳 | 不支持斷點續傳 | |
---|---|---|
支持後臺下載 | ✅ | ✅ |
不支持後臺下載 | ✅ | ✅ |
默認配置爲:支持斷點續傳,不支持後臺下載(由於除非是音樂視頻類等特殊app,後臺下載的操做可能會被Apple拒掉)。
[[SJNetworkManager sharedManager] sendDownloadRequest:@"wallpaper.jpg"
downloadFilePath:_imageFileLocalPath
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
複製代碼
若是支持斷點續傳,在返回失敗的回調裏面會傳過來未下載完成數據的路徑:
resumableDataPath
。
[[SJNetworkManager sharedManager] sendDownloadRequest:@"half-eatch.jpg"
downloadFilePath:_imageFileLocalPath
resumable:NO
backgroundSupport:NO
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
複製代碼
[[SJNetworkManager sharedManager] sendDownloadRequest:@"universe.jpg"
downloadFilePath:_imageFileLocalPath
resumable:YES
backgroundSupport:YES
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
複製代碼
[[SJNetworkManager sharedManager] sendDownloadRequest:@"iceberg.jpg"
downloadFilePath:_imageFileLocalPath
resumable:NO
backgroundSupport:YES
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
複製代碼
和上傳同樣,下載接口也都支持是否忽略baseUrl:
[[SJNetworkManager sharedManager] sendDownloadRequest:@"http://oih3a9o4n.bkt.clouddn.com/wallpaper.jpg"
ignoreBaseUrl:YES
downloadFilePath:_imageFileLocalPath
progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
self.progressView.progress = progress;
} success:^(id responseObject) {
NSLog(@"Download succeed!");
} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
NSLog(@"Download failed!");
}];
複製代碼
全部的下載請求都支持暫停,恢復和取消操做。而且這些操做都支持單獨與批量操做:
暫停單獨的下載請求:
[[SJNetworkManager sharedManager] suspendDownloadRequest:@"universe.jpg"];
複製代碼
暫停多個下載請求:
[[SJNetworkManager sharedManager] suspendDownloadRequests:@[@"universe.jpg",@"wallpaper.jpg"]];
複製代碼
暫停全部下載請求:
[[SJNetworkManager sharedManager] suspendAllDownloadRequests];
複製代碼
恢復單獨的正在暫停的下載請求:
[[SJNetworkManager sharedManager] resumeDownloadReqeust:@"universe.jpg"];
複製代碼
恢復多個正在暫停的下載請求:
[[SJNetworkManager sharedManager] resumeDownloadReqeusts:@[@"universe.jpg",@"wallpaper.jpg"]];
複製代碼
恢復全部正在暫停的下載請求:
[[SJNetworkManager sharedManager] resumeAllDownloadRequests];
複製代碼
取消單獨的下載請求:
[[SJNetworkManager sharedManager] cancelDownloadRequest:@"universe.jpg"];
複製代碼
取消多個下載請求:
[[SJNetworkManager sharedManager] cancelDownloadRequests:@[@"universe.jpg",@"wallpaper.jpg"]];
複製代碼
取消全部下載請求:
[[SJNetworkManager sharedManager] cancelAllDownloadRequests];
複製代碼
在該框架中,不管是普通的下載請求,上傳請求和下載請求,在發送請求以前都將用戶傳入的參數保存在專門的請求對象SJNetworkRequestModel
的實例裏面。而這些實例的管理工做交給了SJNetworkRequestPool
類的單例:
除了添加和移除請求對象之外,SJNetworkRequestPool
對請求的管理還包括:
BOOL remaining = [[SJNetworkManager sharedManager] remainingCurrentRequests];
if (remaining) {
NSLog(@"There is remaining request");
}
複製代碼
NSUInteger count = [[SJNetworkManager sharedManager] currentRequestCount];
if (count > 0) {
NSLog(@"There is %lu requests",(unsigned long)count);
}
複製代碼
[[SJNetworkManager sharedManager] logAllCurrentRequests];
複製代碼
請求的取消也分爲單個和批量的取消:
[[SJNetworkManager sharedManager] cancelCurrentRequestWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}];
複製代碼
[[SJNetworkManager sharedManager] cancelCurrentRequestWithUrl:@"toutiao/index"];
複製代碼
[[SJNetworkManager sharedManager] cancelDownloadRequests:@[@"toutiao/index",@"weixin/query"]];
複製代碼
[[SJNetworkManager sharedManager] cancelAllCurrentRequests];
複製代碼
若是將debug模式設置爲YES,則會打印出不少便於調試的log:
[SJNetworkConfig sharedConfig].debugMode = YES;
複製代碼
因爲重寫了SJNetworkRequestModel
的description
方法,因此在打印該對象的時候,普通的網絡請求,上傳請求,下載請求都有屬於本身的log,在這裏舉一個普通請求的log:
{
<SJNetworkRequestModel: 0x6040001fc100>
type: ordinary request
method: GET
url: http://v.juhe.cn/toutiao/index
parameters: {
"app_version" = "1.0";
key = 0c60;
platform = iOS;
type = top;
}
loadCache: YES
cacheDuration: 5 seconds
requestIdentifer:b4b36793efabad54a14389cf09bc8133_a6a72ddee1dd86825cb5707c500784f5_7b65261ff298c6a386c89a632bd17b39_30c9b994c268547f38a2f9af6f8c171f
task: <__NSCFLocalDataTask: 0x7f8e075320a0>{ taskIdentifier: 1 } { completed }
}
複製代碼
舉一個須要獲取緩存的網絡請求可是遇到緩存過時的狀況:
=========== Load cache info failed, reason:Cache is expired, begin to clear cache...
=========== Load cache failed: Cache info is invalid
=========== Faild to load cache, start to sending network request...
=========== Start requesting...
=========== url:http://v.juhe.cn/toutiao/index
=========== method:GET
=========== parameters:{
"app_version" = "1.0";
key = 0c60;
platform = iOS;
type = top;
}
=========== Request succeed!
=========== Request url:http://v.juhe.cn/toutiao/index
=========== Response object:{
code = 200,
msg = "",
data = {}
}
=========== Write cache succeed!
=========== cache object: {
code = 200,
msg = "",
data = {}
}
=========== Cache path: /Users/****/***/***/*******.cacheData
=========== Available duration: 180 seconds
複製代碼
在調試這個框架的時候使用了不少網絡資源,也看了好多文章,得到了不少幫助,因此不得不提一下:
如今社區裏二次封裝AFNetworking,上傳圖片,以及下載器的框架有不少,可是由於老是想寫一個屬於本身代碼風格的框架,並且網絡層對我本身仍是有些挑戰的,因此想試一試。
除去中間間隔的時間,整個框架基本成型的時間用了有一個多月,可是寫全英文的註釋,最後的重構(修改了架構,分離了一些類),命名的規範,修改bug,優化等事情又花去了半個月。特別是因爲本身對下載這一塊不熟,尤爲是斷點續傳,後臺下載這兩方面更是沒有實戰經驗,在寫的時候也花了很多時間。
但願能多給出寶貴意見和建議,我本身發現有不足的地方也會更新~
本篇已同步到我的博客:傳送門
---------------------------- 2018年7月17日更新 ----------------------------
注意注意!!!
筆者在近期開通了我的公衆號,主要分享編程,讀書筆記,思考類的文章。
由於公衆號天天發佈的消息數有限制,因此到目前爲止尚未將全部過去的精選文章都發布在公衆號上,後續會逐步發佈的。
並且由於各大博客平臺的各類限制,後面還會在公衆號上發佈一些短小精幹,以小見大的乾貨文章哦~
掃下方的公衆號二維碼並點擊關注,期待與您的共同成長~