您的位置:首页 > 其它

FindJpg(2)-BitMap的高效加载和缓存

2016-06-11 12:56 162 查看
一、BitMap的高效加载
因为这个小项目是从本地加载图片资源,所以会有图片压缩的过程。一般若是加载网络图片,应该在服务端按需压缩图片,这样既能节省流量,又能提高图片加载的流畅度。
BitMap高效加载的核心思想-采用BitmapFactory.Options来加载所需尺寸的图片:
因为许多时候ImageView并没有原始图片那么大,所以没必要将图片的原始尺寸加载进来,按一定的采样率来加载按需的缩小后的图片,会降低内存的占用,从而在一定程度上避免OOM,提高了Bitmap加载时的性能。
通过BitmapFactory.Options设置inSampleSize(即采样率)来缩放图片。例如一张像素为1024x1024的图片,若设置inSampleSize值为2,则采样后的图片为512x512,占用的内存缩小2*inSampleSize即4倍。

获取inSampleSize的主要流程:
将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片;从字面上就能看到,inJustDecodeBounds参数设为true时,BitmapFactory只解析图片的原始宽高信息,不会真正地加载图片,所以这个操作使得解码时避免了内存分配。
从BitmapFactory.Options取出图片原始宽高信息,对应于outWidth和outHeight参数。
结合目标View所需大小和采样率的规则(官方文档指出inSampleSize的取值应总是2的指数)。
将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。

具体代码如下:

publicBitmapdecodeSampledBitmapFromFile(String file, intreqWidth,
intreqHeight){

    final BitmapFactory.Options options=newBitmapFactory.Options();

    options.inJustDecodeBounds=true;//使BitmapFactory只解析图片原始宽/高信息,不会真正加载图片

    BitmapFactory.decodeFile(file,options);

    options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);//计算采样率

    options.inJustDecodeBounds=false;

    return BitmapFactory.decodeFile(file,options);//根据采样率重新加载图片
}

    public static int calculateInSampleSize(BitmapFactory.Options options,intreqWidth,intreqHeight){
//        图片的原始宽/高

        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;
//            一般建议inSampleSize为2的幂

            while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){

                inSampleSize*=2;

            }

        }

        return inSampleSize;

    }

}

二、BitMap的缓存
缓存策略主要包含缓存的 添加、获取、删除 这三类操作,因为不管内存缓存还是外存缓存,容量都是有限的,所以要删除旧缓存添加新缓存,如何定义缓存的新旧就对应不同的缓存策略。
目前常用的缓存算法是LRU(Least Recently Used),近期最小使用算法,核心思想是缓存满时,优先淘汰近期最少使用的缓存对象。采用LRU算法的缓存有两种:DiskLruCache和LruCache。
DiskLruCache
用于实现存储设备缓存,通过将缓存对象写入文件系统实现缓存效果。
应用场景一般是为了让用户避免流量消耗,将从网络上加载的图片(或其它文件等)缓存到存储设备上。
这里的FindJpg是从本地加载图片,所以用不到DiskLruCache。

LruCache
内部采用LinkedHashMap以强引用的方式存储外界的缓存对象,它是线程安全的。
LinkedHashMap会根据LRU算法来排列对象的顺序,新加入的对象添加到头部,刚被使用过的对象也被移动到头部,所以在链表尾部的对象是最久没有被使用过的,一旦链表满有新对象加入,就会删除链表尾部的对象。

相关代码如下:

   1)   先创建LruCache对象,内存缓存容量设为当前进程可用内存的1/8(安卓官方文档推荐),通过重写sizeOf()方法计算缓存对象(即图片)大小,单位得和总容量一致。

privateImageLoader(Context context){

    mContext=context.getApplicationContext();

    int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);//除1024是为了转KB

    int cacheSize=maxMemory/8;

    mMemoryCache=newLruCache<String,Bitmap>(cacheSize){

        @Override

        protected int sizeOf(String key,Bitmap bitmap){//计算Bitmap对象大小

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

        }

    };
}

   2)   编写向LruCache中添加、删除缓存对象(图片)的方法。

   private voidaddBitmapToMemoryCache(String
key,Bitmap bitmap){//向LruCache中添加一个缓存对象

        if(getBitmapFromMemCache(key)==null){
//            Log.e(TAG,"addBitmapToMemoryCache");

            mMemoryCache.put(key,bitmap);

        }

    }

    private BitmapgetBitmapFromMemCache(String key){//从LruCache中获取一个缓存对象

        return mMemoryCache.get(key);

    }//从LruCache中获取一个缓存对象

   3)   图片的加载,先尝试从内存加载图片,若内存中没有这张图片,再从外存中加载按需压缩的图片并放入内存缓存起来。

publicBitmaploadBitmap(String uri,intreqWidth,intreqHeight){

        Bitmap bitmap=loadBitmapFromMemCache(uri);

        if(bitmap!=null){
//            Log.e(TAG,"loadBitmapFromMemCache,url:"+uri);

            return bitmap;

        }

        try {

            bitmap = loadBitmapFromDisk(uri, reqWidth,reqHeight);

a6d5

            if (bitmap != null) {
//                Log.e(TAG, "loadBitmapFromDisk,url:" + uri);

                return bitmap;

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

        return bitmap;

    }

privateBitmaploadBitmapFromDisk(String url, intreqWidth,
intreqHeight)throwsIOException {

    if (Looper.myLooper() == Looper.getMainLooper()) {

        Log.w(TAG,"load bitmap from UI Thread, it's not recommended!");

    }

    Bitmap bitmap = null;

    bitmap = mImageResizer.decodeSampledBitmapFromFile(url,reqWidth,reqHeight);

    if (bitmap != null) {

        addBitmapToMemoryCache(url, bitmap);

    }

    return bitmap;
}

privateBitmaploadBitmapFromMemCache(String url){

    Bitmap bitmap=getBitmapFromMemCache(url);

    return bitmap;
}

Demo地址:FindJpg
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: