Android:Gallery中的内存不足异常

时间:2010-07-13 14:52:33

标签: android memory-leaks gallery

我的应用程序显示了9个类别的列表,每个类别显示基于图库的封面流程(由Neil Davies here慷慨提供)以及所选类别的图像。
图像从Web获取,每个图像的大小从300K到500K,并存储在Drawables的arrayList中。使用BaseAdapter(下面的代码)将此数据绑定到封面流 每次我退出封面流并返回到类别列表时,我都会清除arrayList(再次,下面的代码) 在方案1中,我的arrayList包含5个Drawable。在这种情况下,我可以自由浏览所有类别并显示他们的图像。在我的测试中,我循环了所有类别5次,这似乎足以确定没有问题 在方案2中,我的arrayList包含10个drawable。在这种情况下,我在第5或第6个类别中的图像时遇到OutOfMemoryError异常:

07-13 08:38:21.266: ERROR/dalvikvm-heap(2133): 819840-byte external allocation too large for this process.
07-13 08:38:21.266: ERROR/(2133): VM won't let us allocate 819840 bytes
07-13 08:38:21.277: DEBUG/skia(2133): --- decoder->decode returned false
07-13 08:38:21.287: WARN/dalvikvm(2133): threadid=25: thread exiting with uncaught exception (group=0x4001b188)
07-13 08:38:21.296: ERROR/AndroidRuntime(2133): Uncaught handler: thread Thread-64 exiting due to uncaught exception
07-13 08:38:21.308: ERROR/AndroidRuntime(2133): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697)
07-13 08:38:21.308: ERROR/AndroidRuntime(2133):     at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657)

这对我没有意义。如果我泄漏内存,我本来期望在方案1中的某个时刻崩溃,但是我经历了很多次并且没有崩溃。我还使用了Eclipse的Memory Analyzer插件,它没有任何潜在的罪魁祸首 如果系统无法处理10个图像,例如在scenarion 2中,我本来应该在第一个类别中崩溃,但是我只在5或6个类别后崩溃。
一些代码:

Coverflow的适配器功能:

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);  
            }    

}

编辑:

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

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 6.20MB of 6.28MB (0.07MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (0.00MB free)
DEBUG/dalvikvm(5221): GC freed 4558 objects / 638152 bytes in 84ms
DEBUG/dalvikvm(5221): GC freed 17 objects / 808 bytes in 67ms

进入第一个画廊后:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 14.90MB of 16.89MB (0.07MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 357 objects / 50080 bytes in 68ms
DEBUG/dalvikvm(5221): GC freed 353 objects / 27312 bytes in 67ms

存在第一个画廊后:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 14.83MB of 16.89MB (0.11MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 330 objects / 17920 bytes in 77ms
DEBUG/dalvikvm(5221): GC freed 13 objects / 760 bytes in 67ms

进入第五个画廊后:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 16.80MB of 23.32MB (0.08MB free) in [com.example.Coverflow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 842 objects / 99256 bytes in 73ms
DEBUG/dalvikvm(5221): GC freed 306 objects / 24896 bytes in 69ms

退出第五个画廊后:

DEBUG/Application(5221): debug. =================================
DEBUG/Application(5221): debug.heap native: allocated 16.74MB of 23.32MB (0.11MB free) in [com.example.Coverlow]
DEBUG/Application(5221): debug.memory: allocated: 4.00MB of 24.00MB (1.00MB free)
DEBUG/dalvikvm(5221): GC freed 331 objects / 18184 bytes in 68ms
DEBUG/dalvikvm(5221): GC freed 60 objects / 3128 bytes in 68ms

似乎在进入图库时会分配越来越多的内存,但退出后会释放很少的内存。我不能正确清理我的抽屉吗?对于我的arrayable of drawables中的每个元素,我调用setCallBack(null)并将该元素设置为null。这还不够吗? 渴望任何洞察力。
感谢

4 个答案:

答案 0 :(得分:37)

  

图像是从网上获取的,   每个范围从300K到500K in   size,并存储在arrayList中   可绘。

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

位图使用本机堆,通常不会在hprof中显示。 hprof应该只显示对象的数量,即剩下的BitmapDrawables或Bitmaps。

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

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());

以下是一些信息性链接 - 通常在这里有很多关于此主题的主题。

这也是Romain Guy(Android Framework工程师)关于软引用,弱引用,简单缓存,图像处理的有用幻灯片: http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf

答案 1 :(得分:4)

以下是一些建议:

  1. 您使用inSampleSize选项吗?如果缩放图像,它可以减少内存消耗。 Strange out of memory issue while loading an image to a Bitmap object

  2. 当您不再需要图像时,应该调用Bitmap.recycle()。我认为这对你的情况很重要。 Android: OutofMemoryError: bitmap size exceeds VM budget with no reason I can see

答案 2 :(得分:0)

您在图库5或图库6中加载的图片可能太大而无法加载,并且超出了VM允许的最大尺寸。

答案 3 :(得分:0)

您最好清楚convertView参数列表中的getView始终为null。也就是说,图库不会重复使用旧视图。