本文承接ReactNative圖片下載過程(一)城侧,繼續(xù)深入研究易遣。源碼在文末,如想直接看可翻至最后嫌佑。
整個圖片的下載過程豆茫,基本思想就是先在模塊里找合適的loader去下載,如果找不到則用RCTNetwork去下屋摇,當(dāng)然我們看源碼肯定不能只懂思想揩魂,一些細(xì)節(jié)處理也是很值得關(guān)注的,比如緩存處理炮温,緩存的訪問限制火脉,取消下載的處理,下載完回調(diào)函數(shù)的線程處理等等柒啤。
1倦挂、處理圖片下載完成后的回調(diào)函數(shù),將回調(diào)函數(shù)放在非主線程中處理白修,防止耗費(fèi)資源妒峦,不得不說其對于輸入的判斷都很到位
RCTImageLoaderCompletionBlockcompletionHandler = ^(NSError*error, UIImage*image) {
if([NSThreadisMainThread]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (!cancelled) {
completionBlock(error, image);
}
});
} elseif(!cancelled) {
completionBlock(error, image);
}
};
2、判斷圖片的URL是否為空兵睛,如果為空則拋出錯誤肯骇。
if(imageTag.length== 0) {
completionHandler(RCTErrorWithMessage(@"source.uri should not be an empty string"), nil);
return^{};
}
3窥浪、建NSURLCache串行隊列
// All accessto URL cache must be serialized
if(!_URLCacheQueue) {
_URLCacheQueue= dispatch_queue_create("com.facebook.react.ImageLoaderURLCacheQueue", DISPATCH_QUEUE_SERIAL);
}
4、異步執(zhí)行隊列_URLCacheQueue,初始化URLCache緩存
if(!_URLCache) {
_URLCache= [[NSURLCachealloc] initWithMemoryCapacity:5* 1024* 1024// 5MB
diskCapacity:200* 1024* 1024// 200MB
diskPath:@"React/RCTImageDownloader"];
}
5笛丙、找到可以執(zhí)行下載操作的loader漾脂,如果找到則執(zhí)行下載操作loadImageForURL:
RCTImageLoader*strongSelf = weakSelf;
if(cancelled || !strongSelf) {
return;
}
// Findsuitable image URL loader
NSURLRequest*request = [RCTConvertNSURLRequest:imageTag];
id<RCTImageURLLoader> loadHandler = [strongSelf imageURLLoaderForURL:request.URL];
if(loadHandler) {
cancelLoad = [loadHandler loadImageForURL:request.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
completionHandler:completionHandler]?: ^{};
return;
}
6、調(diào)試用胚鸯,檢查網(wǎng)絡(luò)模塊是否可用并能下載圖片
// Checkif networking module is available
if(RCT_DEBUG&& ![_bridgerespondsToSelector:@selector(networking)]) {
RCTLogError(@"No suitableimage URL loader found for %@. You may need to "
" import the RCTNetworkinglibrary in order to load images.",
imageTag);
return;
}
// Checkif networking module can load image
if(RCT_DEBUG&& ![_bridge.networkingcanHandleRequest:request]) {
RCTLogError(@"No suitableimage URL loader found for %@",imageTag);
return;
}
7骨稿、使用網(wǎng)絡(luò)模塊來下載圖片
__blockRCTImageLoaderCancellationBlockcancelDecode = nil;
RCTURLRequestCompletionBlockprocessResponse =
^(NSURLResponse*response, NSData*data, NSError*error) {
// 檢查是否有下載出錯或沒有數(shù)據(jù)返回
if(error) {
completionHandler(error, nil);
return;
} elseif(!data) {
completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil);
return;
}
// 檢查HTTP請求是否返回錯誤,如有誤則拋出返回的狀態(tài)碼
if([response isKindOfClass:[NSHTTPURLResponseclass]]) {
NSInteger statusCode =((NSHTTPURLResponse*)response).statusCode;
if (statusCode != 200) {
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
code:statusCode
userInfo:nil], nil);
return;
}
}
// 圖片解碼
cancelDecode = [strongSelf decodeImageData:data
size:size
scale:scale
resizeMode:resizeMode
completionBlock:completionHandler];
};
8姜钳、添加png后綴
//判斷請求的url是否為fileURL并且是否沒有后綴坦冠,如果都是則添加后綴png
if(request.URL.fileURL&& request.URL.pathExtension.length== 0) {
NSMutableURLRequest*mutableRequest = [request mutableCopy];
mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]];
request = mutableRequest;
}
9、根據(jù)request在responseCache緩存中查找是否已經(jīng)有了緩存哥桥,如果有則執(zhí)行緩存內(nèi)容
NSCachedURLResponse*cachedResponse = [_URLCachecachedResponseForRequest:request];
if(cachedResponse) {
processResponse(cachedResponse.response,cachedResponse.data, nil);
return;
}
10辙浑、使用RCTNetworkTask來下載圖片
//調(diào)用network模塊來發(fā)起請求
RCTNetworkTask*task = [_bridge.networkingnetworkTaskWithRequest:request completionBlock:
^(NSURLResponse*response, NSData*data, NSError*error) {
if(error) {
completionHandler(error, nil);
return;
}
dispatch_async(_URLCacheQueue, ^{
// 將請求的回應(yīng)緩存起來
BOOL isHTTPRequest =[request.URL.scheme hasPrefix:@"http"];
[strongSelf->_URLCache storeCachedResponse:
[[NSCachedURLResponse alloc] initWithResponse:response
data:data
userInfo:nil
storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly]
forRequest:request];
// 處理返回的數(shù)據(jù)
processResponse(response,data, nil);
});
}];
11、開始下載
task.downloadProgressBlock= progressHandler;
[task start];
cancelLoad = ^{
[task cancel];
if(cancelDecode) {
cancelDecode();
}
};
12拟糕、返回一個可取消下載的block
return^{
if(cancelLoad) {
cancelLoad();
}
//執(zhí)行1和cancelled的或運(yùn)算然后把結(jié)果存入&cancelled
OSAtomicOr32Barrier(1, &cancelled);
};
官方部分源碼
RCTImageLoader.m
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString*)imageTag
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
__blockvolatileuint32_tcancelled = 0;
__blockvoid(^cancelLoad)(void) = nil;
__weakRCTImageLoader*weakSelf = self;
RCTImageLoaderCompletionBlockcompletionHandler = ^(NSError*error, UIImage*image) {
if([NSThreadisMainThread]) {
//Most loaders do not return on the main thread, so caller is probably not
//expecting it, and may do expensive post-processing in the callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (!cancelled) {
completionBlock(error, image);
}
});
} elseif(!cancelled) {
completionBlock(error, image);
}
};
if(imageTag.length== 0) {
completionHandler(RCTErrorWithMessage(@"source.uri should not be an empty string"), nil);
return^{};
}
// All accessto URL cache must be serialized
if(!_URLCacheQueue) {
_URLCacheQueue= dispatch_queue_create("com.facebook.react.ImageLoaderURLCacheQueue", DISPATCH_QUEUE_SERIAL);
}
dispatch_async(_URLCacheQueue, ^{
if(!_URLCache) {
_URLCache= [[NSURLCachealloc] initWithMemoryCapacity:5* 1024* 1024// 5MB
diskCapacity:200* 1024* 1024// 200MB
diskPath:@"React/RCTImageDownloader"];
}
RCTImageLoader*strongSelf = weakSelf;
if(cancelled || !strongSelf) {
return;
}
// Findsuitable image URL loader
NSURLRequest*request = [RCTConvertNSURLRequest:imageTag];
id<RCTImageURLLoader> loadHandler = [strongSelf imageURLLoaderForURL:request.URL];
if(loadHandler) {
cancelLoad = [loadHandler loadImageForURL:request.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
completionHandler:completionHandler]?: ^{};
return;
}
// Checkif networking module is available
if(RCT_DEBUG&& ![_bridgerespondsToSelector:@selector(networking)]) {
RCTLogError(@"No suitableimage URL loader found for %@. You may need to "
" import the RCTNetworkinglibrary in order to load images.",
imageTag);
return;
}
// Checkif networking module can load image
if(RCT_DEBUG&& ![_bridge.networkingcanHandleRequest:request]) {
RCTLogError(@"No suitableimage URL loader found for %@",imageTag);
return;
}
// Usenetworking module to load image
__blockRCTImageLoaderCancellationBlockcancelDecode = nil;
RCTURLRequestCompletionBlockprocessResponse =
^(NSURLResponse*response, NSData*data, NSError*error) {
//Check for system errors
if(error) {
completionHandler(error, nil);
return;
} elseif(!data) {
completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil);
return;
}
//Check for http errors
if([response isKindOfClass:[NSHTTPURLResponseclass]]) {
NSInteger statusCode =((NSHTTPURLResponse*)response).statusCode;
if (statusCode != 200) {
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
code:statusCode
userInfo:nil], nil);
return;
}
}
//Decode image
cancelDecode = [strongSelf decodeImageData:data
size:size
scale:scale
resizeMode:resizeMode
completionBlock:completionHandler];
};
// Addmissing png extension
if(request.URL.fileURL&& request.URL.pathExtension.length== 0) {
NSMutableURLRequest*mutableRequest = [request mutableCopy];
mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]];
request = mutableRequest;
}
// Checkfor cached response before reloading
// TODO:move URL cache out of RCTImageLoader into its own module
NSCachedURLResponse*cachedResponse = [_URLCachecachedResponseForRequest:request];
if(cachedResponse) {
processResponse(cachedResponse.response,cachedResponse.data, nil);
return;
}
//Download image
RCTNetworkTask*task = [_bridge.networkingnetworkTaskWithRequest:request completionBlock:
^(NSURLResponse*response, NSData*data, NSError*error) {
if(error) {
completionHandler(error, nil);
return;
}
dispatch_async(_URLCacheQueue, ^{
// Cache the response
// TODO: move URL cache out of RCTImageLoader into itsown module
BOOL isHTTPRequest =[request.URL.scheme hasPrefix:@"http"];
[strongSelf->_URLCache storeCachedResponse:
[[NSCachedURLResponse alloc] initWithResponse:response
data:data
userInfo:nil
storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly]
forRequest:request];
// Process image data
processResponse(response,data, nil);
});
}];
task.downloadProgressBlock= progressHandler;
[task start];
cancelLoad = ^{
[task cancel];
if(cancelDecode) {
cancelDecode();
}
};
});
return^{
if(cancelLoad) {
cancelLoad();
}
OSAtomicOr32Barrier(1, &cancelled);
};
}