Submit your appAboutContact

Android out of memory: how to load large bitmap from url efficiently

In this tutorial you'll learn how to download and load large bitmap images from an url in an efficient way to avoid the android out of memory error. You'll see how to create a simple class that use the ExecutorService java class that manages the asynchronous requests, how to download the images from an url, how to resample them to avoid the out of memory error and how to display them in an ImageView.

Project initialization

Let's start by creating a new Android Studio project. Put inside your layout an ImageView with id "image".

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="kaleidosstudio.com.loadimages.MainActivity">

    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         />

</RelativeLayout>

Insert then in the Androidmanifest.xml file the internet access permission settings.

    <uses-permission android:name="android.permission.INTERNET" />

 

ImageLoader class

Let's create now the ImageLoader class. Just right click in your java project folder, new, java class. Call the file ImageLoader.

Define the required variables. We are creating a pool of 5 asynchronous tasks with the ExecutorService java feature. The req_width and req_height are used to compute the image sample size based on the required width and height.

public class ImageLoader {
    public ExecutorService executorService;
    public AppCompatActivity c = null;
    public int req_width = 0;
    public int req_height = 0;

    ImageLoader(AppCompatActivity c)
    {
        this.c = c;
        executorService= Executors.newFixedThreadPool(5);
    }
}

Let's define now the queueImage function. This function will have as an input the ImageView in which you can load the bitmap image when it is ready and the direct link of the image to download.

public void queueImage(final ImageView imageView, String image_link)
    {
        ImageToLoad p=new ImageToLoad(imageView, image_link);
        executorService.submit(new ImageLoaderProcess(p));
    }


ImageToLoad is a class used to store the ImageView reference and the url of the image to download. ImageLoaderProcess is a class that implements the Runnable java background process.

 private class ImageToLoad
    {
        public ImageView imageView;
        String image_link;
        public Activity c;

        public ImageToLoad( ImageView i, String image_link){
            this.imageView = i;
            this.image_link = image_link;
        }
    }


    class ImageLoaderProcess implements Runnable {
        ImageToLoad imagetoload;
        ImageLoaderProcess(ImageToLoad imagetoload){
            this.imagetoload=imagetoload;
        }

        @Override
        public void run() {
                Bitmap bmp=getBitmap(imagetoload.image_link);
                BitmapDisplayer bd=new BitmapDisplayer(bmp, imagetoload);
                c.runOnUiThread(bd);
        }
    }

Let's define now the getBitmap function. This is the core function that will download the image from the given URL. If the req_width size is equal to 0, the image is directly downloaded without the resampling process. Otherwise as a first step just the images metadata are downloaded (this is reached by turning on inJustDecodeBounds). This is done in order to get the images information such as the width and height, used to calculate the largest sample size, larger than the requested height and width. Then the image is downloaded and directly resampled.

 private Bitmap getBitmap(String image_link)
    {
        URL website;

        try {
            website = new URL(image_link);
            HttpURLConnection connection = (HttpURLConnection) website.openConnection();
            InputStream is = connection.getInputStream();

            if(req_width == 0)
            {
                return BitmapFactory.decodeStream(is);
            }


            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(is, null, options);
            is.close();


            connection = (HttpURLConnection) website.openConnection();
            is = connection.getInputStream();
            options.inSampleSize = calculateInSampleSize(options, req_width, req_height);
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeStream(is, null , options);

        } catch (Exception  e) {
            return null;
        }
    }

    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // 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;
    }

 

Display the bitmap image

It's now time to display the image in the ImageView component. This should be done in the UI Thread process. For this reason the runOnUiThread function is used, that's because the image is returned from a background process.

   class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageToLoad imagetoload;
        public BitmapDisplayer(Bitmap b, ImageToLoad imagetoload){this.bitmap=b;this.imagetoload=imagetoload;}
        public void run()
        {

            if(bitmap!=null)
            {
                imagetoload.imageView.setImageBitmap(bitmap);
            }
        }
    }

 

Let's start downloading resampled images

Open now the MainActivity class and start downloading images from the given url, resample them basing on the given width and height and display them in the android ImageView component.

    public ImageLoader imageloader = null;

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

        imageloader = new ImageLoader(this);

        Display display = getWindowManager().getDefaultDisplay();
        DisplayMetrics outMetrics = new DisplayMetrics ();

        display.getMetrics(outMetrics);
        imageloader.req_width = outMetrics.widthPixels;
        imageloader.req_height = outMetrics.heightPixels;

        imageloader.req_width = outMetrics.widthPixels;
        imageloader.req_height = outMetrics.heightPixels;

        ImageView image = (ImageView) findViewById(R.id.image);
        imageloader.queueImage(image, "http://www.kaleidosblog.com/tutorial/sunset-169925_1920.jpg");
    }

 

Here you'll find the android studio project for this example: download.

 

 

 

Leave a comment












Submit your appAboutContactPrivacy