BitmapFactory.decodeResource和莫名其妙的内存不足

时间:2016-03-18 17:26:47

标签: android android-image android-bitmap bitmapfactory android-memory

我得到一个奇怪的Out of Memory错误解码一个可绘制的图像资源960x926px jpg,分配3555856字节。 图像仅放在drawable-xxhdpi(3x)中,我使用的是hdpi(1.5x)设备。 两个问题:

  • 为什么我在堆中有足够的可用内存时会出现错误?

  • 为hdpi设备分配应该是((960/2)x(926/2))x 4 = 888960字节(不是3555856)?

有人可以解释一下吗?

注意:问题是为什么在拥有22.5MB可用内存的情况下获得3.5MB的OOM分配(参见日志)

  

03-18 17:30:15.050 32750-32750 /? D / dalvikvm:GC_FOR_ALLOC被释放   10809K,49%免费23735K / 46087K,暂停89ms,总计89ms

     

03-18 17:30:15.050 32750-32750 /? I / dalvikvm-heap:强制收集   SoftReferences用于3555856字节分配

     

03-18 17:30:15.160 32750-32750 /? D / dalvikvm:GC_BEFORE_OOM释放29K,    49%免费23705K / 46087K ,暂停103毫秒,总计103毫秒

     

03-18 17:30:15.160 32750-32750 /? E / dalvikvm-heap:内存不足   3555856字节分配

     

03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:"主要" prio = 5 tid = 1   RUNNABLE

     

03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:|基团="主" SCOUNT = 0   dsCount = 0 obj = 0x418fc6a0 self = 0x4010c008

     

03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:| sysTid = 32750 nice = 1   sched = 0/0 cgrp = apps handle = 1075251280

     

03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:| schedstat =(0 0 0)   utm = 3807 stm = 859 core = 0

     

03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at   android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)

     

03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at   android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:636)

     

03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at   android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:484)   03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at   android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:512)

     

03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at   android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:542)

5 个答案:

答案 0 :(得分:2)

1)如果您在hdpi文件夹中没有较小的版本,它将使用最接近的匹配。因此,如果不存在hdpi或drawable / version,它将使用xxhdpi。

2)它不会自动缩放。它将以完整尺寸读取。

3)如果这会导致OOM,一般情况下你可能会使用太多内存。

答案 1 :(得分:2)

你获得OOM的原因是因为当图像被解码到内存中时,它的位图比图像分辨率需要更多的大小(差不多4次,我不确定这个值)。

使用图片时要记住几点:

  1. 永远不要处理主线程上的位图。在后台进行所有解码。
  2. 始终考虑要放置图像的视图的屏幕大小或大小。例如,如果屏幕尺寸为360X720(某些随机值),那么解码分辨率大于所需尺寸的全分辨率图像并不是一个好主意(因为它会在主存储器中加载完整的位图)。因此,在解码时总是进行采样。
  3. 因此,请尝试使用以下解决方案:

    第1步:查找屏幕尺寸

    取自here

    Display display = getWindowManager().getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    int width = size.x;
    int height = size.y;
    

    步骤1:创建异步任务以解码位图

    如果您使用异步任务作为内部类,则考虑使用公共静态内部类(以节省内存泄漏问题)并保留要加载图像的imageview的弱引用。还可以将图像资源或文件或流传输到构造函数。在下面的代码中,假设您要解码资源。同时传递步骤1中计算的宽度和高度。

    public static class BitmapDecodeTask extends AsyncTask<Void, Void, Bitmap> {
        //the reason to use a weak reference is to protect from memory leak issues.
        private WeakReference<Context> mContextReference;
        private WeakReference<ImageView> mImageViewReference;
        private int mResourceId;
        private int mRequiredWidth;
        private int mRequiredHeight;
    
        public BitmapDecodeTask(Context context, ImageView imageView, int resourceId, int width, int height) {
            this.mContextReference = new WeakReference<>(context);
            this.mImageViewReference = new WeakReference<>(imageView);
            this.mResourceId = resourceId;
            this.mRequiredWidth = width;
            this.mRequiredHeight = height;
        }
    
        @Override
        protected Bitmap doInBackground(Void... params) {
    
            Context context = mContextReference.get();
    
            if(context != null) {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeResource(getResources(), mImageResourceId, options);
    
                //set inSampleSize
                options.inSampleSize = calculateInSampleSize(options);
    
                //set inJustDecodeBounds = false;
                options.inJustDecodeBounds = false;
    
                //decode
                return BitmapFactory.decodeResource(getResources(), mImageResourceId, options);
            }
    
            return null;
        }
    
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            //check if imageview is available or not
            ImageView imageView = mImageViewReference.get();
    
            if(imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    
        public static int calculateInSampleSize(BitmapFactory.Options options) {
            // Raw height and width of image
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
    
            if (height > mRequiredHeight || width > mRequiredWidth) {
                final int halfHeight = height / 2;
                final int halfWidth = width / 2;
    
                // Calculate the largest inSampleSize value that is a power of 2 and keeps both
                // height and width larger than the requested height and width.
                while ((halfHeight / inSampleSize) > mRequiredHeight 
                    && (halfWidth / inSampleSize) > reqWidth) {
                    inSampleSize *= 2;
                }
            }
            return inSampleSize;
        }
    }
    

    参考文献:

    Bitmaps

答案 2 :(得分:1)

位图的内存大小是宽度x高度x每个颜色的位数。我相信你没有使用任何特定的选项,你使用每像素4个字节(红色1个字节,绿色1个字节,蓝色1个字节,Alpha 1个字节)。所以:960 * 926 * 4 = 3555840字节。

关于你的OOM:对我来说似乎并不常见的是:

  

强制收集3555856字节分配的SoftReferences

您在哪里存储分配的位图? Android上应避免使用软引用。

答案 3 :(得分:1)

根据Gabe Sechan的建议,我建议使用像makeappicon这样的网站,它允许您自动缩放任何图像太大。它是一个有用的工具,虽然Android应该为您处理这些缩放问题。

Mimmo Grottoli关于ARGB字节是正确的,所以就我所知,没有什么可担心的。

你最有可能得到这个错误的原因是因为创建(而不是破坏)位图所涉及的内存泄漏很大,我从一段时间的steg项目中学到了很多困难。

为了清理此内存,您可以覆盖Activity / Fragment的onDestroy()方法,或在必要时手动执行。

@Override
 public void onDestroy() {
    yourbitmap.recycle();
    yourbitmap = null;
    super.onDestroy();
 }

希望他对你有所帮助。

答案 4 :(得分:0)

最好使用Glide或picasso等库来进行所有图像操作(解码,调整大小,下载等):

将路径替换为本地文件夹路径或服务器URL

依赖

使用Glide加载图片

compile 'com.github.bumptech.glide:glide:3.5.2'