Picasso的缓存理解

Picasso的缓存理解

想必大家对Picasso的使用已经比较熟悉了,在此处我就不多说了,不熟悉的小伙伴可以看看Picasso官网。然后简单说说项目的依赖。

文章目录

  • Picasso的缓存理解
    • 依赖
        • 权限
    • 缓存
        • LruCache
        • DiskLruCache
        • Picasso中的缓存操作
        • 缓存清除

依赖

一般情况下都是通过 GRADLE 进行依赖会方便很多

compile 'com.squareup.picasso:picasso:2.5.2' 进行依赖

但值得注意的是如果说想使用OkHttpDownloader来进行自定义缓存路径或者其他操作,则需引入okhttp的库,但不能使用最新版的okhttp3,因为包名发生了变化,加载的时候会出错。所以可以引入下面的版本。当然也可以使用okhttp3重写。

compile 'com.squareup.okhttp:okhttp:2.6.0'

权限




缓存

大家最关心的可能就是Picasso的缓存机制,如何进行缓存路径更改和大小设置。

首先Picasso的缓存机制是LruCache和DiskLruCache的组合,也就是memory and disk caching。

Picasso的图片加载优先级是 memory > disk > network,可以设置setIndicatorsEnabled(true)来判断图片是通过什么渠道加载的,可用以调试缓存清理的API。

LruCache

LruCache的主要核心实现是LinkHashMap,LinkHashMap是一个能按顺序储存的链表,其中有一个叫Lru的算法排序,能将最近添加或使用的文件放在表头,而很少用的文件放在表尾,当缓存的文件达到设置的阈值,则将表尾不常用的缓存文件删除。因此大多数情况下可以不用删除缓存,除非需要更新某个图片列如更换头像之类的。

LruCache将缓存放于Memory中,因此当该应用进程结束的时候会自动被释放,为了数据的持久化,加快缓存读取因此还有个DiskLruCache。

DiskLruCache

和LruCache类似,只不过是将缓存文件放到了Disk上,当一个图片被缓存下来了之后,缓存目录下回看到3个文件,分别是xxxxxxxx.0,xxxxxxxx.1,journal,对应的目标文件是网络请求头文件,缓存文件,日志。

journal在DiskLruCache还是扮演了比较重要的作用,它保存了所有对缓存操作的记录包括数据是否成功写入,读取记录,及缓存文件的大小。同样当缓存文件超出了设定范围,DiskLruCache也会删除比较旧的数据,以及journal中旧的操作记录。

由于网上关于LruCache和DiskLruCache源码的解析很多,所以这里我只是简单的提一下它们的作用。

Picasso中的缓存操作

使用过Picasso的小伙伴应该知道Picasso暴露在外层对缓存处理的API,没办法对disk上的缓存文件产生作用,只是将LruCache中的缓存进行删除操作,一般用于图片的更新。在下面我将介绍自定义缓存及删除disk上缓存的方法。

自定义缓存路径

自定义缓存路径最常规的办法就是不让Picasso使用默认的Downloder,OkHttpDownloader中封装了可以更改缓存路径和缓存区大小的而设置,我们可以先看看默认的OkHttpDownloader的缓存位置。

static Downloader createDefaultDownloader(Context context) {
  try {
    Class.forName("com.squareup.okhttp.OkHttpClient");
    return OkHttpLoaderCreator.create(context);
  } catch (ClassNotFoundException ignored) {
  }
  return new UrlConnectionDownloader(context);
}

根据是否有com.squareup.okhttp.OkHttpClient这个包,来判断是否使用OkHttpDownloader,否则使用UrlConnectionDownloader。OkHttpLoaderCreator.create(context)这个方法调用的是OkHttpDownloader的默认构造器,只传入Context,其他数据默认。

/**
 * 默认调用的构造器,依次调用下方的其他构造器
 */
public OkHttpDownloader(final Context context) {
  this(Utils.createDefaultCacheDir(context));
}

/** 
 * @param cacheDir 缓存指定的路径,File类型的参数
 */
public OkHttpDownloader(final File cacheDir) {
  this(cacheDir, Utils.calculateDiskCacheSize(cacheDir));
}

/**
  * 最终调用的构造函数,同时实例化默认的OkHttpClient
  * @param 缓存指定的路径,File类型的参数
  * @param 允许缓存的最大值
  */
 public OkHttpDownloader(final File cacheDir, final long maxSize) {
    this(defaultOkHttpClient());
    try {
      client.setCache(new com.squareup.okhttp.Cache(cacheDir, maxSize));
    } catch (IOException ignored) {
    }
  }

通过上面的构造方法可以看出,默认的缓存路径和缓存空间的最大值是由picasso包下的Utils执行创建的。

默认的缓存路径

static File createDefaultCacheDir(Context context) {
//该路径的位置在 /data/data/packagename/cache/picasso-cache,也就是手机自身内存中。
  File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
  if (!cache.exists()) {
    //noinspection ResultOfMethodCallIgnored
    cache.mkdirs();
  }
  return cache;
}

默认的缓存空间大小

static long calculateDiskCacheSize(File dir) {
  long size = MIN_DISK_CACHE_SIZE;

  try {
    StatFs statFs = new StatFs(dir.getAbsolutePath());
    long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize();
    // 空间总大小的2%
    size = available / 50;
  } catch (IllegalArgumentException ignored) {
  
  }
  //返回值得范围在MIN_DISK_CACHE_SIZE和MAX_DISK_CACHE_SIZE之间,也就是说大于5MB小于50MB的值有效。
  return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
}

大家可以去翻翻源码Utils中还有很多方法值得取出来在日后使用。

ok说说正经的,自定义缓存的路径从上方的构造方法就可以看出,只要重新实例化一个OkHttpDownloader,使用第二个或者第三个构造器即可。具体的代码如下

Picasso singeltonPicasso = new Picasso.Builder(context)
        //重新定义下载器,指定缓存路径
        .downloader(new OkHttpDownloader(cacheFile))
        //log日志开启
        .loggingEnabled(true)
        .build();
        //设置到全局的Picasso中
 Picasso.setSingletonInstance(singeltonPicasso);

这样即可在指定的路径下创建缓存区,一般来说缓存位置都放在SD卡中,避免占用手机自身内存,不过现在手机大多数是手机一体化的,因此需要做一些判断,在我的Demo中会有相应的代码。

缓存清除

Picasso只提供的对LruCache清除的方法,没有对DiskLruCache清除的方法,其实也没有必要将起删除,因为达到阈值会自动删除最旧的数据。但为了一些业务需求,以及缓存空间设置过大的情况,还是可以考虑将其删除,我这里有两种方式。

1.直接删除的方式:

通过保存的缓存路径,遍历该路径下的所有文件,将其删除,可以参考我的demo。这种方法较笨,但也比较直观。

2.利用OkHttpDownloader:

喜欢翻源码的朋友可以发现,其实OkHttpClient中的Cache类封装了对DiskLruCache的操作,包括删除所有,计算缓存大小。

注意一定要将Downloader传入Picasso中,如上面自定义缓存路径的做法否则无法生效。

使用OkHttpDownloader该构造方法

public OkHttpDownloader(OkHttpClient client) {  this.client = client;}

该构造方法可以自定义一个OkHttpClient,然后设置它的Cache就可以达到预期的效果,在外部将自定义的OkHttpClient或者Cache保存下来,调用Cache实现的方法即可。

//实例化一个OkHttpClient为其设置自定义的缓存
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setCache(new Cache(new File(getExternalCacheDir(),"my-cache"),50*1024*1024));
//实例化OkHttpDownloader
OkHttpDownloader downloader = new OkHttpDownloader(okHttpClient);
//将downloader构建进Picasso中
//-----------------------------------------------------------------------------------------------
//获取缓存操作的对象
Cache cache = okHttpClient.getCache();
try {
//删除所以缓存目录下的文件
    cache.delete();
} catch (IOException e) {
    e.printStackTrace();
}

再或者重写OkHttpDownloader,调用父类的getClient(),在子类中将OkHttpClient的Cache对象返回。

protected final OkHttpClient getClient() {return client;}
public class MyDownloader extends OkHttpDownloader {
    public MyDownloader(Context context) {
        super(context);
    }

    /**
     * 获取Cache对象
     * @return Cache
     */
   public Cache getCache(){
       return getClient().getCache();
   }
}

再或者那么就是java的反射机制啦,这就不多说了,大家有兴趣可以自己尝试。能够将OkHttpClient中的Cache拿到操作disk中的缓存自然就不是问题了。顺便提一下如果想获取Picasso图片加载的进度也可以通过重新DownLoader进行实现。

你可能感兴趣的:(android,学习笔记,图片处理)