Picasso的缓存理解及简单的封装
来源:互联网 发布:剑网3秀萝捏脸数据图片 编辑:程序博客网 时间:2024/06/08 08:25
Picasso的缓存理解及简单的封装
想必大家对Picasso的使用已经比较熟悉了,在此处我就不多说了,不熟悉的小伙伴可以看看Picasso官网。然后简单说说项目的依赖。
- Picasso的缓存理解及简单的封装
- 依赖
- 权限
- 缓存
- LruCache
- DiskLruCache
- Picasso中的缓存操作
- 缓存清除
- 封装
- 接口部分
- 接口实现
- 转换车间
- 函数调用
- Demo下载
- 依赖
依赖
一般情况下都是通过 GRADLE 进行依赖会方便很多
compile 'com.squareup.picasso:picasso:2.5.2' 进行依赖
但值得注意的是如果说想使用OkHttpDownloader来进行自定义缓存路径或者其他操作,则需引入okhttp的库,但不能使用最新版的okhttp3,因为包名发生了变化,加载的时候会出错。所以可以引入下面的版本。当然也可以使用okhttp3重写。
compile 'com.squareup.okhttp:okhttp:2.6.0'
权限
<uses-permission android:name="android.permission.INTERNET"></uses-permission><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
缓存
大家最关心的可能就是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));//实例化OkHttpDownloaderOkHttpDownloader 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进行实现。
封装
Picasso自身本来已经就是全局的单例模式了,通过Picasso.with(contenxt)即可拿到对象,操作非常的方便,所以封装也没有多大的意思,就是将常用的功能模块化,让代码的可读性更高一些。
下面就是我简单的封装了一些业务常用的功能。
接口部分
package com.pmlee.picassodemo.presenter;import android.widget.ImageView;import com.squareup.picasso.Callback;import com.squareup.picasso.RequestCreator;/** * Created by liyunshuang on 2016/11/18. * <p> * Email 522940943@qq.com or liyunshuang21@gmail.com */public interface INetworkImageLoadPresenter { /** * 步加载图片 * 加载默认原图片大小 * @param iv * @param url */ void loadImage(ImageView iv, String url); /** * 步加载图片 * 自定义Picasso请求参数 * @param iv * @param requestCreator */ void loadImage(ImageView iv, RequestCreator requestCreator); /** * 异步加载图片 * * @param iv 目标ImageView * @param url 图片URL地址 * @param height 指定高度 * @param width 指定宽度 单位 px * * 缩放方式为CenterCrop */ void loadImage(ImageView iv, String url, int width, int height); /** * 带有监听回调的 图片加载 * com.squareup.picasso.callback * @param iv * @param url * @param width * @param height * @param callback */ void loadImage(ImageView iv, String url, int width, int height, Callback callback); void loadImage(ImageView iv, String url, boolean showError); /** * 加载并处理为圆形图片 * @param iv * @param url */ void loadCircleImage(ImageView iv, String url); /** * 计算缓存大小 * @return 带单位的数值 最小单位为 bit 最大为 GB */ String calculateCacheSize(); /** * 清除所有的缓存 */ void cleanCacheAll(); /** * 删除请求过的缓存 * 针对于头像更新等操作 * @param requestedUrl */ void cleanCache(String requestedUrl); /** * 获取Picasso请求构建器 * @param url * @return */ RequestCreator getRequestCreator(String url);}
接口实现
其中缓存计算和删除都是使用工具类的最笨方法,大家可以尝试我上述通过重新Downloader之类的方法较好。利用okhttp可以扩展很多的功能。
package com.pmlee.picassodemo.presenter;import android.content.Context;import android.support.annotation.NonNull;import android.text.TextUtils;import android.widget.ImageView;import com.pmlee.picassodemo.PicassoInfo;import com.pmlee.picassodemo.util.CircleTransform;import com.pmlee.picassodemo.util.FileUtils;import com.squareup.okhttp.OkHttpClient;import com.squareup.picasso.Callback;import com.squareup.picasso.NetworkPolicy;import com.squareup.picasso.Picasso;import com.squareup.picasso.RequestCreator;import java.io.File;/** * Created by liyunshuang on 2016/11/18. * <p> * Email 522940943@qq.com or liyunshuang21@gmail.com */public class NetworkImageLoadPresenter implements INetworkImageLoadPresenter { //测试标签 private static final String TAG = "NetworkImageLoad"; private static Context mContext; //缓存信息 private Picasso mSingletonPicasso; private PicassoInfo mPicassoInfo; private static NetworkImageLoadPresenter customLoader; /** * 传入 自定义Picasso的构造器 * * @param context Context * @param mPicassoInfo PicassoInfo */ private NetworkImageLoadPresenter(Context context, PicassoInfo mPicassoInfo) { mContext = context; this.mPicassoInfo = mPicassoInfo; this.mSingletonPicasso = mPicassoInfo.mPicasso; Picasso.setSingletonInstance(mSingletonPicasso); } /** * 默认数据加载 */ private static class SingletonImageLoadHolder { //获取默认的Picasso参数 public static PicassoInfo info = PicassoInfo.getDefaultInfo(mContext); //实例化NetworkImageLoadPresenter类 public static NetworkImageLoadPresenter loader = new NetworkImageLoadPresenter(mContext, info); } /** * 构建NetworkImageLoad实例 * * @param context * @return NetworkImageLoad */ public static NetworkImageLoadPresenter create(Context context) { if (context != null) mContext = context; else return null; return SingletonImageLoadHolder.loader; } /** * 构建NetworkImageLoad实例 * * @param context * @param picassoInfo 自定义PicassoInfo * @return NetworkImageLoad */ public static NetworkImageLoadPresenter create(Context context, PicassoInfo picassoInfo) { if (customLoader == null) customLoader = new NetworkImageLoadPresenter(context, picassoInfo); return customLoader; } @Override public void loadImage(@NonNull ImageView iv, String url) { if (TextUtils.isEmpty(url)) { return; } getRequestCreator(url) .into(iv); } @Override public void loadImage(@NonNull ImageView iv, @NonNull RequestCreator requestCreator) { requestCreator.into(iv); } @Override public void loadImage(@NonNull ImageView iv, String url, int width, int height) { if (TextUtils.isEmpty(url)) { return; } RequestCreator requestCreator = getRequestCreator(url); requestCreator.resize(width, height) .centerCrop() .into(iv); } @Override public void loadImage(@NonNull ImageView iv, String url, int width, int height, Callback callback) { if (TextUtils.isEmpty(url)) { return; } RequestCreator requestCreator = getRequestCreator(url); if (callback != null) requestCreator.fetch(callback); requestCreator.resize(width, height) .centerCrop() .into(iv); } @Override public void loadImage(ImageView iv, String url, boolean showError) { if (TextUtils.isEmpty(url)) { return; } RequestCreator requestCreator = getRequestCreator(url); if (showError) {// requestCreator.error(mContext.getResources().getDrawable(R.mipmap.photo_fault)); } requestCreator.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE).into(iv); } @Override public void loadCircleImage(ImageView iv, String url) { if (TextUtils.isEmpty(url)) {// iv.setImageResource(R.mipmap.ic_launcher); return; } RequestCreator requestCreator = getRequestCreator(url); requestCreator.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE).transform( new CircleTransform()) .into(iv); } @Override public RequestCreator getRequestCreator(String url) { return mSingletonPicasso.load(url); } @Override public String calculateCacheSize() { long total = FileUtils.getTotalSizeOfFilesInDir(mPicassoInfo.mCacheFile); return total >= 1024 && total < (1024 * 1024) ? (total >> 10) + " KB" : total >= (1024 * 1024) && total < (1024 * 1024 * 1024) ? (total >> 20) + " MB" : total >= (1024 * 1024 * 1024) ? (total >> 30) + " GB" : total + "B"; } @Override public void cleanCacheAll() { if (mPicassoInfo.mCacheFile.exists() && mPicassoInfo.mCacheFile.isDirectory()) { for (File target : mPicassoInfo.mCacheFile.listFiles()) { target.delete(); } } } @Override public void cleanCache(String requestedUrl) { if (mSingletonPicasso != null) mSingletonPicasso.invalidate(requestedUrl); } /** * 将Picasso进行返回 用于参数设置 * @return */ public Picasso getPicasso(){ return mSingletonPicasso==null?Picasso.with(mContext):mSingletonPicasso; }}
然后就是用于信息同步的及Picasso构造的PicassoInfo类
package com.pmlee.picassodemo;import android.content.Context;import com.pmlee.picassodemo.util.FileUtils;import com.squareup.picasso.Downloader;import com.squareup.picasso.OkHttpDownloader;import com.squareup.picasso.Picasso;import java.io.File;/** * Created by liyunshuang on 2017/4/18. * <p> * Email 522940943@qq.com or liyunshuang21@gmail.com * * 该类用于保存创建的Picasso和部分设置,好同步NetworkImageLoadPresenter中的删除和查询功能 */public class PicassoInfo { //缓存名 private static final String MY_CACHE_NAME = "my-image-cache"; private static final String DEFAULT_CACHE = "picasso-cache"; //自定义Picasso public Picasso mPicasso; //下载器中缓存的文件 public File mCacheFile; //自定义下载器 public Downloader mDownloader; private PicassoInfo(Picasso mPicasso, File cacheFile, Downloader downloader) { this.mPicasso = mPicasso; this.mCacheFile = cacheFile; this.mDownloader = downloader; } /** * 构建默认的 PicassoInfo 包含默认缓存路径 * * @param context * @return PicassoInfo */ public static PicassoInfo getDefaultInfo(Context context) { //定义缓存路径 File cacheFile = new File(FileUtils.getCacheDir(context), MY_CACHE_NAME); //内置下载器 OkHttpDownloader downloader = new OkHttpDownloader(cacheFile); Picasso singeltonPicasso = new Picasso.Builder(context) //重新定义下载器,指定缓存路径 .downloader(downloader) //log日志开启 .loggingEnabled(true) .build(); return new PicassoInfo(singeltonPicasso, cacheFile, downloader); } /** * 传入自定义的Picasso相关设置 * 包括缓存的位置 自定义下载器 监听器 * * @param context * @param downloader * @param cacheFile 该缓存路径为Downloader中所设置的,如果Downloader为null则该cacheFile无效,还是 * 为Picasso默认的路径,也会导致NetworkImageLoadPresenter中清理缓存和查询缓存失效. * @param listener * @return */ public static PicassoInfo createPicassoInfo(Context context, Downloader downloader, File cacheFile, Picasso.Listener listener) { Picasso.Builder builder = new Picasso.Builder(context); if (listener != null) builder.listener(listener); if (downloader != null) builder.downloader(downloader); if (cacheFile == null||downloader== null) { cacheFile = new File(context.getCacheDir(), DEFAULT_CACHE); } return new PicassoInfo(builder.build(), cacheFile, downloader); }}
如果开启了Picasso的log可以在logcat增加名为“Picasso”进行信息过滤,就可以看到执行的信息。
转换车间
也就是Picasso中的Transformation,可以将获取到得图片按自己的需求进行转换,在Demo中我只用了圆形图片的转换,因为很多头像都需要切成圆形的。
package com.pmlee.picassodemo.util;import android.graphics.Bitmap;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Paint;import com.squareup.picasso.Transformation;/** * Created by liyunshuang on 2016/12/1. * <p> * Email 522940943@qq.com or liyunshuang21@gmail.com */public class CircleTransform implements Transformation { @Override public Bitmap transform(Bitmap source) { //画圆则需以最短边长为基准 int size = Math.min(source.getWidth(), source.getHeight()); int x = (source.getWidth() - size) / 2; int y = (source.getHeight() - size) / 2; Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size); if (squaredBitmap != source) { source.recycle(); } Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig()); //利用BitmapShader绘制圆角及圆形图片 Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP); paint.setShader(shader); //抗锯齿 paint.setAntiAlias(true); float r = size / 2f; canvas.drawCircle(r, r, r, paint); squaredBitmap.recycle(); return bitmap; } @Override public String key() { return "toCircle"; }}
函数调用
实例化
//默认加载//imageLoader = NetworkImageLoadPresenter.create(this);//自定义NetworkImageLoadPresenterFile cacheFile = new File(getExternalCacheDir(),"cache-test");imageLoader = NetworkImageLoadPresenter.create(this,PicassoInfo.createPicassoInfo(this, new OkHttpDownloader(cacheFile),cacheFile,null));
函数方法都是一步调用,具体的演示可以参考Demo。
代码都非常的简单,如果有什么问题或者更好的使用方法可以发邮箱给我相互学习。
Demo下载
demo下载
邮箱:liyunshuang21@gmail.com
- Picasso的缓存理解及简单的封装
- Picasso的简单使用及封装
- Picasso的封装(一)
- Picasso的封装(二)
- 仿Picasso框架,实现简单的图片三级缓存处理
- SharedPreferences+okhttp+gson+picasso实现简单的离线缓存
- Picasso 图片加载及缓存的使用心得
- Picasso 图片加载及缓存的使用心得
- picasso的简单用法
- Picasso的简单实用
- Picasso的简单使用
- Picasso的简单实用
- 封装的理解及应用
- Picasso picasso-强大的Android图片下载缓存库
- 自定义简单的封装SharedPreferences缓存类
- Android Picasso的简单使用
- Picasso库的简单用法
- 对于Picasso的封装和优化方案
- Eclipse新建Maven Project界面select an archetype 空白
- NestedScrolling 机制深入解析
- 结果集分页的两种方式
- drawableTop 图片大小
- linux 下查看机器是cpu是几核的
- Picasso的缓存理解及简单的封装
- JS生成渐变颜色
- POI的使用
- 【已解决】Apk签名后报so找不到
- OpenCV 行列值与坐标的对应关系
- java I/O系统(1)-File类
- 【剑指offer之二维数组中的查找 】
- 深入C语言可变参数(va_arg,va_list,va_start,va_end,_INTSIZEOF)
- http状态码清单