您的位置:首页 > 其它

图片缓存之内存缓存技术LruCache,软引用

2016-09-20 14:59 387 查看
图片缓存之内存缓存技术LruCache,软引用

每当碰到一些大图片的时候,我们如果不对图片进行处理就会报OOM异常,
这个问题曾经让我觉得很烦恼,后来终于得到了解决,
那么现在就让我和大家一起分享一下吧。
这篇博文要讲的图片缓存机制,我接触到的有两钟,一种是软引用,另一种是内存缓存技术。
先来看下两者的使用方式,再来作比较。
除了加载图片时要用到缓存处理,还有一个比较重要的步骤要做,就是要先压缩图片。

1、压缩图片
至于要压缩到什么状态就要看自己当时的处境了,压缩图片的时候既要达到一个小的值,又不能让其模糊
,更不能拉伸图片。

/**

         * 加载内存卡图片

         */

        BitmapFactory.Options options
= new BitmapFactory.Options();

        options.inJustDecodeBounds
= true; // 设置了此属性一定要记得将值设置为false

        Bitmap bitmap =
null;

        bitmap = BitmapFactory.decodeFile(url, options);

        int be
= (int)
((options.outHeight
> options.outWidth
? options.outHeight
/ 150

                : options.outWidth
/ 200));

        if (be
<= 0)
// 判断200是否超过原始图片高度

            be = 1;
// 如果超过,则不进行缩放

        options.inSampleSize
= be;

        options.inPreferredConfig
= Bitmap.Config.ARGB_4444;

        options.inPurgeable
= true;

        options.inInputShareable
= true;

        options.inJustDecodeBounds
= false;

        try {

            bitmap = BitmapFactory.decodeFile(url, options);

        } catch
(OutOfMemoryError e)
{

            System.gc();

            Log.e(TAG,
"OutOfMemoryError");

        }

2、软引用:
只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有Strong
Ref时才回收对象。
我们可以这样定义:map里面的键是用来放图片地址的,既可以是网络上的图片地址,也可以SDcard上的图片地址,
map里面的值里面放的是持有软引用的Bitmap,当然如果你要放Drawable,那也是可以的。

private
Map<String,
SoftReference<Bitmap>> imageMap 

                                           =
new HashMap<String,
SoftReference<Bitmap>>();

接下来就让我再介绍一下如何具体加载图片:
步骤:(1)先通过URL查看缓存中是否有图片,如果有,则直接去缓存中取得。
          
如果没有,就开线程重新去网上下载。
     
(2)下载完了之后,就把图片放在缓存里面,方便下次可以直接从缓存中取得。

public Bitmap loadBitmap(final
String imageUrl,final ImageCallBack imageCallBack)
{

        SoftReference<Bitmap>
reference = imageMap.get(imageUrl);

        if(reference
!=
null) {

            if(reference.get()
!=
null) {

                return
reference.get();

            }

        }

        final
Handler handler
= new Handler()
{

            public
void handleMessage(final android.os.Message msg)
{

                //加入到缓存中

                Bitmap bitmap =
(Bitmap)msg.obj;

                imageMap.put(imageUrl,
new SoftReference<Bitmap>(bitmap));

                if(imageCallBack
!=
null) {

                    imageCallBack.getBitmap(bitmap);

                }

            }

        };

        new Thread(){

            public
void run()
{

                Message message =
handler.obtainMessage();

                message.obj
= downloadBitmap(imageUrl);

                handler.sendMessage(message);

            }

        }.start();

        return
null ;

    }

    // 从网上下载图片

    private Bitmap downloadBitmap
(String imageUrl)
{

        Bitmap bitmap =
null;

        try {

            bitmap = BitmapFactory.decodeStream(new
URL(imageUrl).openStream());

            return bitmap
;

        } catch
(Exception e)
{

            e.printStackTrace();

            return
null;

        }

    }
    public interface ImageCallBack{

        void getBitmap(Bitmap bitmap);

    }

2、内存缓存技术
另外一种图片缓存的方式就是内存缓存技术。在Android中,有一个叫做LruCache类专门用来做图片缓存处理的。
它有一个特点,当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。
步骤:(1)要先设置缓存图片的内存大小,我这里设置为手机内存的1/8,
          
手机内存的获取方式:int MAXMEMONRY = (int) (Runtime.getRuntime()
.maxMemory() / 1024);
     
(2)LruCache里面的键值对分别是URL和对应的图片
     
(3)重写了一个叫做sizeOf的方法,返回的是图片数量。

private LruCache<String, Bitmap> mMemoryCache;

private LruCacheUtils()
{

        if (mMemoryCache
==
null)

            mMemoryCache =
new LruCache<String, Bitmap>(

                    MAXMEMONRY
/ 8) {

                @Override

                protected
int sizeOf(String
key, Bitmap bitmap)
{

                    // 重写此方法来衡量每张图片的大小,默认返回图片数量。

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

                }

                @Override

                protected
void entryRemoved(boolean evicted,
String key,

                        Bitmap oldValue, Bitmap newValue)
{

                    Log.v("tag",
"hard cache is full , push to soft cache");

                  

                }

            };

    }

    
(4)下面的方法分别是清空缓存、添加图片到缓存、从缓存中取得图片、从缓存中移除。
         
移除和清除缓存是必须要做的事,因为图片缓存处理不当就会报内存溢出,所以一定要引起注意。

public
void clearCache()
{

        if (mMemoryCache
!=
null) {

            if
(mMemoryCache.size()
> 0)
{

                Log.d("CacheUtils",

                        "mMemoryCache.size() "
+ mMemoryCache.size());

                mMemoryCache.evictAll();

                Log.d("CacheUtils",
"mMemoryCache.size()"
+ mMemoryCache.size());

            }

            mMemoryCache =
null;

        }

    }

    public synchronized
void addBitmapToMemoryCache(String
key, Bitmap bitmap)
{

        if (mMemoryCache.get(key)
==
null) {

            if
(key !=
null && bitmap
!=
null)

                mMemoryCache.put(key, bitmap);

        } else

            Log.w(TAG,
"the res is aready exits");

    }

    public synchronized Bitmap getBitmapFromMemCache(String
key)
{

        Bitmap bm = mMemoryCache.get(key);

        if (key
!=
null) {

            return bm;

        }

        return
null;

    }

    /**

     * 移除缓存

     *

     * @param key

     */

    public synchronized
void removeImageCache(String
key)
{

        if (key
!=
null) {

            if
(mMemoryCache !=
null)
{

                Bitmap bm = mMemoryCache.remove(key);

                if
(bm !=
null)

                    bm.recycle();

            }

        }

    }

4、两者的比较
说到这里,我觉得有必要来进行一下比较了。
网上有很多人使用软引用加载图片的多
,但是现在已经不再推荐使用这种方式了,
(1)因为从
Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,

     这让软引用和弱引用变得不再可靠。
(2)另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,

     因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃,
所以我这里用得是LruCache来缓存图片,当存储Image的大小大于LruCache设定的值,系统自动释放内存,

这个类是3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包。

后记:我一直有强调一件事件,就是人应该要不停地进步,没有人生来就会编码,

更没有人一开始就能找到很好的解决方案,我介绍了这两种用法,其实就是想说,

这些都是我的技术进步的一个历程。如果大家有好的建议或者有什么好的看法,
记得提出来,很高兴能和大家分享。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: