美文网首页
Android 开发艺术探索笔记(十六) 之 Bitmap 的加

Android 开发艺术探索笔记(十六) 之 Bitmap 的加

作者: innovatorCL | 来源:发表于2018-04-29 11:21 被阅读9次

一、Bitmap 的加载方法

BitmapFactory 类提供了 4 类方法来加载 Bitmap:decodeFile()decodeResource()decodeStream()decodeByteArray(),分别支持从文件系统、资源、输入流以及字节数组中加载出一个 Bitmap 对象。其中 decodeFile()decodeResource()间接调用了decodeStream()`,这四类方法最终是在 Android 底层实现的,对应着 BitmapFactory 类的几个 native 方法。

二、Android 中的缓存策略

一般的图片加载策略都是:当程序第一次从网络加载图片后,就将其缓存到存储设备上,这样下次加载使用这张图片就不用再从网络上获取了。很多时候,为了提高用户体验,往往会把图片放在内存中缓存一份,这样应用打算从网络中获取图片的时候,首先从内存中获取,如果内存中没有就去存储设备中获取,如果存储设备也没有就去从网络中加载图片。

缓存策略主要包括:缓存的添加、获取、删除。目前使用比较普遍的缓存算法是:LRU(Least Recently Used 近期最少使用算法)。LRU 算法的核心思想是:当缓存满时,会优先淘汰那些近期最少使用的缓存对象。它的缓存有两种:LruCache(内存缓存)和 DiskLruCache(磁盘缓存)。

  • Lrucache(内存缓存)

LruCache 类是一个泛型类,内部采用一个 LinkedHashMap 以强引用的方式存储外界的缓存对象。其提供了 get()put() 方法来完成缓存对象的存取。当缓存满的时候,LruCache 会移除较早使用的缓存对象,然后添加新的缓存对象。

        //内存缓存为进程最大可使用内存的 1/8,单位 KB
        int maxMemory = (int)(Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                //单位相同,转成 KB
                return value.getRowBytes() * value.getHeight() / 1024;
            }
        };
  • DiskLruCache(磁盘缓存)

通过将缓存对象写进文件系统以实现缓存的效果。

1、DiskLruCache 的创建:

public static DiskLruCache open(File dictory,int appVersion,int valueCount,long maxSize)

2、DiskLruCache 的缓存添加:

DiskLruCache 的缓存添加操作是通过 Editor 完成的,Edtior 表示一个缓存对象的编辑对象。以图片加载为例,获取图片缓存时,首先需要根据图片的 url 的 md5 值作为 key 来通过 edit() 获取 Editor 对象。

/**
     * 从网络中获取图片,存入磁盘缓存,然后加载到内存缓存中
     * @param url
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    private Bitmap loadBitmapFromHttp(String url,int reqWidth,int reqHeight) throws IOException{

        //不能在主线程调用
        if(Looper.myLooper() == Looper.getMainLooper()){
            throw new RuntimeException("cannot visit network from UI Thread");
        }

        if(mDiskLruCache == null){
            return null;
        }

        String key = hashKeyFromUrl(url);
        DiskLruCache.Editor editor = mDiskLruCache.edit(key);
        if(editor != null){
            OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);

            //存到磁盘缓存
            if(downloadUrlToStream(url,outputStream)){
                editor.commit();
            }else {
                editor.abort();
            }

            mDiskLruCache.flush();
        }

        return loadBitmapFromDiskCache(url,reqWidth,reqHeight);
    }

3、DiskLruCache 的缓存查找

缓存查找过程也是通过将 url 转成 key,然后通过 DiskLruCache 的 get() 获取一个 SnapShot 对象,紧接着通过 SnapShot 对象即可获得缓存文件的输入流,然后通过输出流得到 Bitmap 对象。

/**
     * 从磁盘缓存中加载Bitmap
     * @param url
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    private Bitmap loadBitmapFromDiskCache(String url,int reqWidth,int reqHeight) throws IOException{
        //不能在主线程调用
        if(Looper.myLooper() == Looper.getMainLooper()){
            throw new RuntimeException("cannot visit network from UI Thread");
        }

        if(mDiskLruCache == null){
            return null;
        }

        Bitmap bitmap = null;
        String key = hashKeyFromUrl(url);
        DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
        if (snapshot != null) {
            FileInputStream fileInputStream = (FileInputStream)snapshot.getInputStream(DISK_CACHE_INDEX);
            FileDescriptor fileDescriptor = fileInputStream.getFD();
            bitmap = mImageResizer.decodeSampleBitmapFromFileDescriptor(fileDescriptor,reqWidth,reqHeight);

            // 添加到内存缓存
            if (bitmap != null){
                addBitmapToMemoryCache(key,bitmap);
            }
        }

        return bitmap;
    }

三、自定义一个两级缓存的 ImageLoader

主要的思路:在加载图片的时候,首先判断是否有内存缓存,没有的话就去查找磁盘缓存,磁盘缓存仍然没有就去加载网络,然后将获取到的图片存储进磁盘缓存中,然后添加进内存缓存中。

源码

相关文章

网友评论

      本文标题:Android 开发艺术探索笔记(十六) 之 Bitmap 的加

      本文链接:https://www.haomeiwen.com/subject/lnomkftx.html