在Android里图片溢出,报错信息为以下内容:
java.lang.outofmemoryerror
Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M ,一般是8M。在真机上,应该远远不止这么小,具体多少我也不知道,有知道的请告诉我(我看资料有说真机使用第三方ROM,内存分配有40MB ),一般在虚拟机上会报内存溢出错的,实际上在真机上是不会报错的,这个我有实际体会。然后如果在虚拟机上报错了的,在真机上没报错,那么这个问题存在但可以先放着,但必须解决。一般在三星真机上不会出这个错,但在差一点的手机比如华为的某个机器上就报错了。
我的图片远没有8M,所有的图片加起来都没有这么大,但还是报了这个错,要想办法解决。
可以通过 long freeStart = Runtime.getRuntime().freeMemory(); 来查看内存
我先查了自己的资源,看是否有占用的资源没有释放。
资源释放问题:
转载JAVA之旅的Android防止内存溢出浅析(有改动,http://www.cnblogs.com/java-android/archive/2012/06/24/2560405.html )
在Android平台上,长期保持一些资源的引用,造成一些内存不能释放,带来的内存泄露问题很多。比如:Context(下文中提到的Activity都是Context),在一些你需要保持你的首个类对象状态,并且把状态传入其他类对象中时,这样消除掉首个类对象之前,你必须先把接收类对象释放掉。需要注意一点的是:因为在Java或者Android内存机制中,顶点的结点释放前必须保证其他对象没有调用才能被系统GC回收释放。我们来看一段代码:
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextViewlabel = new TextView(this);
label.setText("Leaksare bad");
setContentView(label);
}
这个代码的意思就是我们把一个TextView的实例加载到了我们正在运行的Activity(Context)当中,因此,通过GC回收机制,我们知道,要释放Context,就必须先释放掉引用他的一些对象。如果没有,那在要释放Context的时候,你会发现会有大量的内存溢出。所以在你不小心的情况下内存溢出是一件非常容易的事情。 保存一些对象时,同时也会造成内存泄露。最简单的比如说位图(Bitmap),比如说:在屏幕旋转时,会破坏当前保持的一个Activity状态,并且重新申请生成新的Activity,直到新的Activity状态被保存。我们再看一段代码:
privatestatic Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground =getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
这个代码是非常快的同时也是错误的。它的内存泄露很容易出在屏幕转移的方向上。虽然我们会发现没有显示的保存Context这个实例,但是当我们把绘制的图连接到一个视图的时候,Drawable就会将被View设置为回调,这就说明,在上述的代码中,其实在绘制TextView到活动中的时候,我们已经引用到了这个Activity。链接情况可以表现为:Drawable->TextView->Context。
所以在想要释放Context的时候,其实还是保存在内存中,并没有得到释放。
如何避免这种情况:主要在于。线程最容易出错。大家不要小看线程,在Android里面线程最容易造成内存泄露。线程产生内存泄露的主要原因在于线程生命周期的不可控。下面有一段代码:
publicclass MyTest extends Activity {
@Override
publicvoid onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new MyThread().start();
}
privateclass MyThread extends Thread{
@Override
public void run() {
super.run();
//do somthing
}
}
}
代码很简单,但是在Android上又来新问题了,当我们在切换视图屏幕的时候(横竖屏),就会重新建立横屏或者竖屏的Activity。我们形象的认为之前建立的Activity会被回收,但是事实如何呢?Java机制不会给你同样的感受,在我们释放Activity之前,因为run函数没有结束,这样MyThread并没有销毁,因此引用它的Activity(Mytest)也有没有被销毁,因此也带来的内存泄露问题。
有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重,Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。
如果要使用到Context,尽量使用ApplicationContext去代替Context,因为ApplicationContext的生命周期较长,引用情况下不会造成内存泄露问题
然后再就是图片处理问题。
第一可以自己设置堆内存:
private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ;
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE);
堆内存一般有个上限值,一般为16M,有好的机型是24M,你设个高于此值的会按照上限值来算的。
第二可以处理图片
缩小图片大小,分辨率会下降,有可能失真
低内存编码
//以省内存的方式读取图片
public Bitmap getBitmap(InputStream is){
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
//下面两个要一起用
opt.inPurgeable = true;
opt.inInputShareable = true;
opt.inSampleSize = 4;
//获取资源图片
//InputStream is = mContext.getResources().openRawResource(resId);
return BitmapFactory.decodeStream(is,null,opt);
}
//以省内存的方式读取图片
public Bitmap getBitmap(int resId){
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
opt.inSampleSize = 4;
//获取资源图片
// InputStream is = mContext.getResources().openRawResource(resId);
return BitmapFactory.decodeResource(mContext.getResources(), resId,opt);
}
Android中有四种,分别是:
ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存
RGB_565:每个像素占用2byte内存
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但同样的,占用的内存也最大。
1 BitmapFactory.Options options = new BitmapFactory.Options();
2 options.inPreferredConfig = Bitmap.Config.ARGB_4444;
3 Bitmap img = BitmapFactory.decodeFile("/sdcard/1.png", options);
以上代码即是将1.png以ARGB_4444模式读出。内存减少虽然不如第一种方法明显,但是对于大多数图片,看不出与ARGB_8888模式有什么差别。不过在读取有渐变效果的图片时,可能有颜色条出现。另外,会影响图片的特效处理。
第三缓存进内存里,但要记得释放回收
释放
@Override
public void releaseImage(String path) {
if(mImageCache.containsKey(path)) {
SoftReference<Bitmap> reference = mImageCache.get(path);
Bitmap bitmap = reference.get();
if(null != bitmap) {
Log.d(“OomDemo”, “recyling ” + path);
bitmap.recycle();
}
mImageCache.remove(path);
}
}