roostaamir
roostaamir

Reputation: 1968

Android gives me `OutOfMemoryException` when loading images from web in a listview

I am using this ArrayAdapter to load thumbnail images asynchronously

public class CourierArrayAdapter extends ArrayAdapter<Courier> {
    private Map<String, Bitmap> bitmaps = new HashMap<>();

    private static class ViewHolder{
        TextView txtViewName;
        TextView txtViewNumber;
        CircleImageView imageViewPic;
        int position;
    }

    public CourierArrayAdapter(Context context, List<Courier> requests) {
        super(context, -1, requests);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        Courier request = getItem(position);

        if (convertView == null) {
            viewHolder = new ViewHolder();
            LayoutInflater layoutInflater = LayoutInflater.from(getContext());
            convertView = layoutInflater.inflate(R.layout.list_item_courier, parent, false);
            viewHolder.txtViewName = (TextView) convertView.findViewById(R.id.textView_list_item_courier_name);
            viewHolder.txtViewNumber = (TextView) convertView.findViewById(R.id.textView_list_item_courier_number);
            viewHolder.imageViewPic = (CircleImageView) convertView.findViewById(R.id.imageView_list_item_courier_pic);
            convertView.setTag(viewHolder);
        }
        else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.txtViewName.setText(request.name);
        viewHolder.txtViewNumber.setText(request.number);
        viewHolder.position = position;

        if (bitmaps.containsKey(request.picURL)) {
            viewHolder.imageViewPic.setImageBitmap(bitmaps.get(request.picURL));
        }
        else
            new LoadImageTask(position, viewHolder).execute(request.picURL);

        return convertView;
    }

    private class LoadImageTask extends AsyncTask<String, Void, Bitmap> {

        private int mPosition;
        private ViewHolder mViewHolder;


        public LoadImageTask(int position, ViewHolder holder) {
            this.mPosition = position;
            this.mViewHolder = holder;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            Bitmap bitmap = null;
            HttpURLConnection connection = null;

            try {
                URL url = new URL(params[0]); // create URL for image

                connection = (HttpURLConnection) url.openConnection();

                InputStream inputStream = connection.getInputStream();
                try {
                    bitmap = BitmapFactory.decodeStream(inputStream);
                    bitmaps.put(params[0], bitmap);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                finally {
                    inputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                connection.disconnect(); // close the HttpURLConnection
            }

            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (mViewHolder.position == mPosition)
                mViewHolder.imageViewPic.setImageBitmap(bitmap);
        }
    }
}

As you can see, I am loading these images using a picURL property and I am loading them using a private AsyncTask class. The problem is that I get OutOfMemoryException an this error message:

FATAL EXCEPTION: AsyncTask #3
  java.lang.RuntimeException: An error occured while executing doInBackground()
      at android.os.AsyncTask$3.done(AsyncTask.java:304)
      at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
      at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
      at java.util.concurrent.FutureTask.run(FutureTask.java:242)
      at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
      at java.lang.Thread.run(Thread.java:818)
   Caused by: java.lang.OutOfMemoryError: Failed to allocate a 37602252 byte allocation with 16776720 free bytes and 29MB until OOM
      at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
      at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
      at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:863)
      at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:839)
      at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:877)
      at net.faraznet.provider.arrayadapters.CourierArrayAdapter$LoadImageTask.doInBackground(CourierArrayAdapter.java:99)
      at net.faraznet.provider.arrayadapters.CourierArrayAdapter$LoadImageTask.doInBackground(CourierArrayAdapter.java:76)
      at android.os.AsyncTask$2.call(AsyncTask.java:292)
      at java.util.concurrent.FutureTask.run(FutureTask.java:237)
      at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) 
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
      at java.lang.Thread.run(Thread.java:818)

The images are a bit large in size and if possible, it is not an issue that I resize them before loading them into memory, but the problem is that I don't know how to achieve this task or any other solutions to my problem

Upvotes: 0

Views: 85

Answers (4)

Raju
Raju

Reputation: 1223

recently i have achieved this, so the best way to achieve this use Picasso, user this code in your array adapter

Picasso.with(this)
            .load(url)
            .into(new Target() {
                @Override
                public void onBitmapLoaded (final Bitmap bitmap, Picasso.LoadedFrom from){
            /* Save the bitmap or do something with it here */

                    //Set it in the ImageView

                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {

                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {

                }
            });

Use this in manifest before Application.

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

Add the following line in dependencies or Gradle.

compile 'com.squareup.picasso:picasso:2.5.2'

for information go through the following links

http://square.github.io/picasso/

https://github.com/square/picasso

i hope it will helpful.

Upvotes: 0

Sreehari
Sreehari

Reputation: 5655

If you can use custom libraries, then use Glide.

Glide is more efficient in performance and memory management with respect to Images than Picasso. check this comparison

All the steps are clearly defined in github to use Glide in application

Alternatively you can increase heap size of application by android:largeHeap="true" . But this will not work on any pre Honeycomb devices

On pre 2.3 devices, you can use the VMRuntime class. but this will not work on Gingerbread and above.

VMRuntime.getRuntime().setMinimumHeapSize(BIGGER_SIZE);

Upvotes: 1

Syed Daniyal Nasir
Syed Daniyal Nasir

Reputation: 134

Add the below line in dependencies.

compile 'com.squareup.picasso:picasso:2.5.2'

and below code to get the image in ImageView.

Picasso.with(context)
.load(url)
.into(imageView)

And you are good to go. For more information, check this link.

Upvotes: 0

Mithun Sarker
Mithun Sarker

Reputation: 4023

You can use a third party library named Glide , which is recommended by google . To add this library , add the the following dependency in your build.gradle file , in module app .

dependencies {
    compile 'com.github.bumptech.glide:glide:3.5.2'
    compile 'com.android.support:support-v4:22.0.0'
}

And then you can simply load image in following way

Glide.with(context)
    .load(request.picURL)
    .into(viewHolder.imageViewPic);

Upvotes: 0

Related Questions