原文:http://www.codeceo.com/article/android-load-image-oom.html

一、分析

在加载图片过程中出现的OOM的几种情况:

1、  加载的图片过大

2、  一次加载的图片过多

3、  以上两种情况兼有

出现OMM的主要原因有两点:

1、移动设备会限制每个app所能够使用的内存,最小为16M,有的设备分配的会更多,如24、32M、64M等等不一,总之会有限制,不会让你无限制的使用。

2、在andorid中图片加载到内存中是以位图的方式存储的,在android2.3之后默认情况下使用ARGB_8888,这种方式下每个像素要使用4各字节来存储。所以加载图片是会占用大量的内存。

二、解决大图加载问题

首先先来解决大图加载的问题,一般在实际应用中展示图片时,因屏幕尺寸及布局显示的原因,我们没有必要加载原始大图,只需要按照比例采样缩放即可。这样即节省内存又能保证图片不失真,具体实施步骤如下:

1、在不加载图片内容的基础上,去解码图片得到图片的尺寸信息

这里需要用的BitmapFactory的decode系列方法和BitmapFactory.Options。当使用decode系列方法加载图片时,一定要将Options的inJustDecodeBounds属性设置为true。

    BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(path, options);

2、根据获取的图片的尺寸和要展示在界面的尺寸计算缩放比例。

public int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1; if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
}
return inSampleSize;
}

3、根据计算的比例缩放图片。

//计算图片的缩放比例
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
Bitmap bitmap= BitmapFactory.decodeFile(path, options);

根据缩放比例,会比原始大图节省很多内存,效果图如下:

三、批量加载大图

下面我们看看如何批量加载大图,首先第一步还是我们上面所讲到的,要根据界面展示图片控件的大小来确定图片的缩放比例。在此我们使用gridview加载本地图片为例,具体步骤如下:

1、通过系统提供的contentprovider加载外部存储器中的所有图片地址

     private void loadPhotoPaths(){
Cursor cursor= getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
while(cursor.moveToNext()){
String path = cursor.getString(cursor.getColumnIndex(MediaColumns.DATA));
paths.add(path);
}
cursor.close();
}

2、自定义adapter,在adapter的getview方法中加载图片

    @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder=null;
if(convertView==null){
convertView = LayoutInflater.from(this.mContext).inflate(R.layout.grid_item_layout, null);
holder = new ViewHolder();
holder.photo=(ImageView)convertView.findViewById(R.id.photo);
convertView.setTag(holder);
}else{
holder=(ViewHolder)convertView.getTag();
} final String path = this.paths.get(position);
holder.photo.setImageBitmap(imageLoader.getBitmapFromCache(path));
return convertView;
}

通过以上关键两个步骤后,我们发现程序运行后,用户体验特别差,半天没有反应,很明显这是因为我们在主线程中加载大量的图片,这是不合适的。在这里我们要将图片的加载工作放到子线程中进行,改造自定义的ImageLoader工具类,为其添加一个线程池对象,用来管理用于下载图片的子线程。

    private ExecutorService executor;
private ImageLoader(Context mContxt) {
super();
executor = Executors.newFixedThreadPool(3);
}
//加载图片的异步方法,含有回调监听
public void loadImage(final ImageView view,
final String path,
final int reqWidth,
final int reqHeight,
final onBitmapLoadedListener callback){ final Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
Bitmap bitmap = (Bitmap)msg.obj;
callback.displayImage(view, bitmap);
break; default:
break;
}
} }; executor.execute(new Runnable() {
@Override
public void run() {
Bitmap bitmap = loadBitmapInBackground(path, reqWidth,
reqHeight);
putBitmapInMemey(path, bitmap); Message msg = mHandler.obtainMessage(1);
msg.obj = bitmap;
mHandler.sendMessage(msg);
}
});
}

通过改造后用户体验明显好多了,效果图如下:

虽然效果有所提升,但是在加载过程中还存在两个比较严重的问题:

1、图片错位显示

2、当我们滑动速度过快的时候,图片加载速度过慢

经过分析原因不难找出,主要是因为我们时候holder缓存了grid的item进行重用和线程池中的加载任务过多所造成的,只需要对程序稍作修改,具体如下:

Adapter中:

        holder.photo.setImageResource(R.drawable.ic_launcher);
holder.photo.setTag(path);
imageLoader.loadImage(holder.photo,
path,
DensityUtil.dip2px(80),
DensityUtil.dip2px(80),
new onBitmapLoadedListener() {
@Override
public void displayImage(ImageView view, Bitmap bitmap) {
String imagePath= view.getTag().toString();
if(imagePath.equals(path)){
view.setImageBitmap(bitmap);
}
}
});

ImageLoader中:

executor.execute(new Runnable() {
@Override
public void run() {
String key = view.getTag().toString();
if (key.equals(path)) {
Bitmap bitmap = loadBitmapInBackground(path, reqWidth,
reqHeight);
putBitmapInMemey(path, bitmap); Message msg = mHandler.obtainMessage(1);
msg.obj = bitmap;
mHandler.sendMessage(msg);
}
}
});

为了获得更好的用户体验,我们还可以继续优化,即对图片进行缓存,缓存我们可以分为两个部分内存缓存磁盘缓存,本文例子加载的是本地图片所有只进行了内存缓存。对ImageLoader对象继续修改,添加LruCache对象用于缓存图片。

private ImageLoader(Context mContxt) {
super();
executor = Executors.newFixedThreadPool(3);
//将应用的八分之一作为图片缓存
ActivityManager am=(ActivityManager)mContxt.getSystemService(Context.ACTIVITY_SERVICE);
int maxSize = am.getMemoryClass()*1024*1024/8;
mCache = new LruCache<String, Bitmap>(maxSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes()*value.getHeight();
}
};
} //存图片到缓存
public void putBitmapInMemey(String path,Bitmap bitmap){
if(path==null)
return;
if(bitmap==null)
return;
if(getBitmapFromCache(path)==null){
this.mCache.put(path, bitmap);
}
} public Bitmap getBitmapFromCache(String path){
return mCache.get(path);
}

在loadImage方法中异步加载图片前先从内存中取,具体代码请下载案例

四、总结

总结一下解决加载图片出现OOM的问题主要有以下方法:

1、  不要加载原始大图,根据显示控件进行比例缩放后加载其缩略图。

2、  不要在主线程中加载图片,主要在listview和gridview中使用异步加载图片是要注意处理图片错位和无用线程的问题。

3、  使用缓存,根据实际情况确定是否使用双缓存和缓存大小。

图片过大导致OOM的更多相关文章

  1. Mysql中使用JDBC流式查询避免数据量过大导致OOM

    一.前言 java 中MySQL JDBC 封装了流式查询操作,通过设置几个参数,就可以避免一次返回数据过大导致 OOM. 二.如何使用 2.1 之前查询 public void selectData ...

  2. 【tf.keras】在 cifar 上训练 AlexNet,数据集过大导致 OOM

    cifar-10 每张图片的大小为 32×32,而 AlexNet 要求图片的输入是 224×224(也有说 227×227 的,这是 224×224 的图片进行大小为 2 的 zero paddin ...

  3. React Image加载图片过大导致ListView滑动卡顿

    今天莫名的发现ListView加载Item很卡,一顿一顿的... ListView Item 中只加载一张图片,小编从百度爸爸上随便复制的链接,这张图片很大,以致埋下如此大坑... Image的Sty ...

  4. Android LruCache 压缩图片 有效避免程序OOM

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9316683 本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工, ...

  5. Android 导致OOM的常见原因

    OOM主要有两种原因导致: 1. 加载大图片: 2. 内存泄漏: 一.加载大图片 在Android应用中加载Bitmap的操作是需要特别小心处理的,因为Bitmap会消耗很多内存.比如,Galaxy ...

  6. iOS 图片加载导致内存警告

    虽然UITableView和UICollectionView都有cell复用机制,但是如果利用SDWebImage加载的图片本身过大且cell复用池中的个数比较多(cell的Size越小,复用池中的c ...

  7. <转载>CSS解决图片过大撑破DIV的方法

    DIV+CSS网页内容中如果插入大于DIV层宽度显示,过大的图片将会撑破网页宽度显示从而网页严重变形,您是否遇到过?这里DIVCSS5给大家介绍几种解决图片撑破撑开网页DIV层方法. 图片撑破宽度解决 ...

  8. Django中上传图片---避免因图片重名导致被覆盖

    上一篇文章中(https://www.cnblogs.com/lutt/p/10640412.html),我们以图片文件夹+图片名字的方式来储存图片,这样的做法会导致有重名的图片会导致之前的图片被覆盖 ...

  9. Android RecyclerView利用Glide加载大量图片into(Target)导致OOM异常

    学过android的人应该都知道Glide是一个无比强大的图片加载库,它内部已经提供了很好的缓存机制供我们选择,我们只需一个参数调用即可(DiskCacheStrategy()),而不必像Univer ...

随机推荐

  1. Java 基础知识 练习

    1.在DOS命令下输入:java Hello出现以下结果:Bad command or the file name可能是什么原因? (错误的命令或文件名) 输入的命令不存在,或者不在指定的路径中 2. ...

  2. js中const,var,let区别

    今天第一次遇到const定义的变量,查阅了相关资料整理了这篇文章.主要内容是:js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始化. co ...

  3. Python3基础 把一个列表中内容给另外一个列表,形成两个独立的列表

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  4. 3个常用基于Linux系统命令行WEB网站浏览工具(w3m/Links/Lynx)

    一般我们常用的浏览器肯定是基于可视化界面的图文结合的浏览界面效果,比如FireFox.Chrome.Opera等等,但是有些时候折腾和项目 的需要,在Linux环境中需要查看某个页面的文字字符,我们需 ...

  5. python ConfigParser配置读写

    一.ConfigParser简介 ConfigParser 是用来读取配置文件的包.配置文件的格式如下:中括号"[ ]"内包含的为section.section 下面为类似于key ...

  6. 《BI项目笔记》历年感官评吸质量均值变化分析Cube的建立

    分析主题主要维度:烟叶级别.烟叶级别按等级信息.烟叶级别按分级标准(标准维度)产地(父子维度)检测时间(时间维度,以Tqc_Raw_SmokingTest .CheckTime字段派生CheckDat ...

  7. 关于php语言的使用! ------php语言与JavaScript的使用 方法是相似

    <script type="text/javascript"> </script>--js与PHP同是一种弱类型语言 弱类型语言只是不显示表现  定义变量时 ...

  8. Postman 安装及使用入门教程

    安装 本文只是基于 Chrome 浏览器的扩展插件来进行的安装,并非单独应用程序. 首先,你要台电脑,其次,安装有 Chrome 浏览器,那你接着往下看吧. 1. 官网安装(别看) 打开官网,http ...

  9. Django1.9开发博客(11)- 富文本与代码高亮

    TinyMCE是一个轻量级的基于浏览器的所见即所得编辑器,支持目前流行的各种浏览器,由JavaScript写成. 功能配置灵活简单(两行代码就可以将编辑器嵌入网页中),支持AJAX.另一特点是加载速度 ...

  10. windows下面安装casperjs

    因为需要 就学习了一下casperjs,CasperJS是一个开源的导航脚本处理和测试工具,基于PhantomJS(前端自动化测试工具)编写.由于casperjs对PhantomJS的依赖性,所以需要 ...