在Android上加载非常大的图像时的内存分配 [英] Memory allocation when loading a really large image on Android

查看:83
本文介绍了在Android上加载非常大的图像时的内存分配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在加载位图时,我需要弄清楚可以分配多少内存而无需OutOfMemoryError.我决定从文件中加载真正大图像.这是其网址: https://upload.wikimedia.org/wikipedia /commons/3/3d/LARGE_elevation.jpg

I need to figure out much memory I can allocate without having an OutOfMemoryError when loading a bitmap. I decided to load a really large image from a file. Here is its URL: https://upload.wikimedia.org/wikipedia/commons/3/3d/LARGE_elevation.jpg

图像大小为14488 498字节(磁盘上为14.5 MB).这是我的代码:

The image size is 14 488 498 bytes (14,5 MB on disk). And here is my code:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String LOG_TAG = "LargeBitmapLargeBitmap";

    private ActivityManager mActivityManager;
    private ImageView mImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        mImageView = (ImageView) findViewById(R.id.image);
        findViewById(R.id.get_mem_info).setOnClickListener(this);
        findViewById(R.id.load_bitmap).setOnClickListener(this);

    }

    @Override
    public void onClick(final View v) {
        switch (v.getId()) {
            case R.id.get_mem_info:
                Runtime rt = Runtime.getRuntime();
                Log.v(LOG_TAG, "maxMemory: " + rt.maxMemory());
                Log.v(LOG_TAG, "freeMemory: " + rt.freeMemory());
                Log.v(LOG_TAG, "totalMemory: " + rt.totalMemory());
                Log.v(LOG_TAG, "mActivityManager.getMemoryClass(): " + mActivityManager.getMemoryClass());
                break;
            case R.id.load_bitmap:
                new ImageLoadingTask(mImageView).execute();
                break;
        }
    }

    // I don't handle the screen rotation here since it's a test app
    private static class ImageLoadingTask extends AsyncTask<Void, Void, Bitmap>  {

        private ImageView mImageView;

        ImageLoadingTask(ImageView imageView) {
            mImageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(final Void... params) {
            String path = Environment.getExternalStorageDirectory().getAbsolutePath()
                    + File.separator
                    + "LARGE_elevation.jpg";
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeFile(path);
            } catch (OutOfMemoryError error) {
                Log.e(LOG_TAG, "Failed to load the large bitmap", error);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(final Bitmap bitmap) {
            super.onPostExecute(bitmap);
            mImageView.setImageBitmap(bitmap);
        }
    }
}

我编译了上面的代码,并在Nexus 5上运行了它.然后按下get_mem_info按钮并获得以下输出:

I compiled the code above and ran it on my Nexus 5. Then I pressed the get_mem_info button and got the following output:

05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: maxMemory: 201326592
05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: freeMemory: 4991056
05-02 12:07:35.872 28053-28053/com.sample V/LargeBitmapLargeBitmap: totalMemory: 20733248
05-02 12:07:35.873 28053-28053/com.sample V/LargeBitmapLargeBitmap: mActivityManager.getMemoryClass(): 192

这意味着我的应用程序可用的堆内存为192 MB.但是当我按下load_bitmap按钮时,没有加载图像,并且得到了OutOfMemoryError:

which means that the heap memory available for my application is 192 MB. But when I pressed the load_bitmap button, the image wasn't loaded, and I got an OutOfMemoryError:

05-02 12:07:38.712 28053-29533/com.sample E/LargeBitmapLargeBitmap: Failed to load the large bitmap
                                                                    java.lang.OutOfMemoryError: Failed to allocate a 233280012 byte allocation with 10567360 free bytes and 176MB until OOM
                                                                        at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
                                                                        at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
                                                                        at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635)
                                                                        at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611)
                                                                        at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:391)
                                                                        at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:417)
                                                                        at com.sample.MainActivity$ImageLoadingTask.doInBackground(MainActivity.java:70)
                                                                        at com.sample.MainActivity$ImageLoadingTask.doInBackground(MainActivity.java:55)

为什么会这样?为什么系统需要分配233280012字节(约220MB)? "10567360可用字节和176MB直到OOM"这一部分对我来说也很奇怪.为什么我看到这些数字?

Why did that happen? Why did the system need to allocate 233280012 bytes (about 220MB)? The part "10567360 free bytes and 176MB until OOM" looks strange to me as well. Why do I see these numbers?

推荐答案

首先,
JPEG 的大小为14.5 MB,而不是实际图像的大小.
同样,尝试将图像重新保存为png,您会看到大小增加了10倍,即,甚至可能达到150 MB

First things first,
14.5 MB is the size of the JPEG but not the size of your actual image.
Similarly, try to resave the image as png, you will see that size is increased by factors of 10, i.e. it might even reach 150 MB

您必须记住的一件事是,这些图像被压缩为JPEG或PNG.
但是,当将这些图像加载到imageview左右时,图像的每个 Bit 都会被解压缩并占用RAM中的内存.

One thing that you must keep in mind is that these images are compressed into JPEG or PNG.
But when these images are loaded into imageview or so, each Bit of the image is decompressed and occupies a memory in RAM.

因此,图像的实际大小基本上是其分辨率乘以4(A-R-G-B)

So the actual size of your image is basically its resolution multiplied by 4 (A-R-G-B)

编辑-那么如何处理这些图像?
首先,使用Glide(或Picasso)加载图像,而不是为此编写自己的AsyncTask.

EDIT - How to handle these images then?
Firstly, use Glide (or Picasso) to load the images, instead of writing own AsyncTask for this.

在Glide中,有一种名为override(int width, int height)的方法,该方法将在下载时调整图像大小.
理想情况下,宽度和高度应该是ImageView(或任何视图)的确切尺寸,这样可以防止图像像素化,并且还可以节省内存的额外消耗. (您也可以始终进行较小的计算来保持图像的宽高比)

In Glide there is a method called override(int width, int height) which will resize the image while downloading.
Ideally width and height should be the exact dimension of the ImageView (or any view), this will prevent the image from pixellating and also it will save the additional consumption of the memory. (you can always do minor calculations to retain image aspect ratio too)

这篇关于在Android上加载非常大的图像时的内存分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆