Android 加载图片时出错,内存溢出

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的实例加载到了我们正在运行的ActivityContext)当中,因此,通过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并没有销毁,因此引用它的ActivityMytest)也有没有被销毁,因此也带来的内存泄露问题。

有些人喜欢用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);

              }

       }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值