探究react-native 源码的图片缓存-- 自定义图片缓存

最近一个项目RN版本是0.61,因里面有个图片浏览功能,要做一个清理缓存的功能。一番捣鼓后功能解决,记录如下
找到的清理缓存组建是react-native-http-cache,因为该组建长期没人维护android编译不过,本人又不善长android,遂找了个进化版react-native-http-cache3。这个在安卓上面需要更改两个地方才可正常工作,不做讨论,本文主要是讲ios上面。

iOS在解决了2个bug之后这个组建也能使用,但是发现ios导给rn的方法getImageCacheSize以及clearImageCache都只是空实现,没有具体内容。原因下面解释。

三年前我写的分析RN端图片缓存的策略(https://www.jianshu.com/p/c7376db9b7ec),当时图片是没有磁盘缓存的,内存缓存也无效。现在再看下新的RN版本图片缓存相关代码,经过测试发现,对解压后2M以内的图片,内存缓存生效。磁盘缓存依旧没有。正因为没有磁盘缓存,所谓的清理图片缓存也就不存在,于是方法为空实现也就能理解了。

本人项目中有图片浏览功能,当预览图片时图片是高清的原图,比较大,按照rn默认的行为图片不做缓存,那么每次预览时都会去下载,很缓慢,影响体验。而且因为图片远大于2M内存缓存也无法生效,效果很差劲。这里需要自定义图片的缓存

网上搜索了一圈发现都是解决方案都是使用react-native-img-cache,但是要求使用特定的图片组件,这样是不太理想的,比如我这里使用的图片预览组件,其内部使用的是Animate.Image显示图片,总不能去别个组件内部更改为CachedImage吧。

翻看RN图片加载相关的代码,发现了关键地方。在RCTImageLoaderProtocol.h这个图片加载相关的协议里面发现

/**
 * Allows developers to set their own caching implementation for
 * decoded images as long as it conforms to the RCTImageCache
 * protocol. This method should be called in bridgeDidInitializeModule.
 */
- (void)setImageCache:(id)cache;

很明显,这里可以让开发者自定义图片的缓存类,只需实现RCTImageCache协议即可。

很简单,我使用SDWebImage实现了这个协议

#import "PSImageCache.h"
#import 

@implementation PSImageCache

- (UIImage *)imageForUrl:(NSString *)url
                    size:(CGSize)size
                   scale:(CGFloat)scale
              resizeMode:(RCTResizeMode)resizeMode{
  if (!url) return nil;
  return [[SDImageCache sharedImageCache] imageFromCacheForKey:url];
}

- (void)addImageToCache:(UIImage *)image
                    URL:(NSString *)url
                   size:(CGSize)size
                  scale:(CGFloat)scale
             resizeMode:(RCTResizeMode)resizeMode
               response:(NSURLResponse *)response{
  if (!image || !url) return;
  [[SDImageCache sharedImageCache] storeImage:image forKey:url completion:nil];
}
@end

现在只需要在一个合适的时机,将自定义的PSImageCache类赋值给RCTImageLoader即可。

多方实验,如下最佳实践。在didFinishLaunchingWithOptions中

  // 监听RCTImageLoader模块加载完毕,自定义imageCache为sdwebimage缓存图片
  [[NSNotificationCenter defaultCenter] addObserverForName:RCTDidInitializeModuleNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
    RCTImageLoader *imageLoader = note.userInfo[@"module"];
    if ([imageLoader isKindOfClass:[RCTImageLoader class]]) {
      [imageLoader setImageCache:[PSImageCache new]];
    }
  }];

现在图片缓存已经走Sdwebcache通道了,一切正常。最上面说的清理缓存的两个空方法需要更改下。将Podfile中倒入的react-native-http-cache3删除,RNHttpCache文件挪到本地文件,修改getImageCacheSize和clearImageCache方法

RCT_EXPORT_METHOD(getImageCacheSize:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
    resolve(@([[SDImageCache sharedImageCache] totalDiskSize]));
}

RCT_EXPORT_METHOD(clearImageCache:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
  [[SDImageCache sharedImageCache] clearDiskOnCompletion:^{
    resolve(nil);
  }];
}

大功告成!

你可能感兴趣的:(探究react-native 源码的图片缓存-- 自定义图片缓存)