Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证):
方案一、读取图片时注意方法的调用,适当压缩 尽量不要使用setImageBitmap或setImageResource或 BitmapFactory.decodeResource来设置一张大图,
因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。
因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。
InputStream is = this.getResources().openRawResource(R.drawable.pic1);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = 10; // width,hight设为原来的十分一
Bitmap btp = BitmapFactory.decodeStream(is, null, options);
如果在读取时加上图片的Config参数,可以跟有效减少加载的内存,从而跟有效阻止抛out of Memory异常。
/**
* 以最省内存的方式读取本地资源的图片
*/
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);
}
另外,decodeStream直接拿图片来读取字节码, 不会根据机器的各种分辨率来自动适应,使用了decodeStream之后,需要在hdpi和mdpi,ldpi中配置相应的图片资源, 否则在不同分辨率机器上都是同样大小(像素点数量),显示出来的大小就不对了。
方案二、在适当的时候 及时回收图片占用的内存 通常Activity或者Fragment在onStop/onDestroy时候就可以释放图片资源:
if(imageView != null && imageView.getDrawable() != null){
Bitmap oldBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
imageView.setImageDrawable(null);
if(oldBitmap != null){
oldBitmap.recycle();
oldBitmap = null;
}
}
// Other code.
System.gc();
在释放资源时,需要注意释放的Bitmap或者相关的Drawable是否有被其它类引用。
如果正常的调用,可以通过Bitmap.isRecycled()方法来判断是否有被标记回收;
而如果是被UI线程的界面相关代码使用,就需要特别小心避免回收有可能被使用的资源,不然有可能抛出系统异常: E/AndroidRuntime: java.lang.IllegalArgumentException: Cannot draw recycled bitmaps 并且该异常无法有效捕捉并处理。
方案三、不必要的时候避免图片的完整加载
只需要知道图片大小的情形下,可以不完整加载图片到内存。 在使用BitmapFactory压缩图片的时候,BitmapFactory.Options设置inJustDecodeBounds为true后,再使用decodeFile()等方法,可以在不分配空间状态下计算出图片的大小。
示例:
BitmapFactory.Options opts = new BitmapFactory.Options();
// 设置inJustDecodeBounds为false
opts.inJustDecodeBounds = false;
// 使用decodeFile方法得到图片的宽和高
BitmapFactory.decodeFile(path, opts);
// 打印出图片的宽和高
Log.d("example", opts.outWidth + "," + opts.outHeight);
(ps:原理其实就是通过图片的头部信息读取图片的基本信息)
方案四、优化Dalvik虚拟机的堆内存分配
堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。
比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。
倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。 Heap Utilization是堆的利用率。当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。使用 dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率。
private final static float TARGET_HEAP_UTILIZATION = 0.75f;
// 在程序onCreate时就可以调用
VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);
方案五、自定义堆(Heap)内存大小
对于一些Android项目,影响性能瓶颈的主要是Android自己内存管理机制问题,目前手机厂商对RAM都比较吝啬,对于软件的流畅性来说RAM对性能的影响十分敏感,除了优化Dalvik虚拟机的堆内存分配外,我们还可以强制定义自己软件的对内存大小,我们使用Dalvik提供的 dalvik.system.VMRuntime类来设置最小堆内存为例:
private final static int CWJ_HEAP_SIZE = 6 * 1024 * 1024 ;
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); // 设置最小heap内存为6MB大小。
但是上面方法还是存在问题,函数setMinimumHeapSize其实只是改变了堆的下限值,它可以防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值(Max Heap Size)时仍然采用堆的上限值,对于内存不足没什么作用。
最后介绍一下图片占用进程的内存算法。android中处理图片的基础类是Bitmap,顾名思义,就是位图。占用内存的算法如:图片的width*height*Config。 如果Config设置为ARGB_8888,那么上面的Config就是4。一张480*320的图片占用的内存就是480*320*4 byte。 在默认情况下android进程的内存占用量为16M,因为Bitmap他除了java中持有数据外,底层C++的 skia图形库还会持有一个SKBitmap对象,因此一般图片占用内存推荐大小应该不超过8M。这个可以调整,编译源代码时可以设置参数。
图片--Android加载图片导致内存溢出(Out of Memory异常)的更多相关文章
-
Android加载大图到内存如何避免内存溢出?
加载大图怎么避免溢出实际做法就是对图像进行压缩,也是比较老的话题了,在最初做android时是经常会遇到的问题,而如今对于图片加载这一块都已经有很成熟稳定的三方库来弄它了,所以图片加载过大内存溢出的比 ...
-
Android加载图片导致内存溢出(Out of Memory异常)
Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证) ...
-
android 加载图片防止内存溢出
图片资源: private int fore[]; private int back[]; fore = new int[]{R.drawable.a0, R.drawable.a1, R.drawa ...
-
Android加载图片小结
应用中用到图片加载需要解决的问题 无网络环境下图片不可用 图片的本地缓存,或者默认预加载的图片 低配置机型,加载图像资源超内存(OutOfMemory, OoM) 需要合理使用内存,尤其是bitmap ...
-
android 加载图片oom若干方案小结
本文根据网上提供的一些技术方案加上自己实际开发中遇到的情况小结. 众所周知,每个Android应用程序在运行时都有一定的内存限制,限制大小一般为16MB或24MB(视手机而定).一般我们可以通过获取当 ...
-
Android加载图片OOM错误解决方式
前几天做项目的时候,甲方要求是PAD (SAMSUNG P600 10.1寸 2560*1600)的PAD上显示高分辨率的大图片. SQLITE採用BOLD方式存储图片,这个存取过程就不说了哈,网上一 ...
-
Android加载图片的策略
实现图片缓存也不难,需要有相应的cache策略.这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且 ...
-
android 加载图片框架--Glide使用详解
一.简介 Glide,一个被google所推荐的图片加载库,作者是bumptech.这个库被广泛运用在google的开源项目中,包括2014年的google I/O大会上发布的官方app.(PS:众所 ...
-
解决android加载图片时内存溢出问题
尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过jav ...
随机推荐
-
AlertDialog错误
Unable to add window token null is not for an application AlertDialog.Builder localBuilder = new Ale ...
-
【SGU 104】Little shop of flowers
题意 每个花按序号顺序放到窗口,不同窗口可有不同观赏值,所有花都要放上去,求最大观赏值和花的位置. 分析 dp,dp[i][j]表示前i朵花最后一朵在j位置的最大总观赏值. dp[i][j]=max( ...
-
【测试】自行建表并演示append+nologging,并描述数据写入后产生的效果
①创建表: SQL> create table t4 as select * from all_objects; Table created. ②设置t4处于nologging: SQL> ...
-
漫游Kafka设计篇之Producer和Consumer
Kafka Producer 消息发送 producer直接将数据发送到broker的leader(主节点),不需要在多个节点进行分发.为了帮助producer做到这点,所有的Kafka节点都可以及时 ...
-
shell执行php文件传递参数
php -f index.php hello test 2314 shell命令执行php文件不像http那样通过GET方式传参 同样php文件获取的时候也不能用$_GET方法了 而是通过$argv[ ...
-
postgresql 抽样查询
curl -GET 'http://****/query' --data-urlencode "db=db" --data-urlencode "q=SELECT las ...
-
No curses/termcap library found
CentOS6.5中编译Mysql时遇见如下错误 error: No curses/termcap library found checking for tgetent in -lncurses... ...
-
CMDB服务器管理系统【s5day88】:采集资产-文件配置(二)
上节疑问: 1.老师我们已经写到global_settings里了,为什么还要写到__init__.py setting 这的作用是为了:整合起两个的组合global_settings和setting ...
-
如何快速连接无线Wifi 使用二维码
无线Wifi现在已经很普及了,无论是在家.还是在单位.书店.餐馆.咖啡馆等公共场所,只要手机打开Wifi,选择相应的无线Wifi热点,手工输入密码后,即可通过无线Wifi访问互联网. 如何让用户更方便 ...
-
基础003_V7-Memory Resources
一.综述 参考ug473.pdf. 常用Memory 资源: 在IP核中,Block memory(distributed memory为CLB中的资源): 通常选用Native,而不用AXI接口: ...