对于一个图片较多的应用来说,产品可能会提出根据网络环境下载不同大小图片的需求,比如在WiFI环境时下载高清图,在WLAN时下载一般图片。这对这个需求一般程序员都会觉得这不是很简单的事件吗?直接获取当前网络环境然后下载并设置图片,于是三下五除二的写出了以下代码。
1 2 3 4 5 6 7 8 9 10 11
| - setItem:(CustomItem *)item { _item = item; UIImage *placeholder = [UIImage imageNamed:@"placeholderImage"]; AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager]; if (mgr.isReachableViaWiFi) { // 在使用Wifi, 下载原图 [self.imageView sd_setImageWithURL:[NSURL URLWithString:item.originalImage] placeholderImage:placeholder]; } else { // 其他,下载小图 [self.imageView sd_setImageWithURL:[NSURL URLWithString:item.thumbnailImage] placeholderImage:placeholder]; } }
|
上面代码是一般情况下对自定义视图设置模型数据,然后根据模型数据更新UI,这个没什么好说的。那上面的方法有什么问题呢?问题大了去了,上面的代码虽然也能给用户节省一点点流量也不会造成应用崩溃或降低流畅度,在这种情况下不明真相的测试小妹和产品都会觉得程序小哥很给力,做事真利索。我们都知道SDWebImage会缓存已经下载过的图片,设想一下如果手机沙盒或内存中缓存了高清图,而这时又是在WLAN网络下,根据以上代码就会直接去下载一般图片。这就很尴尬了,手机里面明明有高清图非得给我去下载不清晰的图片。又比如,用户当前没有网络但是呢又缓存高清图,然后又根据以上代码程序小哥给设置了占位图,我明明有高清图啊大哥,你为毛要设置占位图?发现问题的聪明程序小哥又会写出以下代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| - setItem:(CustomItem *)item { _item = item; // 占位图片 UIImage *placeholder = [UIImage imageNamed:@"placeholderImage"]; // 从内存\沙盒缓存中获得原图, UIImage *originalImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:item.originalImage]; if (originalImage) { // 如果内存\沙盒缓存有原图,那么就直接显示原图(不管现在是什么网络状态) self.imageView.image = originalImage; } else { // 内存\沙盒缓存没有原图 AFNetworkReachabilityManager *mgr = [AFNetworkReachabilityManager sharedManager]; if (mgr.isReachableViaWiFi) { // 在使用Wifi, 下载原图 [self.imageView sd_setImageWithURL:[NSURL URLWithString:item.originalImage] placeholderImage:placeholder]; } else if (mgr.isReachableViaWWAN) { // 在使用手机自带网络 // 用户的配置项假设利用NSUserDefaults存储到了沙盒中 // [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"alwaysDownloadOriginalImage"]; // [[NSUserDefaults standardUserDefaults] synchronize]; #warning 从沙盒中读取用户的配置项:在3G\4G环境是否仍然下载原图 BOOL alwaysDownloadOriginalImage = [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysDownloadOriginalImage"]; if (alwaysDownloadOriginalImage) { // 下载原图 [self.imageView sd_setImageWithURL:[NSURL URLWithString:item.originalImage] placeholderImage:placeholder]; } else { // 下载小图 [self.imageView sd_setImageWithURL:[NSURL URLWithString:item.thumbnailImage] placeholderImage:placeholder]; } } else { // 没有网络 UIImage *thumbnailImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:item.thumbnailImage]; if (thumbnailImage) { // 内存\沙盒缓存中有小图 self.imageView.image = thumbnailImage; } else { // 处理离线状态,而且有没有缓存时的情况 self.imageView.image = placeholder; } } } }
|
这仍然会很尴尬,如果以上代码是设置UITableView里cell的图片,网络环境差的时候就会出现已经显示的图片突然被设置成另外一张图造成数据错乱。为什么会数据错乱呢?调用sd_setImageWithURL方法会关联对应的UIImageView,当cell滚出屏幕然扔到缓存里而图片还在后台继续下载并且这种关联的关系仍然会存在.随着用户继续滑动tableview,缓存里的cell被重用于是上面的方法再次被调用设置新的图片。这个时候就会有两个下载对象关联同一个UIImageView对象。如果后添加的下载任务先完成就会出现已经显示的图片突然被设置成了另一张,由此造成数据错乱。
如果你研究过SDWebImage的源码,会发现在sd_setImageWithURL: placeholderImage:方法中会这么一句[self sd_cancelCurrentImageLoad],对,在这个方法中会取消之前下载任务,所以我们最终的代码是以下的样子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| - (void)setImageWihtOriginImageURL:(NSString *)originURL thumbnailImageULR:(NSString *)thumbnialImageURL placeholderImage:(UIImage *)placeholderImage completed:(SDWebImageCompletionBlock)completedBlock {
// 查看内存磁盘是否有原图 UIImage *image = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:originURL]; if (image) { //取消原来的下载队列(如果有),并加载图片 [self sd_setImageWithURL:[NSURL URLWithString:originURL] completed:completedBlock]; } else { AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager]; if (manager.reachableViaWiFi) { // 如果有wifi则下载原图 [self sd_setImageWithURL:[NSURL URLWithString:originURL] placeholderImage:placeholderImage completed:completedBlock]; } else if (manager.reachableViaWWAN) { //根据用户设置是否在wan下下载原图 BOOL downloadOriginImage = [[NSUserDefaults standardUserDefaults] boolForKey:@"alwaysDownloadOriginImage"]; // 用wan情况下根据用户选择下载 if (downloadOriginImage) { [self sd_setImageWithURL:[NSURL URLWithString:originURL] placeholderImage:placeholderImage completed:completedBlock]; } else { [self sd_setImageWithURL:[NSURL URLWithString:thumbnialImageURL] placeholderImage:placeholderImage completed:nil]; } } else { // 没有可用网络 UIImage *image = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:thumbnialImageURL]; if (image) { //如果有缩略图 取消原队列的下载并加载本地图片 [self sd_setImageWithURL:[NSURL URLWithString:thumbnialImageURL] completed:completedBlock]; } else { // 没有网络也没有本地图片则设置占位图片 [self sd_setImageWithURL:nil placeholderImage:placeholderImage completed:completedBlock]; } }
} }
|