解决加载图片OOM 方法汇总

加载图片出现OOM的情况一般有两个,
    一、图片过大
    二、图片过多

首先来看下第一种情况:
    简单的一句话就能造成
         Bitmap bitmap = BitmapFactory.decodeFile(path);//图片一大就直接OOM
    
    解决方法:
        预加载,获取屏幕大小, 图片大小,计算缩放比例,然后缩放原图后加载
    
    一、获取屏幕大小:
        1,通过DisplayMetrics 
            DisplayMetrics metric=getResources().getDisplayMetrics(); 或  DisplayMetrics metric = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(metric);
            int width = metric.widthPixels;  // 屏幕宽度(像素)
            int height = metric.heightPixels;  // 屏幕高度(像素)
            float density = metric.density;  // 屏幕密度(0.75 / 1.0 / 1.5)
            int densityDpi = metric.densityDpi;  // 屏幕密度DPI(120 / 160 / 240)
        2 , 通过WindowManager
            Display display=getWindowManager().getDefaultDisplay();
            int screenWidth  =display.getWidth();       // 屏幕宽(像素,如:480px)  
            int screenHeight =display.getHeight();      // 屏幕高(像素,如:800p)  

        3, 上面两种在小屏时会有误差
            // 获取屏幕密度(方法3)  
            dm = new DisplayMetrics();  
            getWindowManager().getDefaultDisplay().getMetrics(dm);  
            screenWidth  = (int)(dm.widthPixels * density + 0.5f);      // 屏幕宽(px,如:480px)  
            screenHeight = (int)(dm.heightPixels * density + 0.5f);     // 屏幕高(px,如:800px)  
    二、 预加载获取图片大小
             BitmapFactory.Options options =  new  BitmapFactory.Options();   //new 一个bitmap的属性
             options.inJustDecodeBounds =  true ;   //描 边  为true则 解析方法不止为bitmap分配内存,解析后的bitmap为null但是options的属性已经赋值
       BitmapFactory.decodeResource(getResources(), R.id.image, options);  
       //BitmapFactory.decodeFile(path,options);  这下面5种是根据输入不同来初始化的方法
       //BitmapFactory.decodeByteArray(byteArray, offset, length,options); 
       //BitmapFactory.decodeFileDescriptor(filedescriptor, outPadding, options);
       //BitmapFactory.decodeStream(inputstream, outPadding, options)
       int imageHeight = options.outHeight;   
       int imageWidth = options.outWidth;   
       String imageType = options.outMimeType; 

    三、计算缩放比例   这个网上有几种好的算法, 基本上就是把屏幕高宽与图片大小相除 
        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) {   
               // 计算出实际宽高和目标宽高的比率   
               final int heightRatio = Math.round((float) height / (float) reqHeight);   
               final int widthRatio = Math.round((float) width / (float) reqWidth);   
               // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高   
               // 一定都会大于等于目标的宽和高。   
               inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;   
           }   
           return inSampleSize;   
        
    四、 缩放原图 
               // 重新new一个option 再次预加载
         final  BitmapFactory.Options options =  new  BitmapFactory.Options();   
        options.inJustDecodeBounds =  true ;   
        BitmapFactory.decodeResource(path, options);   
         // 设置 缩放比例inSampleSize值到options内    
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);   
         // 取消预加载
        options.inJustDecodeBounds =  false ;  
        // 最后  加载图片!!!! 
        Bitmap b= BitmapFactory.decodeResource(path, options);  

第二种情况: 图片过多
       最通用的方法就是建立图片缓存
       这里自己没去测试 ,召唤传送门吧

private  LruCache<string, bitmap > mMemoryCache;
 
@Override
protected  void  onCreate(Bundle savedInstanceState) {
     // 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
     // LruCache通过构造函数传入缓存值,以KB为单位。
     int  maxMemory = ( int ) (Runtime.getRuntime().maxMemory() /  1024 );
     // 使用最大可用内存值的1/8作为缓存的大小。
     int  cacheSize = maxMemory /  8 ;
     mMemoryCache =  new  LruCache<string, bitmap= "" >(cacheSize) {
         @Override
         protected  int  sizeOf(String key, Bitmap bitmap) {
             // 重写此方法来衡量每张图片的大小,默认返回图片数量。
             return  bitmap.getByteCount() /  1024 ;
         }
     };
}
 
public  void  addBitmapToMemoryCache(String key, Bitmap bitmap) {
     if  (getBitmapFromMemCache(key) ==  null ) {
         mMemoryCache.put(key, bitmap);
     }
}
 
public  Bitmap getBitmapFromMemCache(String key) {
     return  mMemoryCache.get(key);
}
}


下次继续深入—— 看到了 bavariama大神的博客  

Android 图片 OOM问题总结

XML布局中加载的图片是不用GC的,

他们是和View/Activity 绑定在一起,同生同死。

UI上加载的可以使用SoftReference让虚拟机进行快速回收。
如果迫切需要手动回收,严格控制内存开销,也许就该针对场景写Cache的管理了。

对于一般的场景,使用栈的结构就足够了,对于期望高效流畅又有着复杂item的ListView的话
可能需要预加载和手动回收。

--------------------------------------------------------------------------------------------------------

1.对图片进行预处理
主要是根据实际大小对对质量进行压缩

2.参考下android developer中的demo,里面有两点值得借鉴:一个是内存缓存,一个是disk缓存。可以很好的帮助你处理oom.

 

楼上所说的是LruCache,你好好研究下这个官方DEMO,研究明白了,你以后所有的项目都可以使用这个DEMO中的方式,绝对不会出现OOM。另外,在Gallery3D的源代码中,好像有另外一种cache方式。

 

--------------------------------------------------------------------------------------------------------

https://github.com/dodola/android_waterfall/tree/master/src/net   可以加载上万张图片的瀑布流



还有一个有关OOM 出险原因的  应该不是简单地图片过大
分类: android开发基础 2014-06-06 12:40   660人阅读   评论(1)   收藏   举报

ImageView加载图片时,有时会出现OOM
 

imageView.setImageResource(imageId);

解决方法
 

/**
* 以最省内存的方式读取本地资源的图片

* @param context
* @param resId
* @return
*/
public static Bitmap readBitMap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
// 获取资源图片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is, null, opt);
}
 

Bitmap bitmap=readBitMap(LoginActivity.this,imageId);
imageView.setImageBitmap(bitmap);

那是为什么,会导致oom呢:
  原来当使用像 imageView.setBackgroundResource,imageView.setImageResource, 或者 BitmapFactory.decodeResource 这样的方法来设置一张大图片的时候,这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。
  因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。如果在读取时加上图片的Config参数,可以跟有效减少加载的内存,从而跟有效阻止抛out of Memory异常。
  另外,需要特别注意:
  decodeStream是直接读取图片资料的字节码了, 不会根据机器的各种分辨率来自动适应,使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相应的图片资源,否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了。



Android异步加载图片,解决图片过大OOM问题

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.widget.ImageView;

/**
 * 
 * 功能说明:异步加载图片
 * 
 * @author Jack.wang
 * 
 */
public class AsyncImageLoaderCore
{

  public Context context; // 做本地缓存时会用到
    public HashMap<String, SoftReference<Bitmap>> imageCache;// 软引用集合

    public AsyncImageLoaderCore(Context context)
    {
        this.context = context;
        this.imageCache = new HashMap<String, SoftReference<Bitmap>>();
    }

    public Bitmap loadBitmap(final String imageUrl, final ImageView imageView, final ImageCallback imageCallback)
    {
        if (imageCache.containsKey(imageUrl))
        {
            SoftReference<Bitmap> softReference = imageCache.get(imageUrl);
            if (softReference.get() != null)
                return softReference.get();
        }

        final Handler handler = new Handler(new Callback()
        {

            @Override
            public boolean handleMessage(Message msg)
            {
                imageCallback.imageLoaded((Bitmap) msg.obj, imageView, imageUrl);
                return false;
            }
        });

        new Thread()
        {
            @Override
            public void run()
            {
                Bitmap bitmap = null;
                try
                {
                    bitmap = getHttpBitmap(imageUrl);
                } 
                catch (Exception e)
                {
                    e.printStackTrace();
                	return;
                }
                
                if (null != bitmap)
                {
                    imageCache.put(imageUrl, new SoftReference<Bitmap>(bitmap));
                    handler.sendMessage(handler.obtainMessage(0, bitmap));
                }
            }
        }.start();

        return null;
    }
    
    private final int MAX_PIC_LENGTH = 200000;// 最大字节长度限制[可调,最好不要超过200000]
    private final int SAMPLE_SIZE = 14;// 裁剪图片比列(1/14)[可调]
    
    /**
     * 获取网络图片
     */
    private Bitmap getHttpBitmap(String imgUrl) throws Exception
    {
        URL htmlUrl = new URL(imgUrl);
        URLConnection connection = htmlUrl.openConnection();
        HttpURLConnection conn = (HttpURLConnection) connection;
        
        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK)
        {
        	InputStream inputStream = conn.getInputStream();
        	
        	byte[] bytes = changeToBytes(inputStream);
        	
        	if(bytes.length < MAX_PIC_LENGTH)
    		{
        		return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
    		}
        	else if(bytes.length < MAX_PIC_LENGTH * SAMPLE_SIZE)
        	{
        		BitmapFactory.Options options = new BitmapFactory.Options();
        		options.inJustDecodeBounds = false;
        		options.inSampleSize = SAMPLE_SIZE;
        		
        		return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        	}
        }
        
        return null;
    }

    /**
     * 将流转换成字节数组
     */
  public byte[] changeToBytes(InputStream inputStream) throws Exception
  {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];// 每次读取的字节长度
    int len = 0;
    while((len = inputStream.read(buffer)) != -1)
    {
      outputStream.write(buffer, 0, len);
    }
    inputStream.close();
    return outputStream.toByteArray();
  }

  /**
   * 异步加载资源回调接口
   */
  public interface ImageCallback
    {
        public void imageLoaded(Bitmap bitmap, ImageView imageView, String imageUrl);
    }

}









Android 获取屏幕尺寸与密度

http://www.cnblogs.com/renyuan/archive/2012/07/25/2607936.html




Android加载大图片OOM异常解决




Android 加载大图片时报OOM的解决方案(源码)

http://www.linuxidc.com/Linux/2011-12/50202.htm

Android高效加载大图、多图解决方案,有效避免程序OOM

http://blog.csdn.net/luohai859/article/details/38660257

Android 图片加载图片_OOM异常解决







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值