I am following developer.android website here and I am trying load large image from url (i.e 1024 x 900) My code works fine in my Google Nexus but doesn't work on Nexus s.

Here is my code

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;
        }
    } 

and

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

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));
                }
            }
        }
    }

When I run on Nexus s I have out of memory exception. As following android developer website I didn't understand what I did wrong here and why my application crushes in old devices. Please help.

Note: my image width should be match_parent always. That's why I put calculateInSampleSize(option,1024,128)

有帮助吗?

解决方案

You are wasting memory becuase you've set inJustDecodeBounds after you decoded the whole image once. (I guess you get OOM because of that later on). Consider closing your inputstream as well.

  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;

I think you really meant to do:

  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;

That will get the width and height of the image without storing the bitmap (just get options) and then you subsample the image to correct size after that and returns the bitmap.

I don't understand why you resize the bitmap again in onPostExexute of your asynctask, imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, bitmap.getHeight()/2, bitmap.getWidth(), bitmap.getHeight()/2));

When you just completed to load the bitmap, why not load the correct size to begin with? (after doing the above changes i would suggest you to log the sizes and see if you really need to resize again in onPostExexute)

Have you looked at image loading libraries?

There's quite a few available, one that seems to fit your needs is Picasso

Where one can do such things as: Picasso.with(context).load("url_to_image").fit().into(imageView);

This downloads and caches the bitmap (in a background thread) and fits and draws into an imageView, all in one line, neat!

其他提示

You must do the Bitmap extraction in Thread not in Main thread , Do this within a AsyncTask Otherwise your app will get out of memory.

Do the work in your getBitmapFromURL() work within doInBackground method of Asynctask

In your manifest : add android:largeHeap=true to your application tab.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top