概述:随着手机硬件水平的提高及摩尔效应带来的成本的降低,用户手机上的图片的分辨率越来越高,而对于移动终端而言内存存在一定的限制的,如果程序占用过高的内存,也会抛出OOM异常,降低了APP的用户体验感。另外自己做的APP应该尽可能的减少安装包的体积。本博客主要讲述通过压缩图片来减少安装包体积和通过使用图片缓存技术来高效加载大图。
一 压缩图片:
在加载高分辨率图片的时候,最好先将图片进行压缩,一般情况下将图片压缩到大小与展示该图片的控件的大小差不多就可以。比如说:我们常用的腾讯QQ,在展示用户图像的控件上显示的实际上是压缩过的用户上传后的图片,当你要查看自己上传高清图像时,腾讯专门提供了一个查看大图的功能,此时才会将用户上传的图片显示出来,这样做的目的当然是为了减少程序占用的内存。
压缩图片主要是用到了安卓BitmapFactory.Options类,主要是通过设置options.inJustDecodeBounds = true; 这样就能在不将该bitmap加载到内存的情况下获得该图片的大小方面的属性,如:options.outHeight,options.outWidth。这样就可以根据需求对这些大小属性进行相应的压缩操作,记得在压缩操作完成后要设置options.inJustDecodeBounds
= false;代码如下:
/**@author htq * 在该方法中对图片进行压缩,可以减少加载图片时占用的java内存。 * @param path * @return */ private Bitmap loadBitmapFromLocal(String path) { if (path == null) { return null; } BitmapFactory.Options options = new Options(); options.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeFile(path, options); float height = 800f; float width = 480f; float scale = 1; if (options.outWidth > width && options.outWidth > options.outHeight) { scale = options.outWidth / width; } else if (options.outHeight > height && options.outHeight > options.outWidth) { scale = options.outHeight / height; } else { scale = 1; } options.inSampleSize = (int) scale; options.inJustDecodeBounds = false; bitmap = BitmapFactory.decodeFile(path, options); bitmap = decodeBitmap(bitmap); if (!imgCaches.containsKey(path)) { //imgCaches.put(path, new SoftReference<Bitmap>(bitmap)); addCache(path, bitmap); } return bitmap; }
/**@author htq * 该方法是对图片进行质量压缩,不会减少图片的像素,所以转化为bitmap后java内存占用与压缩前相同 * 仅仅用来减少图片的大小。 */ private Bitmap decodeBitmap(Bitmap bitmap) { int scale = 100; ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, scale, bos); while ((bos.toByteArray().length / 1024) > 30) { bos.reset(); bitmap.compress(Bitmap.CompressFormat.JPEG, scale, bos); scale -= 10; } ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); bitmap = BitmapFactory.decodeStream(bis); return bitmap; }
二 使用图片缓存技术来高效加载大图
2.1使用软引用来缓存图片:主要是通过一个HashMap来将图片路径和图片资源(用软引用表示)关联起来。代码如下:
private static HashMap<String, SoftReference<Bitmap>> imgCaches; imgCaches = new HashMap<String, SoftReference<Bitmap>>();具体加载图片步骤如下:
1.通过path路径查看缓存中是否存在该图片,如果存在,则直接加载,如果不存在,则开启一个子线程从网络中获取。
2.图片下载完成后,将该图片放入到缓存中,供下次查询时使用。具体代码如下:
2.图片下载完成后,将该图片放入到缓存中,供下次查询时使用。具体代码如下:
public void loadBitmap(final String path, final OnLoadBitmapListener listener) { final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Bitmap bitmap = (Bitmap) msg.obj; listener.loadImage(bitmap, path); } }; new Thread() { @Override public void run() { executorThreadPool.execute(new Runnable() { @Override public void run() { Bitmap bitmap = loadBitmapFromCache(path); if (bitmap != null) { Message msg = handler.obtainMessage(); msg.obj = bitmap; handler.sendMessage(msg); } } }); } }.start(); }
2.2内存缓存技术:主要是通过LruCache这个类来缓存图片,故名思意可知,当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。
使用该类时要预先设置缓存图片内存大小,且必须重写该类的sizeof()方法,默认返回图片数量。代码如下:
private LruCache<String, Bitmap> mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // 重写此方法来衡量每张图片的大小,默认返回图片数量。 return bitmap.getByteCount() / 1024; } };
与上述使用软引用来加载缓存图片类似,首先会在 LruCache 的缓存中进行检查。如果找到了该图片的缓存,则直接加载,否则开启一个后台线程来加载这张图片。代码如下:
<pre name="code" class="html">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(); } } }