Bitmap引起的OOM问题怎么解决

其他教程   发布日期:2025年04月21日   浏览次数:183

这篇文章主要讲解了“Bitmap引起的OOM问题怎么解决”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Bitmap引起的OOM问题怎么解决”吧!

1.什么是OOM?为什么会引起OOM?

答:Out Of Memory(内存溢出),我们都知道Android系统会为每个APP分配一个独立的工作空间,或者说分配一个单独的Dalvik虚拟机,这样每个APP都可以独立运行而不相互影响!而Android对于每个Dalvik虚拟机都会有一个最大内存限制,如果当前占用的内存加上我们申请的内存资源超过了这个限制,系统就会抛出OOM错误!另外,这里别和RAM混淆了,即时当前RAM中剩余的内存有1G多,但是OOM还是会发生!别把RAM(物理内存)和OOM扯到一起!另外RAM不足的话,就是杀应用了,而不是仅仅是OOM了!而这个Dalvik中的最大内存标准,不同的机型是不一样的,可以调用:

  1. ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
  2. Log.e("HEHE","最大内存:" + activityManager.getMemoryClass());

获得正常的最大内存标准,又或者直接在命令行键入:

  1. adb shell getprop | grep dalvik.vm.heapgrowthlimit

你也可以打开系统源码/system/build.prop文件,看下文件中这一部分的信息得出:

  1. dalvik.vm.heapstartsize=8m
  2. dalvik.vm.heapgrowthlimit=192m
  3. dalvik.vm.heapsize=512m
  4. dalvik.vm.heaptargetutilization=0.75
  5. dalvik.vm.heapminfree=2m
  6. dalvik.vm.heapmaxfree=8m

你也可以试试自己手头的机子~

好啦,不扯了,关于OOM问题的产生,就扯到这里,再扯就到内存管理那一块了,可是个大块头,现在还啃不动...下面我们来看下避免Bitmap OOM的一些技巧吧!

2.避免Bitmap引起的OOM技巧小结

1)采用低内存占用量的编码方式

上一节说了BitmapFactory.Options这个类,我们可以设置下其中的inPreferredConfig属性,默认是Bitmap.Config.ARGB_8888,我们可以修改成Bitmap.Config.ARGB_4444Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位Bitmap.Config ARGB_8888:每个像素占八位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位默认使用ARGB_8888,即一个像素占4个字节!

2)图片压缩

同样是BitmapFactory.Options,我们通过inSampleSize设置缩放倍数,比如写2,即长宽变为原来的1/2,图片就是原来的1/4,如果不进行缩放的话设置为1即可!但是不能一味的压缩,毕竟这个值太小的话,图片会很模糊,而且要避免图片的拉伸变形,所以需要我们在程序中动态的计算,这个inSampleSize的合适值,而Options中又有这样一个方法:inJustDecodeBounds,将该参数设置为true后,decodeFiel并不会分配内存空间,但是可以计算出原始图片的长宽,调用options.outWidth/outHeight获取出图片的宽高,然后通过一定的算法,即可得到适合的inSampleSize,这里感谢街神提供的代码——摘自鸿洋blog!

  1. public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
  2. int width = options.outWidth;
  3. int height = options.outHeight;
  4. int inSampleSize = 1;
  5. if (width > reqWidth || height > reqHeight) {
  6. int widthRadio = Math.round(width * 1.0f / reqWidth);
  7. int heightRadio = Math.round(height * 1.0f / reqHeight);
  8. inSampleSize = Math.max(widthRadio, heightRadio);
  9. }
  10. return inSampleSize;
  11. }

然后使用下上述的方法即可:

  1. BitmapFactory.Options options = new BitmapFactory.Options();
  2. options.inJustDecodeBounds = true; // 设置了此属性一定要记得将值设置为false
  3. Bitmap bitmap = null;
  4. bitmap = BitmapFactory.decodeFile(url, options);
  5. options.inSampleSize = computeSampleSize(options,128,128);
  6. options.inPreferredConfig = Bitmap.Config.ARGB_4444;
  7. /* 下面两个字段需要组合使用 */
  8. options.inPurgeable = true;
  9. options.inInputShareable = true;
  10. options.inJustDecodeBounds = false;
  11. try {
  12. bitmap = BitmapFactory.decodeFile(url, options);
  13. } catch (OutOfMemoryError e) {
  14. Log.e(TAG, "OutOfMemoryError");
  15. }

3.及时回收图像

如果引用了大量的Bitmap对象,而应用又不需要同时显示所有图片。可以将暂时不用到的Bitmap对象及时回收掉。对于一些明确知道图片使用情况的场景可以主动recycle回收,比如引导页的图片,使用完就recycle,帧动画,加载一张,画一张,释放一张!使用时加载,不显示时直接置null或recycle!比如:imageView.setImageResource(0); 不过某些情况下会出现特定图片反复加载,释放,再加载等,低效率的事情...

4.其他方法

1.简单通过SoftReference引用方式管理图片资源

建个SoftReference的hashmap使用图片时先查询这个hashmap是否有softreference, softreference里的图片是否为空,如果为空就加载图片到softreference并加入hashmap。无需再代码里显式的处理图片的回收与释放,gc会自动处理资源的释放。这种方式处理起来简单实用,能一定程度上避免前一种方法反复加载释放的低效率。但还不够优化。

示例代码:

  1. private Map<String, SoftReference<Bitmap>> imageMap
  2. = new HashMap<String, SoftReference<Bitmap>>();
  3. public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
  4. SoftReference<Bitmap> reference = imageMap.get(imageUrl);
  5. if(reference != null) {
  6. if(reference.get() != null) {
  7. return reference.get();
  8. }
  9. }
  10. final Handler handler = new Handler() {
  11. public void handleMessage(final android.os.Message msg) {
  12. //加入到缓存中
  13. Bitmap bitmap = (Bitmap)msg.obj;
  14. imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap));
  15. if(imageCallBack != null) {
  16. imageCallBack.getBitmap(bitmap);
  17. }
  18. }
  19. };
  20. new Thread(){
  21. public void run() {
  22. Message message = handler.obtainMessage();
  23. message.obj = downloadBitmap(imageUrl);
  24. handler.sendMessage(message);
  25. }
  26. }.start();
  27. return null ;
  28. }
  29. // 从网上下载图片
  30. private Bitmap downloadBitmap (String imageUrl) {
  31. Bitmap bitmap = null;
  32. try {
  33. bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
  34. return bitmap ;
  35. } catch (Exception e) {
  36. e.printStackTrace();
  37. return null;
  38. }
  39. }
  40. public interface ImageCallBack{
  41. void getBitmap(Bitmap bitmap);
  42. }

2.LruCache + sd的缓存方式

Android 3.1版本起,官方还提供了LruCache来进行cache处理,当存储Image的大小大于LruCache 设定的值,那么近期使用次数最少的图片就会被回收掉,系统会自动释放内存!

使用示例

步骤:

1)要先设置缓存图片的内存大小,我这里设置为手机内存的1/8,手机内存的获取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);

2)LruCache里面的键值对分别是URL和对应的图片

3)重写了一个叫做sizeOf的方法,返回的是图片数量。

  1. private LruCache<String, Bitmap> mMemoryCache;
  2. private LruCacheUtils() {
  3. if (mMemoryCache == null)
  4. mMemoryCache = new LruCache<String, Bitmap>(
  5. MAXMEMONRY / 8) {
  6. @Override
  7. protected int sizeOf(String key, Bitmap bitmap) {
  8. // 重写此方法来衡量每张图片的大小,默认返回图片数量。
  9. return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
  10. }
  11. @Override
  12. protected void entryRemoved(boolean evicted, String key,
  13. Bitmap oldValue, Bitmap newValue) {
  14. Log.v("tag", "hard cache is full , push to soft cache");
  15. }
  16. };
  17. }

4)下面的方法分别是清空缓存、添加图片到缓存、从缓存中取得图片、从缓存中移除。

移除和清除缓存是必须要做的事,因为图片缓存处理不当就会报内存溢出,所以一定要引起注意。

  1. public void clearCache() {
  2. if (mMemoryCache != null) {
  3. if (mMemoryCache.size() > 0) {
  4. Log.d("CacheUtils",
  5. "mMemoryCache.size() " + mMemoryCache.size());
  6. mMemoryCache.evictAll();
  7. Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
  8. }
  9. mMemoryCache = null;
  10. }
  11. }
  12. public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
  13. if (mMemoryCache.get(key) == null) {
  14. if (key != null && bitmap != null)
  15. mMemoryCache.put(key, bitmap);
  16. } else
  17. Log.w(TAG, "the res is aready exits");
  18. }
  19. public synchronized Bitmap getBitmapFromMemCache(String key) {
  20. Bitmap bm = mMemoryCache.get(key);
  21. if (key != null) {
  22. return bm;
  23. }
  24. return null;
  25. }
  26. /**
  27. * 移除缓存
  28. *
  29. * @param key
  30. */
  31. public synchronized void removeImageCache(String key) {
  32. if (key != null) {
  33. if (mMemoryCache != null) {
  34. Bitmap bm = mMemoryCache.remove(key);
  35. if (bm != null)
  36. bm.recycle();
  37. }
  38. }
  39. }

以上就是Bitmap引起的OOM问题怎么解决的详细内容,更多关于Bitmap引起的OOM问题怎么解决的资料请关注九品源码其它相关文章!