安卓 - BitmapFactory.decodeByteArray - 内存溢出错误 (OOM)

4

我已经阅读了数百篇关于OOM问题的文章。大部分与大位图有关。我正在制作一个地图应用程序,在该应用程序中,我们下载 256x256 天气叠加瓦片。其中大部分是完全透明且非常小的。我刚刚在调用 BitmapFactory.decodeByteArray(....) 时遇到了一个长度为442字节的位图流崩溃。

异常情况如下:

java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=9415KB, Allocated=5192KB, Bitmap Size=23671KB)

代码如下:
protected Bitmap retrieveImageData() throws IOException {
    URL url = new URL(imageUrl);
    InputStream in = null;
    OutputStream out = null;
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();

    // determine the image size and allocate a buffer
    int fileSize = connection.getContentLength();
    if (fileSize < 0) {
        return null;
    }
    byte[] imageData = new byte[fileSize];

    // download the file
    //Log.d(LOG_TAG, "fetching image " + imageUrl + " (" + fileSize + ")");
    BufferedInputStream istream = new BufferedInputStream(connection.getInputStream());
    int bytesRead = 0;
    int offset = 0;
    while (bytesRead != -1 && offset < fileSize) {
        bytesRead = istream.read(imageData, offset, fileSize - offset);
        offset += bytesRead;
    }

    // clean up
    istream.close();
    connection.disconnect();
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeByteArray(imageData, 0, bytesRead);
    } catch (OutOfMemoryError e) {
        Log.e("Map", "Tile Loader (241) Out Of Memory Error " + e.getLocalizedMessage());
        System.gc();
    }
    return bitmap;

}

这是我在调试器中看到的:

bytesRead = 442

位图数据只有442字节。为什么会尝试创建一个23671KB的位图并且导致内存不足呢?


注意事项:有时候,引发OOM错误的页面并不一定是导致这种错误的唯一原因。有时,OOM是由之前的操作/错误累积触发的,而这些操作/错误只是偶然地通过位图操作被触发。我们应该将整个应用程序作为一个整体来看待,而不仅仅是引发OOM错误的地方。我最近回答的一个SO问题就说明了这种情况http://stackoverflow.com/questions/7136198/android-many-outofmemoryerror-exceptions-only-on-single-activity-with-mapview/7154643#7154643,即OOM错误并不一定是由抛出错误的Activity引起的。 - momo
在SO上已经有很多与这个问题相关的答案,这里是一个应该适用于您的解决方案之一。内存超限 更新:另一个不错的答案在这里,内存泄漏 - Lalit Poptani
3个回答

2

我过去也遇到过这样的问题。Android使用位图虚拟机,它非常小。请确保通过bmp.recycle释放您的位图。较新版本的Android具有更多的位图虚拟机,但我处理的版本有20MB的限制。


我已經盡可能地完成了這件事。這是關於繪製12-18個小位圖的事情。最大的約為10K。所以我已經把它做得盡量瘦了。它只有442個字節,但正在擴展到20MB以上。這是在Android 2.3上進行的。 - Bob Keathley
大家好,这是一个用于缓存瓦片图像以在MapView上绘制的常规程序。如果我不能回收位图或地图上的绘制例程将会崩溃。 - Bob Keathley

0

这可能有效。将位图缩小到较低质量。我不确定,但这可能会在内存中复制图像,但是可以尝试一下。

            Bitmap image;
   image = BitmapFactory.decodeByteArray(data, 0, data.length);
   Bitmap mutableBitmap = image.copy(Bitmap.Config.ARGB_4444, true);
   Canvas canvas = new Canvas(mutableBitmap); 

我之前的回答,我认为在流式传输中不起作用。

                Options options = new BitmapFactory.Options();
        Options options2 = new BitmapFactory.Options();
        Options options3 = new BitmapFactory.Options();

        options.inPreferredConfig = Bitmap.Config.ARGB_8888;///////includes alpha
        options2.inPreferredConfig = Bitmap.Config.RGB_565 ;///////no alpha
        options3.inPreferredConfig = Bitmap.Config.ARGB_4444 ;/////alpha lesser quality

        image=BitmapFactory.decodeResource(getResources(),R.drawable.imagename,options); 
        image=Bitmap.createScaledBitmap(image, widthx,height, true);  

谷歌刚刚发布了一段视频,解释了像素格式,如果有人需要帮助的话! - Philip Giuliani

0

听起来你已经对这个主题有了一些阅读,所以我就不再重复“这是如何解码位图”的评论了。

我觉得你可能还在保留旧位图的引用(也许瓷砖已经移出屏幕,但你仍然在某个数组中保留了一个引用,因此它没有被垃圾回收?)。这在过去曾经让我非常困扰——内存泄漏很难调试。

这里有一个很棒的Google I/O视频,当我遇到类似问题时真的帮了我很多。它大约一个小时,但希望能为你以后节省几天时间。 它涵盖了以下内容:

  1. 创建堆转储
  2. DDMS中的堆使用情况
  3. 使用MAT比较/分析堆转储

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接