内存优化之图片优化

来源:互联网 发布:橙光 闪艺 知乎 编辑:程序博客网 时间:2024/05/18 14:25

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

压缩图片

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

/**             * 加载内存卡图片             */            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");            }

1 软引用

只要有足够的内存,就一直保持对象,直到发现内存吃紧且没有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();                }            }        }

3 两者比较

说到这里,我觉得有必要来进行一下比较了。
网上有很多人使用软引用加载图片的多 ,但是现在已经不再推荐使用这种方式了,
(1)因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,
这让软引用和弱引用变得不再可靠。
(2)另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃,所以我这里用得是LruCache来缓存图片,当存储Image的大小大于LruCache设定的值,系统自动释放内存,
这个类是3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包。后记:我一直有强调一件事件,就是人应该要不停地进步,没有人生来就会编码,
更没有人一开始就能找到很好的解决方案,我介绍了这两种用法,其实就是想说,
这些都是我的技术进步的一个历程。如果大家有好的建议或者有什么好的看法,
记得提出来,很高兴能和大家分享。

0 0