«

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

时间:2024-3-2 18:39     作者:韩俊     分类: Android


转自:http://blog.chinaunix.net/uid-26930580-id-4138306.html

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

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

/**

     * 加载内存卡图片<br style="word-wrap:break-word">

     */<br style="word-wrap:break-word">

    BitmapFactory.Options options = new BitmapFactory.Options();<br style="word-wrap:break-word">

    options.inJustDecodeBounds = true; //

设置了此属性一定要记得将值设置为false

    Bitmap bitmap = null;<br style="word-wrap:break-word">

    bitmap = BitmapFactory.decodeFile(url, options);<br style="word-wrap:break-word">

    int be = (int) ((options.outHeight &gt; options.outWidth ? options.outHeight / 150<br style="word-wrap:break-word">

            : options.outWidth / 200));<br style="word-wrap:break-word">

    if (be &lt;= 0) //

判断200是否超过原始图片高度

        be = 1; // 如果超过,则不进行缩放<br style="word-wrap:break-word">

    options.inSampleSize = be;<br style="word-wrap:break-word">

    options.inPreferredConfig = Bitmap.Config.ARGB_4444;<br style="word-wrap:break-word">

    options.inPurgeable = true;<br style="word-wrap:break-word">

    options.inInputShareable = true;<br style="word-wrap:break-word">

    options.inJustDecodeBounds = false;<br style="word-wrap:break-word">

    try {<br style="word-wrap:break-word">

        bitmap = BitmapFactory.decodeFile(url, options);<br style="word-wrap:break-word">

    } catch (OutOfMemoryError e) {<br style="word-wrap:break-word">

        System.gc();<br style="word-wrap:break-word">

        Log.e(TAG, &quot;OutOfMemoryError&quot;);<br style="word-wrap:break-word">

    }





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&lt;Bitmap&gt; reference = imageMap.get(imageUrl);<br style="word-wrap:break-word">

    if(reference != null) {<br style="word-wrap:break-word">

        if(reference.get() != null) {<br style="word-wrap:break-word">

            return reference.get();<br style="word-wrap:break-word">

        }<br style="word-wrap:break-word">

    }<br style="word-wrap:break-word">

    final Handler handler = new Handler() {<br style="word-wrap:break-word">

        public void handleMessage(final android.os.Message

msg) {

            //加入到缓存中<br style="word-wrap:break-word">

            Bitmap bitmap = (Bitmap)msg.obj;<br style="word-wrap:break-word">

            imageMap.put(imageUrl, new SoftReference&lt;Bitmap&gt;(bitmap));<br style="word-wrap:break-word">

            if(imageCallBack != null) {<br style="word-wrap:break-word">

                imageCallBack.getBitmap(bitmap);<br style="word-wrap:break-word">

            }<br style="word-wrap:break-word">

        }<br style="word-wrap:break-word">

    };<br style="word-wrap:break-word">

    new Thread(){<br style="word-wrap:break-word">

        public void run() {<br style="word-wrap:break-word">

            Message message = handler.obtainMessage();<br style="word-wrap:break-word">

            message.obj = downloadBitmap(imageUrl);<br style="word-wrap:break-word">

            handler.sendMessage(message);<br style="word-wrap:break-word">

        }<br style="word-wrap:break-word">

    }.start();<br style="word-wrap:break-word">

    return null ;<br style="word-wrap:break-word">

}<br style="word-wrap:break-word">


// 从网上下载图片<br style="word-wrap:break-word">

private Bitmap downloadBitmap (String imageUrl) {<br style="word-wrap:break-word">

    Bitmap bitmap = null;<br style="word-wrap:break-word">

    try {<br style="word-wrap:break-word">

        bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());<br style="word-wrap:break-word">

        return bitmap ;<br style="word-wrap:break-word">

    } catch (Exception e) {<br style="word-wrap:break-word">

        e.printStackTrace();<br style="word-wrap:break-word">

        return null;<br style="word-wrap:break-word">

    } <br style="word-wrap:break-word">

}

public interface ImageCallBack{<br style="word-wrap:break-word">

    void getBitmap(Bitmap bitmap);<br style="word-wrap:break-word">

}





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)<br style="word-wrap:break-word">

        mMemoryCache = new LruCache&lt;String, Bitmap&gt;(<br style="word-wrap:break-word">

                MAXMEMONRY / 8) {<br style="word-wrap:break-word">

            @Override<br style="word-wrap:break-word">

            protected int sizeOf(String key, Bitmap

bitmap) {

                // 重写此方法来衡量每张图片的大小,默认返回图片数量。<br style="word-wrap:break-word">

                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;<br style="word-wrap:break-word">

            }<br style="word-wrap:break-word">


            @Override<br style="word-wrap:break-word">

            protected void entryRemoved(boolean evicted, String key,<br style="word-wrap:break-word">

                    Bitmap oldValue, Bitmap newValue) {<br style="word-wrap:break-word">

                Log.v(&quot;tag&quot;, &quot;hard

cache is full , push to soft cache");

               <br style="word-wrap:break-word">

            }<br style="word-wrap:break-word">

        };<br style="word-wrap:break-word">

}

 (4)下面的方法分别是清空缓存、添加图片到缓存、从缓存中取得图片、从缓存中移除。<br style="line-height:26px; word-wrap:break-word; color:rgb(102,102,102); font-family:宋体,Arial; font-size:16px">
      移除和清除缓存是必须要做的事,因为图片缓存处理不当就会报内存溢出,所以一定要引起注意。<br style="line-height:26px; word-wrap:break-word; color:rgb(102,102,102); font-family:宋体,Arial; font-size:16px">

public void clearCache() {

    if (mMemoryCache != null) {<br style="word-wrap:break-word">

        if (mMemoryCache.size() &gt; 0) {<br style="word-wrap:break-word">

            Log.d(&quot;CacheUtils&quot;,<br style="word-wrap:break-word">

                    &quot;mMemoryCache.size() &quot; &#43; mMemoryCache.size());<br style="word-wrap:break-word">

            mMemoryCache.evictAll();<br style="word-wrap:break-word">

            Log.d(&quot;CacheUtils&quot;, &quot;mMemoryCache.size()&quot; &#43; mMemoryCache.size());<br style="word-wrap:break-word">

        }<br style="word-wrap:break-word">

        mMemoryCache = null;<br style="word-wrap:break-word">

    }<br style="word-wrap:break-word">

}<br style="word-wrap:break-word">


public synchronized void addBitmapToMemoryCache(String key, Bitmap

bitmap) {

    if (mMemoryCache.get(key) == null) {<br style="word-wrap:break-word">

        if (key != null &amp;&amp; bitmap != null)<br style="word-wrap:break-word">

            mMemoryCache.put(key, bitmap);<br style="word-wrap:break-word">

    } else<br style="word-wrap:break-word">

        Log.w(TAG, &quot;the

res is aready exits");

}<br style="word-wrap:break-word">


public synchronized Bitmap getBitmapFromMemCache(String key) {<br style="word-wrap:break-word">

    Bitmap bm = mMemoryCache.get(key);<br style="word-wrap:break-word">

    if (key != null) {<br style="word-wrap:break-word">

        return bm;<br style="word-wrap:break-word">

    }<br style="word-wrap:break-word">

    return null;<br style="word-wrap:break-word">

}<br style="word-wrap:break-word">


/**<br style="word-wrap:break-word">

 * 移除缓存<br style="word-wrap:break-word">

 * <br style="word-wrap:break-word">

 * @param key<br style="word-wrap:break-word">

 */<br style="word-wrap:break-word">

public synchronized void removeImageCache(String key) {<br style="word-wrap:break-word">

    if (key != null) {<br style="word-wrap:break-word">

        if (mMemoryCache != null) {<br style="word-wrap:break-word">

            Bitmap bm = mMemoryCache.remove(key);<br style="word-wrap:break-word">

            if (bm != null)<br style="word-wrap:break-word">

                bm.recycle();<br style="word-wrap:break-word">

        }<br style="word-wrap:break-word">

    }<br style="word-wrap:break-word">

}



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包。



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

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

这些都是我的技术进步的一个历程。如果大家有好的建议或者有什么好的看法,

记得提出来,很高兴能和大家分享。

标签: android

热门推荐