大位图压缩(内存不足)

时间:2013-12-20 06:44:38

标签: android bitmap android-imageview bitmapimage android-bitmap

我正在关注developer.android网站here,我正在尝试从网址加载大图片(即1024 x 900)我的代码在我的Google Nexus中工作正常但在Nexus上无效。

这是我的代码

public static Bitmap getBitmapFromURL(String src) {
        Bitmap bmImg;
        URL myFileUrl = null;

        try {
            myFileUrl = new URL(src);

            HttpURLConnection conn= (HttpURLConnection)myFileUrl.openConnection();
            conn.setDoInput(true);
            conn.connect();
            InputStream is = conn.getInputStream();


            final BitmapFactory.Options options = new BitmapFactory.Options();

            BufferedInputStream bis = new BufferedInputStream(is, 4*1024);
            ByteArrayBuffer baf = new ByteArrayBuffer(50);
            int current = 0;
            while ((current = bis.read()) != -1) {
                baf.append((byte)current);
            }
            byte[] imageData = baf.toByteArray();

            BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
            options.inJustDecodeBounds = true;
            options.inSampleSize = calculateInSampleSize(options, 1024, 128);
            options.inJustDecodeBounds = false;
            Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length,options);
            return bitmap;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            return null;
        }
    } 

public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            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) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

AsyncTask类

class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
        private final WeakReference<ImageView> imageViewReference;
        private String data = null;

        public BitmapWorkerTask(ImageView imageView) {
            // Use a WeakReference to ensure the ImageView can be garbage collected
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        // Decode image in background.
        @Override
        protected Bitmap doInBackground(String... params) {
            data = params[0];
            return Util.getBitmapFromURL(data);
        }

        // Once complete, see if ImageView is still around and set bitmap.
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (imageViewReference != null && bitmap != null) {
                final ImageView imageView = imageViewReference.get();
                if (imageView != null) {
                    imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, bitmap.getHeight()/2, bitmap.getWidth(), bitmap.getHeight()/2));
                }
            }
        }
    }

当我在Nexus上运行时,我的内存不足异常。如下面的Android开发者网站,我不明白我在这里做错了什么以及为什么我的应用程序粉碎旧设备。 请帮忙。

注意:我的图片宽度应始终为match_parent。这就是我把calculateInSampleSize(选项, 1024 ,128)

的原因

3 个答案:

答案 0 :(得分:1)

你浪费记忆,因为你在解码整个图像一次后设置了JustDecodeBounds。 (我猜你之后会因为这个而得到OOM)。考虑关闭输入流。

  BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
  options.inJustDecodeBounds = true;
  options.inSampleSize = calculateInSampleSize(options, 1024, 128);
  options.inJustDecodeBounds = false;
  Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length,options);
  return bitmap;

我认为你真的应该这样做:

  options.inJustDecodeBounds = true;
  BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
  options.inSampleSize = calculateInSampleSize(options, 1024, 128);
  options.inJustDecodeBounds = false;
  Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length,options);
  return bitmap;

这将获得图像的宽度和高度而不存储位图(只需获取选项),然后在此之后将图像子采样到正确的大小并返回位图。

我不明白你为什么要在asynctask的 onPostExexute 中重新调整位图的大小, imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, bitmap.getHeight()/2, bitmap.getWidth(), bitmap.getHeight()/2));

当您刚刚完成加载位图时,为什么不加载正确的尺寸开始?(执行上述更改后,我建议您记录尺寸,看看是否真的需要在 onPostExexute

中再次调整大小

您是否看过图片加载库?

有很多可用的,一个似乎符合您需求的是Picasso

在哪里可以执行以下操作: Picasso.with(context).load("url_to_image").fit().into(imageView);

这会下载并缓存位图(在后台线程中)并且适合并绘制到一个imageView中,所有这些都在一行中,整齐!

答案 1 :(得分:0)

您必须在Bitmap Thread内进行Main thread提取,而不是AsyncTask,在getBitmapFromURL()内执行此操作,否则您的应用将会内存不足。

doInBackground Asynctask {{1}} {{1}}方法中的工作是否正常工作

答案 2 :(得分:-1)

在您的清单中:将android:largeHeap=true添加到您的应用程序标签中。