在ViewPager中将大位图加载到ImageView - 内存不足

时间:2013-02-03 15:39:41

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

我有ViewPager用于显示可缩放的图像(使用ImageViewTouch)。 我需要从Internet(http)加载大型位图。我的意思是2000x1000。图像需要如此之大,因为它们是可缩放的,需要显示细节。服务器上的图像是.jpg格式,但不是问题 - 我可以更改它。

如何设置将如此大的图像加载到ImageViewTouch(ImageView)而不会获得带内存的问题?

到目前为止我只使用了这个(AsyncTask):

    ImageView currentView; //ImageView where to place image loaded from Internet
    String ImageUrl; //URL of image to load

    protected Bitmap doInBackground(ArrayList... params) {

            Bitmap bitmap;
            InputStream in = null;
            imageUrl = (String)params[0].get(1);

            try{
                HttpClient httpclient = new DefaultHttpClient();
                HttpResponse response = httpclient.execute(new HttpGet(imageUrl));
                in = response.getEntity().getContent();
            } catch(Exception e){
                e.printStackTrace();
            }
            try {
                bitmapa = BitmapFactory.decodeStream(in);
                in.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }

            return bitmap;
        }


        @Override
        protected void onPostExecute(Bitmap bitmap) {

            currentView.setImageBitmap(bitmap);
        }

它会导致许多内存问题:

E/dalvikvm-heap(369): 69560740-byte external allocation too large for this process.
E/GraphicsJNI(369): VM won't let us allocate 69560740 bytes

E/AndroidRuntime(369): java.lang.RuntimeException: An error occured while executing doInBackground()
E/AndroidRuntime(369):  at android.os.AsyncTask$3.done(AsyncTask.java:200)
E/AndroidRuntime(369):  at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
E/AndroidRuntime(369):  at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
E/AndroidRuntime(369):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
E/AndroidRuntime(369):  at java.util.concurrent.FutureTask.run(FutureTask.java:138)
E/AndroidRuntime(369):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
E/AndroidRuntime(369):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
E/AndroidRuntime(369):  at java.lang.Thread.run(Thread.java:1019)
E/AndroidRuntime(369): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
E/AndroidRuntime(369):  at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
E/AndroidRuntime(369):  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
E/AndroidRuntime(369):  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:525)
E/AndroidRuntime(369):  at com.package.app.ImageDownloader.doInBackground(ImageDownloader.java:78)
E/AndroidRuntime(369):  at com.package.app.ImageDownloader.doInBackground(ImageDownloader.java:1)
E/AndroidRuntime(369):  at android.os.AsyncTask$2.call(AsyncTask.java:185)
E/AndroidRuntime(369):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
E/AndroidRuntime(369):  ... 4 more

2 个答案:

答案 0 :(得分:2)

您在哪个版本的操作系统上进行测试?在早期版本中,可用的堆空间远低于以后的版本。

我会认真考虑缩小你的位图以避免这种情况。如果您为mdpi手机下载2000x1000 Bitmap,那么这可能是一个坏主意。

我还建议您阅读this article,了解有关如何根据尺寸为特定ImageView精确加载相应图片的详细信息。

最后,您应该始终关闭InputStream块中的finally

try {
        bitmap = BitmapFactory.decodeStream(in);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (in != null) { in.close(); }
    }

您还可以考虑使用android:largeHeap="true",专门针对处理大型位图的应用,例如照片编辑应用。

答案 1 :(得分:2)

2000x1000像素图像很大,但不是很大。

我看到,虽然该进程在它死之前尝试分配69560740个字节,但大约是66MByte!

在最坏的情况下,你的2000x1000大约是2000x1000x4 = 8MBytes,小于66MBytes。还有其他事情正在发生。

另外,我发现在AsyncTasks中使用Bitmaps作为返回/结果值存在问题。完成的AsyncTasks仍然保留其结果,直到它们被垃圾收集。由于您不控制AsyncTask实例的garbarge集合,因此不要将Bitmap用作返回/结果值。而是将Bitmap放在一个字段中,并在doInBackground即将结束时分配它,并使用该字段获取onPostExecute中的Bitmap并将(!)设置为null:

Bitmap bitmap;
protected Void doInBackground(ArrayList... params) {

        InputStream in = null;
        imageUrl = (String)params[0].get(1);

        try{
            HttpClient httpclient = new DefaultHttpClient();
            HttpResponse response = httpclient.execute(new HttpGet(imageUrl));
            in = response.getEntity().getContent();
        } catch(Exception e){
            e.printStackTrace();
        }
        try {
            bitmap = BitmapFactory.decodeStream(in);
            in.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        return null;
    }


    @Override
    protected void onPostExecute(Void unused) {
        if (bitmap != null) {
            currentView.setImageBitmap(bitmap);
        }

        // And don't forget to null the bitmap field!
        bitmap = null; 
    }