SDWebImage擴充套件二次載入圖片
隨著網路安全的意識加重,相信很多同學都遇到過圖片二次載入的需求,即第一次請求獲得圖片的URL,第二次請求獲得圖片。在iOS中圖片的載入庫很多,而SDWebImage絕對是其中的翹楚,也是廣大iOSer們開發時候的首選。這裡簡單介紹一下自己在專案中在SDWebImage基礎上擴充套件的圖片二次載入請求。為了不破壞SDWebImage原有的執行緒控制和載入邏輯,所以獲取圖片的URL的執行緒仍然使用繼承SDWebImageOperation的方式來開展,這裡取名為:SDWebImageFetchURLOperation,程式碼如下:
SDWebImageFetchURLOperation.h
/* * This file is part of the SDWebImage package. * (c) Olivier Poitrey <
[email protected]> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ #import <Foundation/Foundation.h> #import "SDWebImageDownloader.h" #import "SDWebImageOperation.h" @interface SDWebImageFetchURLOperation : NSOperation <SDWebImageOperation> /** * The request used by the operation's connection. */ @property (strong, nonatomic) NSMutableURLRequest *request; /** * Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default. * * This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`. */ @property (nonatomic, assign) BOOL shouldUseCredentialStorage; /** * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`. * * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present. */ @property (nonatomic, strong) NSURLCredential *credential; /** * Initializes a `SDWebImageFetchURLOperation` object * * @see SDWebImageDownloaderOperation * * @param request the URL request * @param completedBlock the block executed when the download is done. * @note the completed block is executed on the main queue for success. If errors are found, there is a chance the block will be executed on a background queue * @param cancelBlock the block executed if the download (operation) is cancelled * * @return the initialized instance */ - (id)initWithRequest:(NSMutableURLRequest *)request completed:(SDWebImageFetchURLCompletedBlock)completedBlock cancelled:(SDWebImageNoParamsBlock)cancelBlock; @end
SDWebImageFetchURLOperation.m
/* * This file is part of the SDWebImage package. * (c) Olivier Poitrey <[email protected]> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ #import "SDWebImageFetchURLOperation.h" #import "SDWebImageDecoder.h" #import "UIImage+MultiFormat.h" #import <ImageIO/ImageIO.h> #import "SDWebImageManager.h" @interface SDWebImageFetchURLOperation () <NSURLConnectionDataDelegate> @property (copy, nonatomic) SDWebImageFetchURLCompletedBlock completedBlock; @property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock; @property (assign, nonatomic, getter = isExecuting) BOOL executing; @property (assign, nonatomic, getter = isFinished) BOOL finished; @property (assign, nonatomic) NSInteger expectedSize; @property (strong, nonatomic) NSMutableData *urlData; @property (strong, nonatomic) NSURLConnection *connection; @property (strong, atomic) NSThread *thread; #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId; #endif @end @implementation SDWebImageFetchURLOperation { // size_t width, height; UIImageOrientation orientation; BOOL responseFromCached; } @synthesize executing = _executing; @synthesize finished = _finished; - (id)initWithRequest:(NSMutableURLRequest *)request completed:(SDWebImageFetchURLCompletedBlock)completedBlock cancelled:(SDWebImageNoParamsBlock)cancelBlock { if ((self = [super init])) { self.request = request; _shouldUseCredentialStorage = YES; _completedBlock = [completedBlock copy]; _cancelBlock = [cancelBlock copy]; _executing = NO; _finished = NO; _expectedSize = 0; responseFromCached = YES; // Initially wrong until `connection:willCacheResponse:` is called or not called } return self; } - (void)start { @synchronized (self) { if (self.isCancelled) { self.finished = YES; [self reset]; return; } self.executing = YES; self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; self.thread = [NSThread currentThread]; } [self.connection start]; if (self.connection) { [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self]; if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_5_1) { // Make sure to run the runloop in our background thread so it can process downloaded data // Note: we use a timeout to work around an issue with NSURLConnection cancel under iOS 5 // not waking up the runloop, leading to dead threads (see https://github.com/rs/SDWebImage/issues/466) CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, false); } else { CFRunLoopRun(); } if (!self.isFinished) { [self.connection cancel]; [self connection:self.connection didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorTimedOut userInfo:@{NSURLErrorFailingURLErrorKey : self.request.URL}]]; } } else { if (self.completedBlock) { self.completedBlock(nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES); } } #if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 if (self.backgroundTaskId != UIBackgroundTaskInvalid) { [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskId]; self.backgroundTaskId = UIBackgroundTaskInvalid; } #endif } - (void)cancel { @synchronized (self) { if (self.thread) { [self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO]; } else { [self cancelInternal]; } } } - (void)cancelInternalAndStop { if (self.isFinished) return; [self cancelInternal]; CFRunLoopStop(CFRunLoopGetCurrent()); } - (void)cancelInternal { if (self.isFinished) return; [super cancel]; if (self.cancelBlock) self.cancelBlock(); if (self.connection) { [self.connection cancel]; [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self]; // As we cancelled the connection, its callback won't be called and thus won't // maintain the isFinished and isExecuting flags. if (self.isExecuting) self.executing = NO; if (!self.isFinished) self.finished = YES; } [self reset]; } - (void)done { self.finished = YES; self.executing = NO; [self reset]; } - (void)reset { self.cancelBlock = nil; self.completedBlock = nil; self.connection = nil; self.urlData = nil; self.thread = nil; } - (void)setFinished:(BOOL)finished { [self willChangeValueForKey:@"isFinished"]; _finished = finished; [self didChangeValueForKey:@"isFinished"]; } - (void)setExecuting:(BOOL)executing { [self willChangeValueForKey:@"isExecuting"]; _executing = executing; [self didChangeValueForKey:@"isExecuting"]; } - (BOOL)isConcurrent { return YES; } #pragma mark NSURLConnection (delegate) - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { if (![response respondsToSelector:@selector(statusCode)] || [((NSHTTPURLResponse *)response) statusCode] < 400) { NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0; self.expectedSize = expected; self.urlData = [[NSMutableData alloc] initWithCapacity:expected]; } else { [self.connection cancel]; [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; if (self.completedBlock) { self.completedBlock(nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES); } CFRunLoopStop(CFRunLoopGetCurrent()); [self done]; } } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [self.urlData appendData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)aConnection { SDWebImageFetchURLCompletedBlock completionBlock = self.completedBlock; @synchronized(self) { CFRunLoopStop(CFRunLoopGetCurrent()); self.thread = nil; self.connection = nil; [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; } if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) { responseFromCached = NO; } if (completionBlock) { NSString *url = [[NSString alloc] initWithData:self.urlData encoding:NSUTF8StringEncoding]; if (!url) { completionBlock(nil, [NSError errorWithDomain:@"SDWebImageErrorDomain" code:0 userInfo:@{NSLocalizedDescriptionKey : @"fetch image url failed"}], YES); } else { completionBlock(url, nil, YES); } } self.completionBlock = nil; [self done]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { CFRunLoopStop(CFRunLoopGetCurrent()); [[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:nil]; if (self.completedBlock) { self.completedBlock(nil, error, YES); } [self done]; } - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { responseFromCached = NO; // If this method is called, it means the response wasn't read from cache if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) { // Prevents caching of responses return nil; } else { return cachedResponse; } } - (BOOL)shouldContinueWhenAppEntersBackground { return SDWebImageDownloaderContinueInBackground; } - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection __unused *)connection { return self.shouldUseCredentialStorage; } - (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; } else { if ([challenge previousFailureCount] == 0) { if (self.credential) { [[challenge sender] useCredential:self.credential forAuthenticationChallenge:challenge]; } else { [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; } } else { [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; } } } @end
其實這部分程式碼基本屬於比葫蘆畫瓢,就是模仿SDWebImageDownloaderOperation來寫的。寫好了獲取圖片URL的Operation,就要處理邏輯方面的東西了。這部分的邏輯主要在SDWebImageDownloader中做修改,修改思路就是在每個下載圖片的operation前面都新增一個獲取圖片的operation,然後讓下載的operation依賴於獲取圖片URL的operation,但是相信大家使用SDWebImage的情形大部分還是在UITabView等列表中,而且圖片下載的資料傳輸遠遠大於獲取一個URL的資料量,所以按照上面的思路來做的話很可能造成大量的獲取圖片URL的operation佔用資源造成圖片下載的不及時,所以這裡把獲取圖片URL的operation優先順序降低,把圖片下載operation的優先順序提高,而圖片下載又依賴於圖片URL的獲取,這樣就基本符合原始SDWebImage的載入邏輯了。下面是邏輯處理程式碼。不對的地方歡迎指正交流。
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
__block SDWebImageDownloaderOperation *operation;
__block SDWebImageFetchURLOperation *fetchOperation;
__weak SDWebImageDownloader *wself = self;
[self addProgressCallback:progressBlock andCompletedBlock:completedBlock forURL:url createCallback:^{
NSTimeInterval timeoutInterval = wself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
}
__block NSURL *mURL = [url copy];
if ([mURL.description hasPrefix:@"xxxxxxxxxxxxxxx"]) {//這裡判斷是不是需要進行二次載入的URL
NSMutableURLRequest *prerequest = [[NSMutableURLRequest alloc] initWithURL:mURL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:5.0];
[prerequest setHTTPMethod:@"GET"];
fetchOperation = [[SDWebImageFetchURLOperation alloc] initWithRequest:prerequest completed:^(NSString *url, NSError *error, BOOL finished) {
if (!url) {
return ;
}
if (operation) {
[operation changeURL:[NSURL URLWithString:url]];
}
} cancelled:^{
return ;
}];
fetchOperation.queuePriority = NSOperationQueuePriorityLow;//降低獲取URL執行緒優先順序
}
// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:mURL cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
request.HTTPShouldUsePipelining = YES;
if (wself.headersFilter) {
request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = wself.HTTPHeaders;
}
operation = [[SDWebImageDownloaderOperation alloc] initWithRequest:request
options:options
progress:^(NSInteger receivedSize, NSInteger expectedSize) {
SDWebImageDownloader *sself = wself;
if (!sself) return;
NSArray *callbacksForURL = [sself callbacksForURL:url];
for (NSDictionary *callbacks in callbacksForURL) {
SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
if (callback) callback(receivedSize, expectedSize);
}
}
completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
SDWebImageDownloader *sself = wself;
if (!sself) return;
NSArray *callbacksForURL = [sself callbacksForURL:url];
if (finished) {
[sself removeCallbacksForURL:url];
}
for (NSDictionary *callbacks in callbacksForURL) {
SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
if (callback) callback(image, data, error, finished);
}
}
cancelled:^{
SDWebImageDownloader *sself = wself;
if (!sself) return;
[sself removeCallbacksForURL:url];
}];
if (wself.username && wself.password) {
operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
}
// if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;//提高下載優先順序
// } else if (options & SDWebImageDownloaderLowPriority) {
// operation.queuePriority = NSOperationQueuePriorityLow;
// }
if (fetchOperation) {//如果有二次載入的operation存在,那麼設定下載和獲取operation的依賴關係並加入執行緒佇列
[operation addDependency:fetchOperation];
[wself.downloadQueue addOperation:operation];
[wself.downloadQueue addOperation:fetchOperation];
}else{
[wself.downloadQueue addOperation:operation];
}
if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
if (fetchOperation) {
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = fetchOperation;
}else{
[wself.lastAddedOperation addDependency:operation];
wself.lastAddedOperation = operation;
}
}
}];
return operation;
}
相關推薦
SDWebImage擴充套件二次載入圖片
隨著網路安全的意識加重,相信很多同學都遇到過圖片二次載入的需求,即第一次請求獲得圖片的URL,第二次請求獲得圖片。在iOS中圖片的載入庫很多,而SDWebImage絕對是其中的翹楚,也是廣大iOSer們開發時候的首選。這裡簡單介紹一下自己在專案中在SDWebImage基礎上
引用百度地圖api二次載入地圖錯位
所做的網頁需要有百度地圖的功能。 於是引用了百度地圖,但是在初始化地圖的時候遇到了一個問題。 初始化的地圖中心點不正確。所標記的定位如果是第一次載入的話,定位是正確的。 但如果沒有清快取就進行第二次載入的話,這個定位一開始雖然是在地圖的最中間,但是由於初始定位不正確,一開始標記的定位也不
二次取樣圖片
Mantivity主頁面: package com.bw.ymy.ymy1107; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import
ueditor二次載入(getEditor)渲染失敗(載入失敗)的原因解決方案
大家自己看看官方的js檔案ueditor.all.js有以下的程式碼 /** * @name getEditor * @since 1.2.4+ * @grammar UE.getEditor(id,[opt]) => E
完美解決ViewPager+Fragment二次載入空白問題
ViewPager+Fragment使用的還是比較頻繁的,但是當我開啟應用第一次進入時很正常,然而第二次進入的時候卻顯示的是空白,當時感覺很是迷茫,可是仔細一查,原來是第二次載入的時候重複呼叫了onCreateView()這個方法,重新new了一個pageadapter導致子
jQuery DataTables大資料非同步二次載入渲染及initComplete事件bug
我們在使用dataTables進行資料統計時,不可避免的會碰到對大資料的統計。當進行伺服器端大資料讀取時,毫無疑問的會佔用大量載入時間,拖慢頁面載入速度。為優化頁面載入速度問題,我們便要在將請求中最耗時的部分在頁面載入完成之後,進行二次載入,渲染入資料。 之前
Android-Universal-Image-Loader 學習筆記(二)載入圖片原理
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener pro
canvas載入圖片需要二次重新整理的問題
如題:此問題我也經在百度問問上進行了解答。https://zhidao.baidu.com/question/1048045241465845579.html 好吧,難怪現在百度那麼坑人。下面是js程式碼,親測有用。 //canvas載入要修改的圖片function loadSketch(){ v
SDWebImage載入圖片URL第一次失敗,後面圖片URL存在不重新整理的問題
業務需求,有時候會首先出現圖片的網路URL地址,但是並沒有顯示出來,使用SDWebImage顯示圖片如下 self.itemImageView sd_setImageWithURL:<#(nullable NSURL *)#>]; 但是發現,後面
微信二次分享不顯示摘要和圖片的解決方法
conf eight sage 接口 所有 微信公眾平臺 取消 onf split 微信二次分享不顯示摘要和圖片的解決方法 解決不顯示摘要和圖片的問題,需要調用微信公眾號的js-sdk的api ,需要前端和後臺的配合, 後臺需要返回 appid (公眾號的appid )
Revit二次開發之載入族
return mes 刪除 開發 urn com tar 失敗 commit 載入族 此方法載入族無法覆蓋原有族,即若存在相同名稱的族則會載入失敗 1 Family family = null; //族 2 3 Transaction transact
jquery二次開發之擴充套件物件基元
(function (window, $, undefined) { var _Core = function () { var eventarr = []; var _OnPageLoad = undefined; ///
圖片二次採集和壓縮 ---》需要清單檔案宣告註冊讀寫許可權
MainActivity package com.example.renzhili20181107; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitma
圖片二次取樣
MainActivity頁面 package com.example.image; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import
側滑+fragment切換頁面+fragment巢狀+二次取樣+輪播圖+gridview展示圖片+網路請求資料+資料庫
全域性配置Appliction 所需要的依賴有:implementation ‘com.google.code.gson:gson:2.8.5’ implementation ‘com.nostra13.universalimageloader:universal-image-loader:
PullToRefreshListView上拉和下拉+輪播圖多條目+fragment巢狀fragment+二次取樣+側拉點選切換fragment+PullToRefreshGritView圖片展示
側拉 程式碼 1提取的基類 1.1Activity的基類 package com.example.zonghelianxi02.ui.activity; import android.os.Bundle; import android.support.annotation.Nulla
圖片二次取樣///////////子執行緒
1.提取的基類 在這裡插入程式碼片package com.example.imager_er; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7
fragment中的ImagView+Text多條目,點選ImageView二次取樣切換相簿圖片
##Fragmentd的 XML: <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android=“http://schemas.and
【CAD二次開發】-ObjectARX-擴充套件資料 (Xdata)
基本思路: (1)建立一個新專案,命名為Xdata.註冊一個命令AddXData. 實現程式碼為: static void AAAMyGroupAddXData() { // 提示使用者選擇所要新增擴充套件資料的圖形物件 AcDbEntity *pEnt
織夢二次安裝在二級目錄圖片不顯示的原因
第一種:批量修改域名下所有文章內的圖片路徑。 1、進後臺-核心-批量維護-資料庫內容替換 2、選擇表 dede_addonarticle 3、欄位 body 4、被替換內容: src=”/uploads/ 5、替換為:src=”http://你的域名/uploads