Android:BitmapFactory DecodeStream方法的OutOfMemory错误

时间:2013-12-17 02:51:37

标签: android bitmap android-asynctask out-of-memory decode

我一直在讨论这个问题一段时间了。不过,我还有一些希望。

我的一项活动是使用 AsyncTask 下载图片。它将图像保存在位图中,然后在图像视图中显示。这是代码:

@Override
    protected String doInBackground(String... urls) {
        try {

            URL url = new URL(urls[0]);

            // First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(url.openConnection()
                    .getInputStream(), null, options);

            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, 270, 173);

            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            options.inPurgeable = true;
            options.inInputShareable = true;
            bmp = BitmapFactory.decodeStream(url.openConnection()
                    .getInputStream(), null, options);

        } catch (Exception e) {
            Log.e(MainActivity.class.toString(),
                    "No se pudo descargar la imagen");
        }
        return "";
    }

    @Override
    protected void onPostExecute(String result) {
        if (bmp != null) {

            imagenNoticia.setImageBitmap(bmp);
            imagenNoticia.setVisibility(View.VISIBLE);
        }

    }
}

但是在浏览应用程序一段时间之后,通过打开和关闭此活动,我在代码行 BitmapFactory.decodeStream 处得到了一个 OutOfMemoryError 。我确信这个活动永远不会重复,所以我一次只是其中的一种。

正如大家们所看到的,我已经使用了几种关于有效解码位图的最佳实践。像下采样接收的图像(inJustDecodeBounds)。

我想知道我还能做些什么来避免这个错误。 有人对此有所了解吗?

接下来是错误。

谢谢!任何帮助都会很感激。

12-16 18:19:17.686: E/AndroidRuntime(13321): FATAL EXCEPTION: AsyncTask #1
12-16 18:19:17.686: E/AndroidRuntime(13321): java.lang.RuntimeException: An error         occured while executing doInBackground()
12-16 18:19:17.686: E/AndroidRuntime(13321):    at     android.os.AsyncTask$3.done(AsyncTask.java:299)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at     java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at     java.util.concurrent.FutureTask.setException(FutureTask.java:124)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at java.lang.Thread.run(Thread.java:856)
12-16 18:19:17.686: E/AndroidRuntime(13321): Caused by: java.lang.OutOfMemoryError
12-16 18:19:17.686: E/AndroidRuntime(13321):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:652)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at     com.mobilemedianet.larepublica.activity.NotiDetalle$CargarNoticias.doInBackground(NotiDetalle.java:496)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at com.mobilemedianet.larepublica.activity.NotiDetalle$CargarNoticias.doInBackground(NotiDetalle.java:1)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at android.os.AsyncTask$2.call(AsyncTask.java:287)
12-16 18:19:17.686: E/AndroidRuntime(13321):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
12-16 18:19:17.686: E/AndroidRuntime(13321):    ... 5 more

2 个答案:

答案 0 :(得分:0)

您有两个位图的引用:一个位于视图中,另一个位于AsyncTask中。 你能做什么,至少在理论上是这样做的:

1)将下载的位图保存到磁盘(SD卡等)。在这种情况下,您将能够首先停止使用第一个图像,然后才使用第二个图像。

2)将图像下载到单独的进程。它将有自己的堆引用。缩放可能应该进入相同的(第二个)过程。在XML中,活动和服务具有process属性。通常,杀死进程是杀死Java垃圾的好方法。

3)流程和活动的生命周期中存在低内存回调。可能这是你可以在视图中释放图像的那一刻(我自己没有尝试过,甚至不确定这些回调是否实际被调用)。

4)您可以将应用程序的不同活动放在不同的进程中(这取决于您的应用程序是什么)。

答案 1 :(得分:0)

我认为你有一个变量范围问题,谁拥有bmp变量?它是否从您的代码中清除?在imageview设置了位图后,AsyncTask是否已被淘汰?否则你就是泄漏那个bmp(它没有被释放!)。

重写AsyncTask选项

我认为你是AsyncTask应该扩展AsyncTask<String, Void, Bitmap>给你(并改变我在这里所写的内容):

@Override
protected String doInBackground(String... urls) {
    try {
        // omitted some lines

        return BitmapFactory.decodeStream(url.openConnection()
                .getInputStream(), null, options);

    } catch (Exception e) {
        Log.e(MainActivity.class.toString(),
                "No se pudo descargar la imagen");
    }
    return null;
}

@Override
protected void onPostExecute(Bitmap result) {
    if (result != null) {
        imagenNoticia.setImageBitmap(result);
        imagenNoticia.setVisibility(View.VISIBLE);
    }
}

上述AsyncTask在执行onPostExecute后不会保留任何位图引用。

其他选项

如果您不想执行上述操作,则应考虑在onPostExecute中最后将bmp变量置零,最后在AsyncTask中实现onCancelled回调,当且仅当您在任务上调用cancel时,然后在bmp回调中清除onCancelled引用(因为onPostExecute在这种情况下不会运行)