图片加载和Cache

    xiaoxiao2021-03-25  156

    图片加载和Cache

    ----------(我为自己的勤奋感动了!最近项目调整我负责的模块较多,且都是自定义View相关,后面学习了会分享这方面干货)

        今天我和大家分享的是图片的加载以及图片的缓存,项目中我们运用过最多的是图片的加载。假如通过ImageView来显示图片,很多时候ImageView的大小并没有图片大,这样加载不但没有办法显示原始图片而且图片过大还容易出现OOM问题。如何高效的加载Bitmap图片?就是采用BitmapFactory.Options来加载所需的图片大小。

        BitmapFactory.Options来缩放图片,主要是用到inSampleSize(采样率)参数。inSampleSize为1时,采样后的图片大小为图片的原始大小。inSampleSize为2时其图片的宽/高为原图大小的1/2。像素为原图的1/4。故缩放后图片的大小是以采样率2次方形式递减。

    eg:以一张1024*768图片为例,采样率为:4

    /** * 方法名:decodeSampledBitmap(Resources res,int resId,int reqWidth,int reqHeight)<br> * 用 法:用于获取压缩bitmap<br> * 参 数:Resources res int resId(资源ID) int reqWidth(请求宽度) int reqHeight(请求高度)<br> * 返回值:采样率<br> */ private Bitmap decodeSampledBitmap(Resources res,int resId,int reqWidth,int reqHeight){ final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true;//仅获取图片的长宽 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false;//获取图片的长宽以及像素 return BitmapFactory.decodeResource(res, resId, options);//根据资源获取图片 } /** * 方法名:calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight)<br> * 用 法:用于获取图片采样率<br> * 参 数:BitmapFactory.Options options int reqWidth(请求宽度) int reqHeight(请求高度)<br> * 返回值:采样率<br> */ public int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){ final int height = options.outHeight;//图片的高度 final int width = options.outWidth;//图片的宽度 int inSampleSize = 1; if (height > reqHeight || width > reqWidth) {//这里的采样率是必须两个值都要符合请求要求 final int halfHeight = height/2; final int halfWidth = width/2; while((halfHeight / inSampleSize) >= reqHeight && (halfWidth/inSampleSize) >= reqWidth){ inSampleSize *= 2; } } return inSampleSize; } 可见inSampleSize用于图片的压缩大大的减少了我们内存开销,以及流量开销(采样率也可用于加载网络图片压缩后续讲解到)

    1.2图片的缓存策略

    一般图片缓存我们运用较多的是LruCache(内存缓存),DiskLruCache(储存设备的缓存)。

    LruCache是线程安全,且当缓存满时,移除较早使用的缓存对象。

    LruCache初始化比较简单:

    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);//当前进程的可用类存转化为KB int cacheSize = maxMemory / 8;//大小为当前进程可用类存/8 mMemoryCache = new LruCache<String, Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) {//重写sizeOf()返回当前图片的大小 return value.getRowBytes() * value.getHeight() /1024; } }; LruCache特殊情况下还需要重写LruCache的entryRemoved方法,LruCache移除旧缓存时会调用,此时可以在entryRemoved中完成一些资源的回收工作。

    LruCache还支持获取和添加

    mMemoryCache.get(key); mMemoryCache.put(key, value) 用法和简单但是不影响它强大的功能,依靠LinkedHashMap支持访问顺序排序,淘汰最近使用最少缓存原则。

    DiskLruCache初始化不能通过构造方法,而是使用它的open方法

    public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) File directory 文件存储路径

    int appVersion app版本号,版本号改变DiskLruCache会清空之前所有的缓存文件

    int valueCount 对应数据的个数,一般设为1。

    long maxSize 缓存总数(eg:50M超过这个大小后DiskLruCache会清除一些缓存)

    DiskLruCache 的写入:

    private Bitmap loadBitmapFromHttp(String url,int reqWidth,int reqHeight){ if (Looper.myLooper() == Looper.getMainLooper()) {//这个方法不能在主线程中执行,会抛出异常 throw new IllegalArgumentException("Looper.myLooper() == Looper.getMainLooper()"); } if (mDiskLruCache == null) { return null; } String key = hashKeyFormUrl(url);//我们一般会用url作为key,但是有时url有特殊字符,故需要将url转为md5的值 Editor edit = mDiskLruCache.edit(key);//如果此时这个缓存正在编辑则返回null,diskLruCache不允许同时编辑一个缓存 if (edit != null) { OutputStream newOutputStream = edit.newOutputStream(DISK_CACHE_INDEX);//因为前面我设置了对应的数据个数为1此时DISK_CACHE_INDEX为0即//可 if (downloadUrlToStream (url , newOutputStream)) { edit.commit();//提交写入操作 }else{ edit.abort();//请求失败回退整个操作 } mDiskLruCache.flush(); } return loadBitmapFromDiskCache(url, reqWidth, reqHeight); }

    DiskLruCache 的缓存查找:

    private Bitmap loadBitmapFromDiskCache(String url,int reqWidth,int reqHeight){ if (Looper.myLooper() == Looper.getMainLooper()) { throw new IllegalArgumentException("Looper.myLooper() == Looper.getMainLooper()"); } if (mDiskLruCache == null) { return null; } Bitmap bitmap = null; String key = hashKeyFormUrl(url); Snapshot snapshot = mDiskLruCache.get(key);//通过key获得一个Snapshot对象 if (snapshot != null) { FileInputStream fileInputStream = (FileInputStream)snapshot.getInputStream(DISK_CACHE_INDEX);//获得输入流 FileDescriptor fd = fileInputStream.getFD(); bitmap = mImageResizer.decodeSampleBitmapFromFileDescriptor(fd,reqWidth,reqHeight); } return bitmap;

    图片的缓存Cache使用讲解结束了,如果读者想详细了解他们源码可上网查询。后面我会讲解一个具有图片的同步加载,图片的异步加载,图片压缩,磁盘缓存,网络拉取优秀的ImageLoad是怎么结合使用图片的Cache的。

    谢谢品读!!

    转载请注明原文地址: https://ju.6miu.com/read-6921.html

    最新回复(0)