Android:Gallery中的内存不足exception

我的应用程序显示了9个类别的列表,每个类别都显示了一个基于图库的封面stream(由Neil Davies 在此优雅地提供)以及所选类别的图像。
这些图像是从Web中获取的,每个图像的大小在300K到500K之间,并存储在Drawable的数组列表中。 这些数据使用BaseAdapter绑定到coverflow(代码如下)。
每次我退出coverflow并返回到类别列表,我清除arrayList(再次,代码如下)。
在场景1中,我的arrayList包含5个Drawable。 在这种情况下,我可以自由浏览所有的类别,并显示他们的图像。 在我的testing中,我循环了所有的类别5次,这似乎足以确定没有问题。
在场景2中,我的数组列表包含10个可绘图。 在这种情况下,当通过第5或第6关卡中的图像时,出现OutOfMemoryErrorexception:

 07-13 08:38:21.266:ERROR / dalvikvm-heap(2133):819840字节的外部分配过大。
 07-13 08:38:21.266:错误/(2133):虚拟机不会让我们分配819840字节
 07-13 08:38:21.277:DEBUG / skia(2133):--- decoder-> decode返回false
 07-13 08:38:21.287:WARN / dalvikvm(2133):threadid = 25:线程退出时未捕获的exception(group = 0x4001b188)
 07-13 08:38:21.296:错误/ AndroidRuntime(2133):未捕获的处理程序:线程由于未捕获的exception而退出线程64
 07-13 08:38:21.308:错误/ AndroidRuntime(2133):java.lang.OutOfMemoryError:位图大小超过VM预算
 07-13 08:38:21.308:错误/ AndroidRuntime(2133):在android.graphics.BitmapFactory.nativeDecodeStream(本地方法)
 07-13 08:38:21.308:错误/ AndroidRuntime(2133):在android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
 07-13 08:38:21.308:错误/ AndroidRuntime(2133):在android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
 07-13 08:38:21.308:错误/ AndroidRuntime(2133):在android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
 07-13 08:38:21.308:错误/ AndroidRuntime(2133):在android.graphics.drawable.Drawable.createFromStream(Drawable.java:657)

这对我来说没有意义。 如果我正在泄漏记忆,那么在场景1中的某个时刻,我预计会崩溃,但是我经历了所有类别,而且没有崩溃。 我还使用了Eclipse的Memory Analyzer插件,它没有提供任何潜在的元凶。
如果系统无法处理10张图像,就像情景2中的那样,我预计会在第一类中崩溃,但是我只会在5或6类之后崩溃。
一些代码:

coverflow的适配器function:

public int getCount() { return DataManager.getInstance().getImageBufferInstance().getImageArraySize(); } public Object getItem(int position) { return DataManager.getInstance().getImagesBuffer().get(position); } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ImageView i; if (convertView == null) i = new ImageView(mContext); else i = (ImageView)convertView; Drawable bufferedImage = (Drawable)getItem(position); Log.v("getView", "position: " + position); i.setImageDrawable(bufferedImage); i.setLayoutParams(new CoverFlow.LayoutParams(Utils.getInstance().getScreenWidth() / 2, Utils.getInstance().getScreenHeight() / 2)); i.setScaleType(ImageView.ScaleType.CENTER_INSIDE); try{ //Make sure we set anti-aliasing otherwise we get jaggies BitmapDrawable drawable = (BitmapDrawable) i.getDrawable(); drawable.setAntiAlias(true); } catch (Exception e) { Log.v("getView", "Exception: " + e.toString()); } return i; } 

在进入类别时填充数据源:

 for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++) { String imageUrl = ImageBuffer.getInstance().getImageUrl(i); Log.v("Initial", imageUrl); Drawable fullImage = AsyncImageLoader.getInstance().loadImageByUrl(imageUrl); ImageBuffer.getInstance().getImages().add(i, fullImage); } 

退出类别时(在finish()中)清除数据源:

 for (int i = 0; i < ImageBuffer.getInstance().getImageArraySize(); i++) { if (ImageBuffer.getInstance().images.get(i) != null) { ImageBuffer.getInstance().images.get(i).setCallback(null); ImageBuffer.getInstance().images.set(i, null); } } 

编辑:

好的,我在我的coverflow上应用了Mathias的LogHeap函数,这里有一些输出。 加载第一个画廊之前:

debugging/应用程序(5221):debugging。  =================================
debugging/应用程序(5221):debug.heap本地:在[com.example.Coverflow]中分配6.20MB的6.28MB(0.07MB空闲)
debugging/应用程序(5221):debug.memory:分配:4.00MB的24.00MB(0.00MB免费)
 DEBUG / dalvikvm(5221):GC在84ms内释放了4558个对象/ 638152个字节
 DEBUG / dalvikvm(5221):GC在67ms内释放了17个对象/ 808个字节

进入第一个画廊之后:

debugging/应用程序(5221):debugging。  =================================
debugging/应用程序(5221):debug.heap native:在[com.example.Coverflow]中分配了14.90MB的16.89MB(0.07MB空闲)
debugging/应用程序(5221):debug.memory:分配:4.00MB的24.00MB(1.00MB免费)
 DEBUG / dalvikvm(5221):GC在68ms内释放了357个对象/ 50080个字节
 DEBUG / dalvikvm(5221):GC在67ms内释放了353个对象/ 27312个字节

现有的第一个画廊之后:

debugging/应用程序(5221):debugging。  =================================
debugging/应用程序(5221):debug.heap native:在[com.example.Coverflow]中分配了14.83MB的16.89MB(0.11MB免费)
debugging/应用程序(5221):debug.memory:分配:4.00MB的24.00MB(1.00MB免费)
 DEBUG / dalvikvm(5221):GC在77ms内释放了330个对象/ 17920个字节
 DEBUG / dalvikvm(5221):GC在67ms内释放13个对象/ 760个字节

进入第五画廊后:

debugging/应用程序(5221):debugging。  =================================
debugging/应用程序(5221):debug.heap本地:在[com.example.Coverflow]中分配了16.80MB的23.32MB(0.08MB空闲)
debugging/应用程序(5221):debug.memory:分配:4.00MB的24.00MB(1.00MB免费)
 DEBUG / dalvikvm(5221):GC在73ms内释放了842个对象/ 99256个字节
 DEBUG / dalvikvm(5221):GC在69ms内释放了306个对象/ 24896个字节

退出第五画廊后:

debugging/应用程序(5221):debugging。  =================================
debugging/应用程序(5221):debug.heap native:在[com.example.Coverlow]中分配了23.32MB(0.11MB空闲)的16.74MB
debugging/应用程序(5221):debug.memory:分配:4.00MB的24.00MB(1.00MB免费)
 DEBUG / dalvikvm(5221):GC在68ms内释放331个对象/ 18184个字节
 DEBUG / dalvikvm(5221):GC在68ms内释放了60个对象/ 3128个字节

进入一个画廊似乎分配的内存越来越多,但退出之后很less被释放。 我没有正确清理我的drawables吗? 对于drawList的arrayList中的每个元素,我调用setCallBack(null)并将元素设置为null。 这是不够的?
绝望的任何见解。
谢谢

这些图像是从Web中获取的,每个图像的大小在300K到500K之间,并存储在Drawable的数组列表中。

您从networking加载的图像的kb文件大小不直接相关。 由于它们转换为位图,因此对于普通的ARGB图像,需要计算每个图像的宽*高* 4字节。 (px中的宽度和高度)。

位图消耗本地堆,通常不会显示在hprof中。 hprof应该只显示对象的数量,即BitmapDrawables或Bitmap。

我在我的应用程序中使用此代码来输出应用程序和本地堆使用的当前使用的内存:

 public static void logHeap(Class clazz) { Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576)); Double available = new Double(Debug.getNativeHeapSize())/1048576.0); Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0); DecimalFormat df = new DecimalFormat(); df.setMaximumFractionDigits(2); df.setMinimumFractionDigits(2); Log.d(APP, "debug. ================================="); Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]"); Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)"); System.gc(); System.gc(); // don't need to add the following lines, it's just an app specific handling in my app if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) { android.os.Process.killProcess(android.os.Process.myPid()); } } 

我在开发过程中开始或完成一个活动时会调用它。

 logHeap(this.getClass()); 

以下是一些有用的链接 – 这里通常有很多关于这个主题的话题。

  • Android中的位图
  • Android:Eclipse MAT似乎不显示我所有的应用程序的对象

下面是Romain Guy(Android Framework工程师)关于软引用,弱引用,简单caching和image processing的有用幻灯片: http : //docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

以下是一些build议:

  1. 你使用inSampleSize选项吗? 如果缩放图像,它会减less内存消耗。 在将图像加载到Bitmap对象时出现内存不足的问题

  2. 当你不需要图像时,你应该调用Bitmap.recycle()。 我认为这对你来说很重要。 Android:OutofMemoryError:位图大小超过虚拟机预算,没有任何理由,我可以看到

您在图库5或图库6中加载的图像可能太大而无法加载,并且超过了虚拟机允许的最大尺寸。

你最好清楚getView参数列表中的convertView总是为null 。 也就是说,gallery不会重用里面的旧视图。