Bitmap的加载和Cache

    本次主要是包含3个方面的内容,首先讲述如何有效的加载一个bitmap。由于bitmap特殊性以及Android对单个应用所施加的内容限制。这个导致加载bitmap的时候很容易出现内存溢出。
    java.lang.OutOfMemoryError:bitmap size exceeds VM budget.
    根据上述情况,在android开发中需要经常对bitmap做缓存,通过缓存策略,我们不需要每次都从网络上请求图片或者从内存中加载图片,会极大的提高加载效率以及产品的用户体验。目前常用的缓存策略是LruCache和DiskLruCache。其中LruCache被看做是内存缓存,DiskLruCache被看做是存储缓存。通俗的来说就是:最少使用算法。
算法的核心思想:
    当缓存快满的时候,会淘汰近期最少使用的缓存目标,很显然Lru算法的思想是很容易被接受的。

Bitmap的高校加载:

    这个介绍biemap的高效加载之前,先说一下如何加载一个biemap。BitmapFactory类提供了四类方法:decodeFile、decodeRessource、decodeStream和decodeByteArray,分别是用于支持从文件系统、资源、输入流以及字节数组中加载出一二Bitmap对象。
    如何高效的加载一个Bitmap对象:其实核心思想也很简单,那就是采用BitmapFactory.Options来加载所需尺寸的图片。通过inSampleSize()可以获取到采样率来加载缩小后的图片,将缩小后的图片在对应的位置显示,这样会降低内存占用从而在一定程度上避免OOM。提高了加载时的性能。
    inSampleSize方法的值为1的时候,表示采样后的图片大小为图片的原始大小,当值为2的时候:

公式:
    采样后的图片大小其宽/高均为原图大小的1/2,像素数为原图的1/4

通过采样率就能有效的加载图片,那么到底如何获取采样率:
    (1)将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。
    (2)BitmapFactory.Options中获取图片的原始宽高信息,它们对应于outWidth和outHeight参数。
    (3)根据采样率的规则结合目标view的所需大小计算出采样率inSampleSize。
    (4)将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后冲洗加载图片。
将上面的四个流程用程序来实现,如下:


Android中的缓存策略:
    缓存的机制:
        当程序第一次从网上加载图片后,就将其缓存到内存设备上,这样下次使用这张图片就不用再从网络上获取了,这样就节省了用户的流量,提高了应用的用户体验。往往还会把图片在内存中再缓存一份,这样当应用打算从网络上请求一张图片的时,程序会先从内存中获取,如果内存设备中也没有,就从存储设备中获取,如果存储设备也没有,那就从网络上获取。
    目的:
        内存中加载图片比在存储中加载图片要快。既提高了程序的效率又为用户节省了不不必要的流量开销。

    缓存策略主要包含缓存的添加、获取和删除三类操作。
        添加:
        获取:
        删除:缓存是有大小的,所以会有删除的操作。

LRU算法:
    概念:常用的缓存算法,LRU是近期最少使用算法。核心思想就是当缓存满时,会优先淘汰近期使用最少的缓存对象。LRU算法的缓存有两种:LurCache和DiskLruCache, LurCache用于实现内存缓存,DiskLruCache则充当了存储设备缓存。

LurCache:
    LurCache是android3.1提供的缓存类,通过support-V4兼容包可以兼容到早期的android版本。
    LurCache是一个泛型类,内部采用LinkedHashMap以强引用的方式存储外界的缓存对象。提供了get和put方法来完成缓存的获取和添加操作。
public class LruCache< K , V > {
private final LinkedHashMap< K , V > map ;
    初始化:
    /**
         * LruCache的创建
         * return:    返回的是位图每一行 所占用的空间数*位图的行数   
         *             最后除以1024得到的是KB。
         */
        int maxMemory = (int)(Runtime.getRuntime().maxMemory()/1024);
        //总容量的大小为当前进程的可用内存的1/8,单位是KB。
        int cacheSize = maxMemory/8;
        LruCache<String,Bitmap>linkedHashMap = new LruCache<String,Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {

                return bitmap.getRowBytes()*bitmap.getHeight()/1024;
            }
        };

    获取: linkedHashMap .get(key);
    添加:linkedHashMap.put(key,bitmap);
    删除:linkedHashMap.remove(key);

DiskLruCache:
    概念:DiskLruCache用于实现存储设备缓存,即磁盘缓存,他通过将缓存对象写入文件系统从而实现缓存效果。

    创建:
        public static DiskLruCache open(File directory,int appVersion,iint valueCount,long maxSize)
        
        private static final long Disk_cache_size = 1024*1024*50;  //50MB
        File diskCacheDir = getDiskCacheDir(mContent,"bitmap");
        if(!diskCacheDir.exists()){
            diskCacheDir.mkdirs();
        }    
        mDiskLruCache = DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);

    添加:

        private String hashKeyFormurl(String url) {
        String  cacheKey;
        try {

            MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(url.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (Exception e) {
            cacheKey = String.valueOf(url.hashCode());
        }
        return cacheKey;

    }

    private String bytesToHexString(byte[] digest) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < digest.length; i++) {
            String hex = Integer.toHexString(0xff & digest[i]);
            if(hex.length() == 1){
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    调用:
    String key = hashKeyFormUrl(url);
    DiskLruCache.Editor editor =  mDiskLruCache .edit(key);
    if(editor != null ){
        OutPutStream ops = editor.newOutPutStream(DISK_CACHE_INDEX);
    }

    当从网络下载图片时,图片流可以通过这个文件输入流写入到文件系统上,实现代码如下:
    public boolean downloadUrlToStream(String urlString,
            OutputStream outputStream) {
        try {
            URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(),
                    IO_BUFFER_SIZE);
            out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);
            int b;
            while ((b = in.read()) != -1) {
                out.write(b);
            }
            return true;
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            try {
                in.close();
                out.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return false;
    }

    调用:
    OutputStream outputStream = Editor.newOutPutStream(DISK_CACHE_INDEX);
    if(downloadUrlToStream(url,outputStream)){
        editor.commit();
    }else {
         editor.abort();
    }
    mDiskLruCache.flush();

上述代码,图片已经被正确的写入到文件系统中了,

缓存查找:
    通过 DiskLruCache方法的get方法得到一个Snapshot对象,紧接着通过Snapshot得到一个文件输入流,自然就能得到bitmap对象了。为了避免OOM,需要参考前面所说的,对图片进行缩放处理,再予以显示,事例代码如下显示:
    String key = hashKeyFormurl(url);
        try {
            DiskLruCache.Snapshot snapshot = mDiskLruCache.get("key");
            if (snapshot != null) {
                FileInputStream fileInputStream = (FileInputStream) snapshot
                        .getInputStream(DISK_CACHE_INDEX);
                FileDescriptor fileDescriptor = fileInputStream.getFD();
                Object mImageResizer;
                Bitmap bitmap = mImageResizer
                        .decodeSampleBitmapFromFileDescriptor(fileDescriptor,
                                reqWidth, reqHeight);
                if (bitmap != null) {
                    addBitmapToMemoryCache(key,bitmap);
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值