FATAL EXCEPTION error loading image gallery using AsyncTask

1

I'm creating a small gallery and I'm having a hard time loading images using AsyncTask

class adapter:

public class AdapterGaleriaFragment extends BaseAdapter {
Context ctx;
List<ImageDataModel> lista;
public AdapterGaleriaFragment(Context ctx, List <ImageDataModel> lista){
    this.ctx = ctx;
    this.lista = lista;
}

@Override
public int getCount() {
    return lista != null ? lista.size():0;
}

@Override
public Object getItem(int position) {
    return lista.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View view;
    ViewHolder holder;
    if (convertView == null) {
        view = LayoutInflater.from(ctx).inflate(R.layout.layout_adapter, parent, false);
        holder = new ViewHolder(view);
        view.setTag(holder);
    } else {
        view = convertView;
        holder = (ViewHolder)view.getTag();
    }
    ImageDataModel img = lista.get(position);
    loadBitmap(img.getImagePath(),holder.imgView);
    return view;
}
public void loadBitmap(String path, ImageView view) {
    Task task = new Task(view);
    task.execute(path);
}
}

AsyncTask class:

public class Task extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
public Task(ImageView imageView) {
    // Use a WeakReference to ensure the ImageView can be garbage collected
    imageViewReference = new WeakReference<ImageView>(imageView);
}

@Override
protected Bitmap doInBackground(String... params) {
    File file = new File(params[0]);
    Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());

    return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap){
    if (imageViewReference != null && bitmap != null) {
        final ImageView imageView = imageViewReference.get();
        if (imageView != null) {
            imageView.setImageBitmap(bitmap);
        }
    }
}
}

Log:

E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #5
Process: com.example.alex_sama.galeriramesmo, PID: 25570
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 3686412 byte allocation with 2714916 free bytes and 2MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611)
at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:391)
at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:417)
at com.example.alex_sama.galeriramesmo.Task.doInBackground(Task.java:29)
at com.example.alex_sama.galeriramesmo.Task.doInBackground(Task.java:19)
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)
    
asked by anonymous 18.10.2016 / 23:44

2 answers

2

Your implementation will fatally fail on large images and / or many images. You need to use a list component that implements recycling of the displayed / non-displayed items on the screen, as well as making your life easier with an image upload library.

The simple act of loading an image into a ImageView is infinitely more complex on the android than you think. You need to worry about screen rotations (orientation changes will destroy the context in which you are loading the image), background reading is not simple to implement correctly, and it is necessary to reduce the size of the loaded image to a size that fits in the memory and is compatible with the size of the% target%, etc.

Solve your life using either Google's own Glide or Picasso's square. I suggest Glide.

In addition, change your project to use ImageView , which will implement recycling of your RecyclerView objects, spending memory only for the images displayed on the screen.

A good tutorial, though in English: link

To learn more about Glide: link (scroll down, because the library's "readme" is below the code)

To get to know the Picasso library, this is a good alternative: link (worth reading to see how it is more complex than it seems to display images on android)

That being said, just modifying your code to use Glide or Picasso will most likely avoid the problem of lack of memory.

Add Glide to your gradle project:

dependencies {
  compile 'com.github.bumptech.glide:glide:3.7.0'
  compile 'com.android.support:support-v4:19.1.0'
}

Modify the image upload in your code to:

@Override public View getView(int position, View convertView, ViewGroup parent) {
    View view;
    ViewHolder holder;
    if (convertView == null) {
        view = LayoutInflater.from(ctx).inflate(R.layout.layout_adapter, parent, false);
        holder = new ViewHolder(view);
        view.setTag(holder);
    } else {
        view = convertView;
        holder = (ViewHolder)view.getTag();
    }
    ImageDataModel img = lista.get(position);

    // não precisa se preocupar, pois o Glide já faz em background
    Glide
        .with(holder.imgView.getContext())
        .load(img.getImagePath())
        .centerCrop()
        .into(holder.imgView);

    return view; 
}
    
19.10.2016 / 02:01
0

You can change the size of the android heap memory using the following command in your manifest.xml android:largeHeap="true"

    
18.10.2016 / 23:52