【问题标题】:Out of memory exception with bitmaps位图内存不足异常
【发布时间】:2013-02-13 22:05:11
【问题描述】:

我的应用程序有问题。在陆地模式下切换时,调用 setContentView() 方法来显示钢琴键盘。钢琴键盘类扩展了 Surfaceview 以更好地显示按下的键。这个 SurfaceView 类作为子类添加到我的横向布局中:

RelativeLayout rootLayout = (RelativeLayout) findViewById(R.id.rootLayout);
RelativeLayout.LayoutParams relativeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
relativeLayoutParams.addRule(RelativeLayout.BELOW, R.id.relativeLayoutLowerBar);
rootLayout.addView(keyboardSurfaceView, relativeLayoutParams); 

在我的keyboardSurfaceView 类中,我使用BitmapFactory.decodeResource() 方法将位图加载到位图数组中,并保存位图直到应用程序被销毁。这很好用。当我在纵向模式下切换时,我使用 setContentView() 更改布局并删除 keyboardSurfaceView :

if(keyboardSurfaceView != null && keyboardSurfaceView.getParent() != null)
        ((ViewGroup) keyboardSurfaceView.getParent()).removeView(keyboardSurfaceView);

位图加载一次。第一次切换横向模式。我遇到了内存不足的错误,当从风景中的 protrait 等进行 10-20 次切换时,等等。当我在 ddms 视图中更新堆时,我可以看到,每次当我从横向视图中的 protrait 视图切换时,堆大小都会增长到 ca。 20mb 然后应用程序崩溃。我不知道为什么这一直在发生。位图只加载一次,而不是每次。 我也试过bitmap.recycle(); bitmap = null没有成功。还尝试使用 LRUCache 类缓存位图,如 Google 最佳实践中所述。我还搜索了 stackoverflow 以找到适合我的问题的解决方案。仍然无法解决此问题。我自己处理纵向/横向更改(覆盖了 onConfigurationChanged() 方法)。将所有图像放入 drawable-xhdpi 会有所帮助。改变方向时,堆大小和以前一样增长,但仍在增长。任何帮助将不胜感激...

例外:

 02-13 22:44:09.419: E/dalvikvm-heap(935): 11448-byte external allocation too large for this process.
02-13 22:44:09.419: E/dalvikvm(935): Out of memory: Heap Size=16391KB, Allocated=13895KB, Bitmap Size=16394KB, Limit=32768KB
02-13 22:44:09.419: E/dalvikvm(935): Trim info: Footprint=16391KB, Allowed Footprint=16391KB, Trimmed=432KB
02-13 22:44:09.419: E/GraphicsJNI(935): VM won't let us allocate 11448 bytes

【问题讨论】:

    标签: android bitmap surfaceview out-of-memory bitmapfactory


    【解决方案1】:

    这是关于如何有效处理位图的 Android 文档

    http://developer.android.com/training/displaying-bitmaps/index.html

    【讨论】:

    • 我已经读过了,但没有成功。问题是堆不断增长,即使位图已经在内存中
    • 如果是这样,您能否发布更多代码,因为您的代码可能在其他地方包含错误。
    【解决方案2】:

    好的,第一次在我的surfaceView类中调用surfaceCreated时,我调用了这个方法,并且这个方法只调用一次,而不是每次我在屏幕上显示surfaceView时:

    bitmapKeyboard = BitmapFactory.decodeResource(context.getResources(), R.drawable.keyboard); 
    
    bitmapGlowImages[0] = BitmapFactory.decodeResource(context.getResources(), R.drawable.keycdown); 
    bitmapGlowImages[1] = BitmapFactory.decodeResource(context.getResources(), R.drawable.blackkeydown);
    bitmapGlowImages[2] = BitmapFactory.decodeResource(context.getResources(), R.drawable.keyddown);
    bitmapGlowImages[3] = BitmapFactory.decodeResource(context.getResources(), R.drawable.blackkeydown);
    bitmapGlowImages[4] = BitmapFactory.decodeResource(context.getResources(), R.drawable.keyedown);
    bitmapGlowImages[5] = BitmapFactory.decodeResource(context.getResources(), R.drawable.keycdown); 
    bitmapGlowImages[6] = BitmapFactory.decodeResource(context.getResources(), R.drawable.blackkeydown);
    bitmapGlowImages[7] = BitmapFactory.decodeResource(context.getResources(), R.drawable.keyddown);
    bitmapGlowImages[8] = BitmapFactory.decodeResource(context.getResources(), R.drawable.blackkeydown);
    bitmapGlowImages[9] = BitmapFactory.decodeResource(context.getResources(), R.drawable.keyddown);
    bitmapGlowImages[10] = BitmapFactory.decodeResource(context.getResources(), R.drawable.blackkeydown);
    bitmapGlowImages[11] = BitmapFactory.decodeResource(context.getResources(), R.drawable.keyedown);
    

    在这个类中还运行一个单独的线程来绘制按下的键:canvas.drawBitmap(bitmap, null, destRect, null)。最后,当onDestroy被调用时,我的方法recycleBitmaps()被调用了

    public void recycleBitmaps() {
            if(bitmapKeyboard != null) {
                bitmapKeyboard.recycle();
                bitmapKeyboard = null;
                for(int i = 0; i < bitmapGlowImages.length; i++) {
                    bitmapGlowImages[i].recycle();
                    bitmapGlowImages[i] = null;
                }
                System.gc();
            }
    }
    

    就是这样。每次在横向时,我都会在横向布局中将我的 SurfaceView 添加到我的父视图中,在纵向中我再次将其删除,然后加载我的纵向布局。我在onConfigurationChanged() 中制作了这个,因为我自己处理方向更改,所以每次更改设备方向时都不会破坏和创建应用程序。

    【讨论】:

    • 好的,现在可能没有使应用程序崩溃。我将设备旋转了大约 20-30 次。经过 6-7 次轮换后,它仍在堆上分配更多存储空间(但现在只有 300-400kb)。当达到堆大小时,它之前会崩溃,现在它只会增加堆大小。我在所有可绘制文件夹(mdpi、ldpi、xhdpi、hdpi)中复制了我的 png 文件。但问题仍然存在......我认为它与图片缩放 ldpi、hdpi 等有关。
    猜你喜欢
    • 2013-06-24
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 1970-01-01
    • 2014-08-26
    相关资源
    最近更新 更多