1、有效的处理较大的位图
图像有各种不同的形状和大小。在许多情况下,它们往往比一个典型应用程序的用户界面(UI)所需要的资源更大。
读取一个位图的尺寸和类型:
为了从多种资源来创建一个位图,BitmapFactory类提供了几个解码的方法(decodeByteArray(),decodeFile(),decodeResource()等等),根据你的图像数据资源选择最合适的解码方法。这些方法试图请求分配内存来构造位图,因此很容易导致OutOfMemory异常。每种类型的解码方式都有额外的特征可以让你通过BitMapFactory.Options类指定解码选项。当解码时避免内存分配可以设置inJustDecodeBounds属性为true,位图对象返回null但是设置了outWidth,outHeight和outMimeType。这种技术允许你在创建位图(和分配内存)之前去读取图像的尺寸和类型。
加载一个缩小版本到内存中:
现在的图像尺寸都是已知的,它们可以被用来决定是否应该加载完整的图片到内存或者是是否用一个缩小的版本去代替加载
以下是一些值得考虑的因素:
- 估计加载完整图像所需要的内存
- 你承诺家在这个图片所需空间带给你的程序的其他内存需求
- 准备加载图像的目标ImageView或UI组建的尺寸
- 当前设备的屏幕尺寸和密度
告诉解码器去重新采样这个图像,加载一个更小的版本到内存中,在你的BitmapFactory.Option对象中设置inSampleSize为true。例如,讲一个分辨率为2048*1536的图像用inSampleSize值为4去编码,将产生一个大小约为512*384的位图,加载到内存中仅使用0.75M,而不是完整的12M。这里有一个方法在目标的宽度和高度的基础上来计算一个SampleSize的值。
//计算位图的采样比例 public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { //获取位图的原宽高 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { if (width > height) { //Math.round四舍五入 inSampleSize = Math.round((float) height / (float) reqHeight); } else { inSampleSize = Math.round((float) width / (float) reqWidth); } } return inSampleSize; }
提示:使用2的幂数设置inSampleSize的值可以使解码器更快更有效。然而,如果你想在内存或硬盘中缓存一个图片调整后的版本,通常解码到合适的图像尺寸更适合来节省空间。
要使用这种方法,首先解码,将inJustDecodeBounds设置为true,将选项传递进去,然后再次解码,再使用新的inSampleSize值并将inJustDecodeBounds设置为false:
//位图重新采样 public Bitmap decodeSampledBitmapFromResource(Resources res, int resid, int reqWidth, int reqHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resid,options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeResource(res, resid,options); return bitmap; }
示例:
public void showClick(View view) { //显示50*50像素的图片 Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.ic_welcome1, 50, 50); iv.setImageBitmap(bitmap); }
2、缓存位图
(1)内存缓存
内存缓存提供了可以快速访问位图LruCache类用于缓存位图的任务,最近被引用的对象保存在一个被引用的LinkedHashMap中,以及在缓存超过了其指定的大小之前释放最近很少使用的对象的内存。
LRU:least recently used近期最少使用算法
public class MainActivity extends Activity { private ImageView iv; private LruCache<String, Bitmap> lruCache; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.imageView); //获取当前Activity内存大小(基线16M,手机不同可能也不同),缓存大小基本为内存的1/8 ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE); int memoryClass = am.getMemoryClass(); //1/8的内存大小作为缓存大小 final int cacheSize = memoryClass / 8 * 1024 * 1024; lruCache = new LruCache<>(cacheSize); } //添加缓存方法 public void addBitmapToCache(String key, Bitmap bitmap) { if (getBitmapFromCache(key) == null) { lruCache.put(key, bitmap); } } //从缓存中读取方法 public Bitmap getBitmapFromCache(String key) { return lruCache.get(key); } public void showClick(View view) { String key = String.valueOf(R.drawable.ic_welcome1); Bitmap bitmap = getBitmapFromCache(key); if (bitmap == null) { //显示50*50像素的图片 bitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.ic_welcome1, 50, 50); addBitmapToCache(key, bitmap); } else { System.out.println("LruCache中有位图"); } iv.setImageBitmap(bitmap); } //位图重新采样 public Bitmap decodeSampledBitmapFromResource(Resources res, int resid, int reqWidth, int reqHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resid, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeResource(res, resid, options); return bitmap; } //计算位图的采样比例 public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { //获取位图的原宽高 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { if (width > height) { //Math.round四舍五入 inSampleSize = Math.round((float) height / (float) reqHeight); } else { inSampleSize = Math.round((float) width / (float) reqWidth); } } return inSampleSize; } }