recyclerview vieholder is not getting recycled












0















developing an image picker using recyclerview with all images in devices for an android app, facing an issue when user scroll through images its memory usage keeps increasing eventually if lots of images there is oom exception, have searched for this issue over the internet, have applied several techniques but it is of little use, i have seen other app(whatsapp, olx) its picker scrolling is nice and no oom, i also tried dump heap in android studio to detect memory leakage, i found that there are as many viewholders as number of images, its not being recycled, am not able to find the reason, please help me in detecting the issue, attaching code..



/** adapter class for grid recyclerview*/
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<CreateList> galleryList;
private Context context;
private List<String> imageUrls = new ArrayList<>(3);
private List<Integer> positionList = new ArrayList<>(3);
int count = 0;
RequestOptions options;
RequestBuilder glide;
RequestManager glideMain;
int imgWidth;

public MyAdapter(Context context, ArrayList<CreateList> galleryList, RequestOptions options, RequestBuilder<Bitmap> glide, RequestManager glideMain, int imgWidth) {
this.galleryList = galleryList;
this.context = context;
this.options = options;
this.glide = glide;
this.glideMain = glideMain;
this.imgWidth = imgWidth;
}

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);
holder.img.setImageDrawable(null);
if(holder.title.getText().toString().equals("1")){

}
//holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewRecycled(holder);
}

@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
/* holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);*/
holder.img.setImageDrawable(null);
// holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewDetachedFromWindow(holder);
}

@Override
public void onViewAttachedToWindow(@NonNull final ViewHolder holder) {
/* holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
holder.handleThumbnail(view);
}
});*/
super.onViewAttachedToWindow(holder);
}

public ArrayList<CreateList> getGalleryList() {
return galleryList;
}

public void setGalleryList(ArrayList<CreateList> galleryList) {
this.galleryList = galleryList;
}

public List<Integer> getPositionList() {
return positionList;
}

public void setPositionList(List<Integer> positionList) {
this.positionList = positionList;
}

public List<String> getImageUrls() {
return imageUrls;
}

public void setImageUrls(List<String> imageUrls) {
this.imageUrls = imageUrls;
}

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

@Override
public int getItemViewType(int position) {
return position;
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_layout, viewGroup, false);

//img.setOnClickListener();

return new ViewHolder(view);
}

@Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, int pos) {


glide
.load(galleryList.get(pos).getImage_Location())
.thumbnail(0.1F)
.apply(options)
.into(viewHolder.img);

if (positionList.contains(pos)){
// view not selected
//viewHolder.parent.setBackgroundColor(Color.LTGRAY);
viewHolder.title.setVisibility(View.VISIBLE);
viewHolder.title.setText(String.valueOf(positionList.indexOf(pos)+1));
viewHolder.img.setColorFilter(viewHolder.getColorWithAlpha(Color.GREEN, 0.3f));
}
}

@Override
public int getItemCount() {
return galleryList.size();
}


public class ViewHolder extends RecyclerView.ViewHolder{
private TextView title;
private ImageView img;

public ViewHolder(View view) {
super(view);

title = (TextView)view.findViewById(R.id.highlightText);
img = (ImageView) view.findViewById(R.id.img);
/* view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleThumbnail(v);
}
});*/
/* img = view.findViewById(R.id.img);*/
img.setLayoutParams(new ConstraintLayout.LayoutParams(imgWidth, imgWidth));
/*img.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
// This is OnClick of any list Item
handleThumbnail(v);
}

});*/
/* img.setOnTouchListener(new View.OnTouchListener() {
private Rect rect;

@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
img.setColorFilter(Color.argb(50, 0, 0, 0));
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
}
if(event.getAction() == MotionEvent.ACTION_UP){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
if(event.getAction() == MotionEvent.ACTION_MOVE){
if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
}
return false;
}
});*/
}

public void handleThumbnail(View v) {
if(!imageUrls.contains(galleryList.get(getAdapterPosition()).getImage_Location())) {
if(count<3) {
positionList.add(getAdapterPosition());
String selectedImgUrl = galleryList.get(getAdapterPosition()).getImage_Location();
imageUrls.add(selectedImgUrl);
//img.setBackground(context.getResources().getDrawable(R.drawable.button_background_checked));
img.setColorFilter(getColorWithAlpha(Color.GREEN, 0.3f));
//title.setVisibility(View.VISIBLE);
//title.setText(String.valueOf(count+1));
//View viewSelected = gridRecycler.findViewHolderForAdapterPosition(position).itemView;
title.setVisibility(View.VISIBLE);
title.setText(String.valueOf(count+1));
count++;


} else {
Toast.makeText().show();
}
} else {
imageUrls.remove(galleryList.get(getAdapterPosition()).getImage_Location());
positionList.remove(new Integer(getAdapterPosition()));
//imageUrls.remove( Integer.parseInt(title.getText().toString()));
title.setVisibility(View.GONE);
count--;
// img.setBackground(context.getResources().getDrawable(R.drawable.button_background));
img.setColorFilter(Color.argb(0, 0, 0, 0));
EventBus.getDefault().post(new MessageEvent(imageUrls, positionList));
}
}

public int getColorWithAlpha(int color, float ratio) {
int newColor = 0;
int alpha = Math.round(Color.alpha(color) * ratio);
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
newColor = Color.argb(alpha, r, g, b);
return newColor;
}

}


activity code using background thread to load data and set in adapter:



private class PrepareData extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {

DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int imgWidth = (int) (displayMetrics.widthPixels*0.32);
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(imgWidth/3)
.dontAnimate()
.centerCrop()
.skipMemoryCache(true);

final RequestManager glide = Glide.with(gridRecycler.getContext());

RequestBuilder builder = glide.asBitmap();
gridRecycler.setHasFixedSize(true);
final RecyclerView.LayoutManager layoutManager = new GridLayoutManager(gridRecycler.getContext(), 3);

createLists = getAllFolderImages(this);

adapter = new MyAdapter(this, createLists, options, builder, glide, imgWidth);

gridRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
glide.resumeRequests();
}
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || newState==AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
glide.pauseRequests();
System.gc();
}
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
gridRecycler.setLayoutManager(layoutManager);
gridRecycler.setAdapter(adapter);
//put the code here that is giving exception
}
});

return null;
}
}


android profiling screen-shot, its visible that viewholder is consuming lots of memory and has lots of instances.
enter image description here










share|improve this question


















  • 4





    Remove your getItemViewType() override. As is, it's telling the RecyclerView that every single item is a different type, so it won't try to recycle any of them.

    – Mike M.
    Jan 18 at 19:09











  • @MikeM. You should post that as an answer even though it's short. I read through the code looking for a problem (and didn't notice what you spotted) because nobody had posted an answer.

    – nasch
    Jan 18 at 20:49






  • 1





    Just overriding getItemViewType() doesn't cause the items to not be recycled. In this case, it's what you're returning from getItemViewType() that's causing the problem. You're returning position, and since every item has a unique position, no two items will have the same type, so none of them get recycled. When using multiple View types, you generally only have a handful, compared to the full size of your list, so multiple items will have the same type, and can be recycled for each other.

    – Mike M.
    Jan 19 at 11:42








  • 1





    As a very simplistic example, say you want to alternate item background colors – red, black, red, black, etc. That would require two types, and you would only ever be returning one of two values from getItemViewType() – the red type, or the black type. Now, since there are multiple Views of each type, whenever a red one goes off-screen, for example, it can be recycled for the next red one coming on-screen, because it has the same type. Do you follow me?

    – Mike M.
    Jan 19 at 11:43






  • 1





    Thanks Mike, got it, thanks a lot for such a nice explanation to a noob like me.

    – ghufranne
    Jan 19 at 12:44
















0















developing an image picker using recyclerview with all images in devices for an android app, facing an issue when user scroll through images its memory usage keeps increasing eventually if lots of images there is oom exception, have searched for this issue over the internet, have applied several techniques but it is of little use, i have seen other app(whatsapp, olx) its picker scrolling is nice and no oom, i also tried dump heap in android studio to detect memory leakage, i found that there are as many viewholders as number of images, its not being recycled, am not able to find the reason, please help me in detecting the issue, attaching code..



/** adapter class for grid recyclerview*/
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<CreateList> galleryList;
private Context context;
private List<String> imageUrls = new ArrayList<>(3);
private List<Integer> positionList = new ArrayList<>(3);
int count = 0;
RequestOptions options;
RequestBuilder glide;
RequestManager glideMain;
int imgWidth;

public MyAdapter(Context context, ArrayList<CreateList> galleryList, RequestOptions options, RequestBuilder<Bitmap> glide, RequestManager glideMain, int imgWidth) {
this.galleryList = galleryList;
this.context = context;
this.options = options;
this.glide = glide;
this.glideMain = glideMain;
this.imgWidth = imgWidth;
}

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);
holder.img.setImageDrawable(null);
if(holder.title.getText().toString().equals("1")){

}
//holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewRecycled(holder);
}

@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
/* holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);*/
holder.img.setImageDrawable(null);
// holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewDetachedFromWindow(holder);
}

@Override
public void onViewAttachedToWindow(@NonNull final ViewHolder holder) {
/* holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
holder.handleThumbnail(view);
}
});*/
super.onViewAttachedToWindow(holder);
}

public ArrayList<CreateList> getGalleryList() {
return galleryList;
}

public void setGalleryList(ArrayList<CreateList> galleryList) {
this.galleryList = galleryList;
}

public List<Integer> getPositionList() {
return positionList;
}

public void setPositionList(List<Integer> positionList) {
this.positionList = positionList;
}

public List<String> getImageUrls() {
return imageUrls;
}

public void setImageUrls(List<String> imageUrls) {
this.imageUrls = imageUrls;
}

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

@Override
public int getItemViewType(int position) {
return position;
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_layout, viewGroup, false);

//img.setOnClickListener();

return new ViewHolder(view);
}

@Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, int pos) {


glide
.load(galleryList.get(pos).getImage_Location())
.thumbnail(0.1F)
.apply(options)
.into(viewHolder.img);

if (positionList.contains(pos)){
// view not selected
//viewHolder.parent.setBackgroundColor(Color.LTGRAY);
viewHolder.title.setVisibility(View.VISIBLE);
viewHolder.title.setText(String.valueOf(positionList.indexOf(pos)+1));
viewHolder.img.setColorFilter(viewHolder.getColorWithAlpha(Color.GREEN, 0.3f));
}
}

@Override
public int getItemCount() {
return galleryList.size();
}


public class ViewHolder extends RecyclerView.ViewHolder{
private TextView title;
private ImageView img;

public ViewHolder(View view) {
super(view);

title = (TextView)view.findViewById(R.id.highlightText);
img = (ImageView) view.findViewById(R.id.img);
/* view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleThumbnail(v);
}
});*/
/* img = view.findViewById(R.id.img);*/
img.setLayoutParams(new ConstraintLayout.LayoutParams(imgWidth, imgWidth));
/*img.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
// This is OnClick of any list Item
handleThumbnail(v);
}

});*/
/* img.setOnTouchListener(new View.OnTouchListener() {
private Rect rect;

@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
img.setColorFilter(Color.argb(50, 0, 0, 0));
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
}
if(event.getAction() == MotionEvent.ACTION_UP){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
if(event.getAction() == MotionEvent.ACTION_MOVE){
if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
}
return false;
}
});*/
}

public void handleThumbnail(View v) {
if(!imageUrls.contains(galleryList.get(getAdapterPosition()).getImage_Location())) {
if(count<3) {
positionList.add(getAdapterPosition());
String selectedImgUrl = galleryList.get(getAdapterPosition()).getImage_Location();
imageUrls.add(selectedImgUrl);
//img.setBackground(context.getResources().getDrawable(R.drawable.button_background_checked));
img.setColorFilter(getColorWithAlpha(Color.GREEN, 0.3f));
//title.setVisibility(View.VISIBLE);
//title.setText(String.valueOf(count+1));
//View viewSelected = gridRecycler.findViewHolderForAdapterPosition(position).itemView;
title.setVisibility(View.VISIBLE);
title.setText(String.valueOf(count+1));
count++;


} else {
Toast.makeText().show();
}
} else {
imageUrls.remove(galleryList.get(getAdapterPosition()).getImage_Location());
positionList.remove(new Integer(getAdapterPosition()));
//imageUrls.remove( Integer.parseInt(title.getText().toString()));
title.setVisibility(View.GONE);
count--;
// img.setBackground(context.getResources().getDrawable(R.drawable.button_background));
img.setColorFilter(Color.argb(0, 0, 0, 0));
EventBus.getDefault().post(new MessageEvent(imageUrls, positionList));
}
}

public int getColorWithAlpha(int color, float ratio) {
int newColor = 0;
int alpha = Math.round(Color.alpha(color) * ratio);
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
newColor = Color.argb(alpha, r, g, b);
return newColor;
}

}


activity code using background thread to load data and set in adapter:



private class PrepareData extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {

DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int imgWidth = (int) (displayMetrics.widthPixels*0.32);
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(imgWidth/3)
.dontAnimate()
.centerCrop()
.skipMemoryCache(true);

final RequestManager glide = Glide.with(gridRecycler.getContext());

RequestBuilder builder = glide.asBitmap();
gridRecycler.setHasFixedSize(true);
final RecyclerView.LayoutManager layoutManager = new GridLayoutManager(gridRecycler.getContext(), 3);

createLists = getAllFolderImages(this);

adapter = new MyAdapter(this, createLists, options, builder, glide, imgWidth);

gridRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
glide.resumeRequests();
}
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || newState==AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
glide.pauseRequests();
System.gc();
}
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
gridRecycler.setLayoutManager(layoutManager);
gridRecycler.setAdapter(adapter);
//put the code here that is giving exception
}
});

return null;
}
}


android profiling screen-shot, its visible that viewholder is consuming lots of memory and has lots of instances.
enter image description here










share|improve this question


















  • 4





    Remove your getItemViewType() override. As is, it's telling the RecyclerView that every single item is a different type, so it won't try to recycle any of them.

    – Mike M.
    Jan 18 at 19:09











  • @MikeM. You should post that as an answer even though it's short. I read through the code looking for a problem (and didn't notice what you spotted) because nobody had posted an answer.

    – nasch
    Jan 18 at 20:49






  • 1





    Just overriding getItemViewType() doesn't cause the items to not be recycled. In this case, it's what you're returning from getItemViewType() that's causing the problem. You're returning position, and since every item has a unique position, no two items will have the same type, so none of them get recycled. When using multiple View types, you generally only have a handful, compared to the full size of your list, so multiple items will have the same type, and can be recycled for each other.

    – Mike M.
    Jan 19 at 11:42








  • 1





    As a very simplistic example, say you want to alternate item background colors – red, black, red, black, etc. That would require two types, and you would only ever be returning one of two values from getItemViewType() – the red type, or the black type. Now, since there are multiple Views of each type, whenever a red one goes off-screen, for example, it can be recycled for the next red one coming on-screen, because it has the same type. Do you follow me?

    – Mike M.
    Jan 19 at 11:43






  • 1





    Thanks Mike, got it, thanks a lot for such a nice explanation to a noob like me.

    – ghufranne
    Jan 19 at 12:44














0












0








0








developing an image picker using recyclerview with all images in devices for an android app, facing an issue when user scroll through images its memory usage keeps increasing eventually if lots of images there is oom exception, have searched for this issue over the internet, have applied several techniques but it is of little use, i have seen other app(whatsapp, olx) its picker scrolling is nice and no oom, i also tried dump heap in android studio to detect memory leakage, i found that there are as many viewholders as number of images, its not being recycled, am not able to find the reason, please help me in detecting the issue, attaching code..



/** adapter class for grid recyclerview*/
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<CreateList> galleryList;
private Context context;
private List<String> imageUrls = new ArrayList<>(3);
private List<Integer> positionList = new ArrayList<>(3);
int count = 0;
RequestOptions options;
RequestBuilder glide;
RequestManager glideMain;
int imgWidth;

public MyAdapter(Context context, ArrayList<CreateList> galleryList, RequestOptions options, RequestBuilder<Bitmap> glide, RequestManager glideMain, int imgWidth) {
this.galleryList = galleryList;
this.context = context;
this.options = options;
this.glide = glide;
this.glideMain = glideMain;
this.imgWidth = imgWidth;
}

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);
holder.img.setImageDrawable(null);
if(holder.title.getText().toString().equals("1")){

}
//holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewRecycled(holder);
}

@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
/* holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);*/
holder.img.setImageDrawable(null);
// holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewDetachedFromWindow(holder);
}

@Override
public void onViewAttachedToWindow(@NonNull final ViewHolder holder) {
/* holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
holder.handleThumbnail(view);
}
});*/
super.onViewAttachedToWindow(holder);
}

public ArrayList<CreateList> getGalleryList() {
return galleryList;
}

public void setGalleryList(ArrayList<CreateList> galleryList) {
this.galleryList = galleryList;
}

public List<Integer> getPositionList() {
return positionList;
}

public void setPositionList(List<Integer> positionList) {
this.positionList = positionList;
}

public List<String> getImageUrls() {
return imageUrls;
}

public void setImageUrls(List<String> imageUrls) {
this.imageUrls = imageUrls;
}

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

@Override
public int getItemViewType(int position) {
return position;
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_layout, viewGroup, false);

//img.setOnClickListener();

return new ViewHolder(view);
}

@Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, int pos) {


glide
.load(galleryList.get(pos).getImage_Location())
.thumbnail(0.1F)
.apply(options)
.into(viewHolder.img);

if (positionList.contains(pos)){
// view not selected
//viewHolder.parent.setBackgroundColor(Color.LTGRAY);
viewHolder.title.setVisibility(View.VISIBLE);
viewHolder.title.setText(String.valueOf(positionList.indexOf(pos)+1));
viewHolder.img.setColorFilter(viewHolder.getColorWithAlpha(Color.GREEN, 0.3f));
}
}

@Override
public int getItemCount() {
return galleryList.size();
}


public class ViewHolder extends RecyclerView.ViewHolder{
private TextView title;
private ImageView img;

public ViewHolder(View view) {
super(view);

title = (TextView)view.findViewById(R.id.highlightText);
img = (ImageView) view.findViewById(R.id.img);
/* view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleThumbnail(v);
}
});*/
/* img = view.findViewById(R.id.img);*/
img.setLayoutParams(new ConstraintLayout.LayoutParams(imgWidth, imgWidth));
/*img.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
// This is OnClick of any list Item
handleThumbnail(v);
}

});*/
/* img.setOnTouchListener(new View.OnTouchListener() {
private Rect rect;

@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
img.setColorFilter(Color.argb(50, 0, 0, 0));
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
}
if(event.getAction() == MotionEvent.ACTION_UP){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
if(event.getAction() == MotionEvent.ACTION_MOVE){
if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
}
return false;
}
});*/
}

public void handleThumbnail(View v) {
if(!imageUrls.contains(galleryList.get(getAdapterPosition()).getImage_Location())) {
if(count<3) {
positionList.add(getAdapterPosition());
String selectedImgUrl = galleryList.get(getAdapterPosition()).getImage_Location();
imageUrls.add(selectedImgUrl);
//img.setBackground(context.getResources().getDrawable(R.drawable.button_background_checked));
img.setColorFilter(getColorWithAlpha(Color.GREEN, 0.3f));
//title.setVisibility(View.VISIBLE);
//title.setText(String.valueOf(count+1));
//View viewSelected = gridRecycler.findViewHolderForAdapterPosition(position).itemView;
title.setVisibility(View.VISIBLE);
title.setText(String.valueOf(count+1));
count++;


} else {
Toast.makeText().show();
}
} else {
imageUrls.remove(galleryList.get(getAdapterPosition()).getImage_Location());
positionList.remove(new Integer(getAdapterPosition()));
//imageUrls.remove( Integer.parseInt(title.getText().toString()));
title.setVisibility(View.GONE);
count--;
// img.setBackground(context.getResources().getDrawable(R.drawable.button_background));
img.setColorFilter(Color.argb(0, 0, 0, 0));
EventBus.getDefault().post(new MessageEvent(imageUrls, positionList));
}
}

public int getColorWithAlpha(int color, float ratio) {
int newColor = 0;
int alpha = Math.round(Color.alpha(color) * ratio);
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
newColor = Color.argb(alpha, r, g, b);
return newColor;
}

}


activity code using background thread to load data and set in adapter:



private class PrepareData extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {

DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int imgWidth = (int) (displayMetrics.widthPixels*0.32);
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(imgWidth/3)
.dontAnimate()
.centerCrop()
.skipMemoryCache(true);

final RequestManager glide = Glide.with(gridRecycler.getContext());

RequestBuilder builder = glide.asBitmap();
gridRecycler.setHasFixedSize(true);
final RecyclerView.LayoutManager layoutManager = new GridLayoutManager(gridRecycler.getContext(), 3);

createLists = getAllFolderImages(this);

adapter = new MyAdapter(this, createLists, options, builder, glide, imgWidth);

gridRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
glide.resumeRequests();
}
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || newState==AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
glide.pauseRequests();
System.gc();
}
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
gridRecycler.setLayoutManager(layoutManager);
gridRecycler.setAdapter(adapter);
//put the code here that is giving exception
}
});

return null;
}
}


android profiling screen-shot, its visible that viewholder is consuming lots of memory and has lots of instances.
enter image description here










share|improve this question














developing an image picker using recyclerview with all images in devices for an android app, facing an issue when user scroll through images its memory usage keeps increasing eventually if lots of images there is oom exception, have searched for this issue over the internet, have applied several techniques but it is of little use, i have seen other app(whatsapp, olx) its picker scrolling is nice and no oom, i also tried dump heap in android studio to detect memory leakage, i found that there are as many viewholders as number of images, its not being recycled, am not able to find the reason, please help me in detecting the issue, attaching code..



/** adapter class for grid recyclerview*/
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<CreateList> galleryList;
private Context context;
private List<String> imageUrls = new ArrayList<>(3);
private List<Integer> positionList = new ArrayList<>(3);
int count = 0;
RequestOptions options;
RequestBuilder glide;
RequestManager glideMain;
int imgWidth;

public MyAdapter(Context context, ArrayList<CreateList> galleryList, RequestOptions options, RequestBuilder<Bitmap> glide, RequestManager glideMain, int imgWidth) {
this.galleryList = galleryList;
this.context = context;
this.options = options;
this.glide = glide;
this.glideMain = glideMain;
this.imgWidth = imgWidth;
}

@Override
public void onViewRecycled(@NonNull ViewHolder holder) {
holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);
holder.img.setImageDrawable(null);
if(holder.title.getText().toString().equals("1")){

}
//holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewRecycled(holder);
}

@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
/* holder.img.setOnClickListener(null);
holder.img.setColorFilter(null);*/
holder.img.setImageDrawable(null);
// holder.itemView.setOnClickListener(null);
glideMain.clear(holder.img);
super.onViewDetachedFromWindow(holder);
}

@Override
public void onViewAttachedToWindow(@NonNull final ViewHolder holder) {
/* holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
holder.handleThumbnail(view);
}
});*/
super.onViewAttachedToWindow(holder);
}

public ArrayList<CreateList> getGalleryList() {
return galleryList;
}

public void setGalleryList(ArrayList<CreateList> galleryList) {
this.galleryList = galleryList;
}

public List<Integer> getPositionList() {
return positionList;
}

public void setPositionList(List<Integer> positionList) {
this.positionList = positionList;
}

public List<String> getImageUrls() {
return imageUrls;
}

public void setImageUrls(List<String> imageUrls) {
this.imageUrls = imageUrls;
}

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

@Override
public int getItemViewType(int position) {
return position;
}
@Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.cell_layout, viewGroup, false);

//img.setOnClickListener();

return new ViewHolder(view);
}

@Override
public void onBindViewHolder(final MyAdapter.ViewHolder viewHolder, int pos) {


glide
.load(galleryList.get(pos).getImage_Location())
.thumbnail(0.1F)
.apply(options)
.into(viewHolder.img);

if (positionList.contains(pos)){
// view not selected
//viewHolder.parent.setBackgroundColor(Color.LTGRAY);
viewHolder.title.setVisibility(View.VISIBLE);
viewHolder.title.setText(String.valueOf(positionList.indexOf(pos)+1));
viewHolder.img.setColorFilter(viewHolder.getColorWithAlpha(Color.GREEN, 0.3f));
}
}

@Override
public int getItemCount() {
return galleryList.size();
}


public class ViewHolder extends RecyclerView.ViewHolder{
private TextView title;
private ImageView img;

public ViewHolder(View view) {
super(view);

title = (TextView)view.findViewById(R.id.highlightText);
img = (ImageView) view.findViewById(R.id.img);
/* view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handleThumbnail(v);
}
});*/
/* img = view.findViewById(R.id.img);*/
img.setLayoutParams(new ConstraintLayout.LayoutParams(imgWidth, imgWidth));
/*img.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
// This is OnClick of any list Item
handleThumbnail(v);
}

});*/
/* img.setOnTouchListener(new View.OnTouchListener() {
private Rect rect;

@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
img.setColorFilter(Color.argb(50, 0, 0, 0));
rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
}
if(event.getAction() == MotionEvent.ACTION_UP){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
if(event.getAction() == MotionEvent.ACTION_MOVE){
if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
img.setColorFilter(Color.argb(0, 0, 0, 0));
}
}
return false;
}
});*/
}

public void handleThumbnail(View v) {
if(!imageUrls.contains(galleryList.get(getAdapterPosition()).getImage_Location())) {
if(count<3) {
positionList.add(getAdapterPosition());
String selectedImgUrl = galleryList.get(getAdapterPosition()).getImage_Location();
imageUrls.add(selectedImgUrl);
//img.setBackground(context.getResources().getDrawable(R.drawable.button_background_checked));
img.setColorFilter(getColorWithAlpha(Color.GREEN, 0.3f));
//title.setVisibility(View.VISIBLE);
//title.setText(String.valueOf(count+1));
//View viewSelected = gridRecycler.findViewHolderForAdapterPosition(position).itemView;
title.setVisibility(View.VISIBLE);
title.setText(String.valueOf(count+1));
count++;


} else {
Toast.makeText().show();
}
} else {
imageUrls.remove(galleryList.get(getAdapterPosition()).getImage_Location());
positionList.remove(new Integer(getAdapterPosition()));
//imageUrls.remove( Integer.parseInt(title.getText().toString()));
title.setVisibility(View.GONE);
count--;
// img.setBackground(context.getResources().getDrawable(R.drawable.button_background));
img.setColorFilter(Color.argb(0, 0, 0, 0));
EventBus.getDefault().post(new MessageEvent(imageUrls, positionList));
}
}

public int getColorWithAlpha(int color, float ratio) {
int newColor = 0;
int alpha = Math.round(Color.alpha(color) * ratio);
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
newColor = Color.argb(alpha, r, g, b);
return newColor;
}

}


activity code using background thread to load data and set in adapter:



private class PrepareData extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {

DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int imgWidth = (int) (displayMetrics.widthPixels*0.32);
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(imgWidth/3)
.dontAnimate()
.centerCrop()
.skipMemoryCache(true);

final RequestManager glide = Glide.with(gridRecycler.getContext());

RequestBuilder builder = glide.asBitmap();
gridRecycler.setHasFixedSize(true);
final RecyclerView.LayoutManager layoutManager = new GridLayoutManager(gridRecycler.getContext(), 3);

createLists = getAllFolderImages(this);

adapter = new MyAdapter(this, createLists, options, builder, glide, imgWidth);

gridRecycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}

@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
glide.resumeRequests();
}
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || newState==AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
glide.pauseRequests();
System.gc();
}
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
gridRecycler.setLayoutManager(layoutManager);
gridRecycler.setAdapter(adapter);
//put the code here that is giving exception
}
});

return null;
}
}


android profiling screen-shot, its visible that viewholder is consuming lots of memory and has lots of instances.
enter image description here







android memory-management android-recyclerview memory-leaks out-of-memory






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Jan 18 at 19:02









ghufranneghufranne

146313




146313








  • 4





    Remove your getItemViewType() override. As is, it's telling the RecyclerView that every single item is a different type, so it won't try to recycle any of them.

    – Mike M.
    Jan 18 at 19:09











  • @MikeM. You should post that as an answer even though it's short. I read through the code looking for a problem (and didn't notice what you spotted) because nobody had posted an answer.

    – nasch
    Jan 18 at 20:49






  • 1





    Just overriding getItemViewType() doesn't cause the items to not be recycled. In this case, it's what you're returning from getItemViewType() that's causing the problem. You're returning position, and since every item has a unique position, no two items will have the same type, so none of them get recycled. When using multiple View types, you generally only have a handful, compared to the full size of your list, so multiple items will have the same type, and can be recycled for each other.

    – Mike M.
    Jan 19 at 11:42








  • 1





    As a very simplistic example, say you want to alternate item background colors – red, black, red, black, etc. That would require two types, and you would only ever be returning one of two values from getItemViewType() – the red type, or the black type. Now, since there are multiple Views of each type, whenever a red one goes off-screen, for example, it can be recycled for the next red one coming on-screen, because it has the same type. Do you follow me?

    – Mike M.
    Jan 19 at 11:43






  • 1





    Thanks Mike, got it, thanks a lot for such a nice explanation to a noob like me.

    – ghufranne
    Jan 19 at 12:44














  • 4





    Remove your getItemViewType() override. As is, it's telling the RecyclerView that every single item is a different type, so it won't try to recycle any of them.

    – Mike M.
    Jan 18 at 19:09











  • @MikeM. You should post that as an answer even though it's short. I read through the code looking for a problem (and didn't notice what you spotted) because nobody had posted an answer.

    – nasch
    Jan 18 at 20:49






  • 1





    Just overriding getItemViewType() doesn't cause the items to not be recycled. In this case, it's what you're returning from getItemViewType() that's causing the problem. You're returning position, and since every item has a unique position, no two items will have the same type, so none of them get recycled. When using multiple View types, you generally only have a handful, compared to the full size of your list, so multiple items will have the same type, and can be recycled for each other.

    – Mike M.
    Jan 19 at 11:42








  • 1





    As a very simplistic example, say you want to alternate item background colors – red, black, red, black, etc. That would require two types, and you would only ever be returning one of two values from getItemViewType() – the red type, or the black type. Now, since there are multiple Views of each type, whenever a red one goes off-screen, for example, it can be recycled for the next red one coming on-screen, because it has the same type. Do you follow me?

    – Mike M.
    Jan 19 at 11:43






  • 1





    Thanks Mike, got it, thanks a lot for such a nice explanation to a noob like me.

    – ghufranne
    Jan 19 at 12:44








4




4





Remove your getItemViewType() override. As is, it's telling the RecyclerView that every single item is a different type, so it won't try to recycle any of them.

– Mike M.
Jan 18 at 19:09





Remove your getItemViewType() override. As is, it's telling the RecyclerView that every single item is a different type, so it won't try to recycle any of them.

– Mike M.
Jan 18 at 19:09













@MikeM. You should post that as an answer even though it's short. I read through the code looking for a problem (and didn't notice what you spotted) because nobody had posted an answer.

– nasch
Jan 18 at 20:49





@MikeM. You should post that as an answer even though it's short. I read through the code looking for a problem (and didn't notice what you spotted) because nobody had posted an answer.

– nasch
Jan 18 at 20:49




1




1





Just overriding getItemViewType() doesn't cause the items to not be recycled. In this case, it's what you're returning from getItemViewType() that's causing the problem. You're returning position, and since every item has a unique position, no two items will have the same type, so none of them get recycled. When using multiple View types, you generally only have a handful, compared to the full size of your list, so multiple items will have the same type, and can be recycled for each other.

– Mike M.
Jan 19 at 11:42







Just overriding getItemViewType() doesn't cause the items to not be recycled. In this case, it's what you're returning from getItemViewType() that's causing the problem. You're returning position, and since every item has a unique position, no two items will have the same type, so none of them get recycled. When using multiple View types, you generally only have a handful, compared to the full size of your list, so multiple items will have the same type, and can be recycled for each other.

– Mike M.
Jan 19 at 11:42






1




1





As a very simplistic example, say you want to alternate item background colors – red, black, red, black, etc. That would require two types, and you would only ever be returning one of two values from getItemViewType() – the red type, or the black type. Now, since there are multiple Views of each type, whenever a red one goes off-screen, for example, it can be recycled for the next red one coming on-screen, because it has the same type. Do you follow me?

– Mike M.
Jan 19 at 11:43





As a very simplistic example, say you want to alternate item background colors – red, black, red, black, etc. That would require two types, and you would only ever be returning one of two values from getItemViewType() – the red type, or the black type. Now, since there are multiple Views of each type, whenever a red one goes off-screen, for example, it can be recycled for the next red one coming on-screen, because it has the same type. Do you follow me?

– Mike M.
Jan 19 at 11:43




1




1





Thanks Mike, got it, thanks a lot for such a nice explanation to a noob like me.

– ghufranne
Jan 19 at 12:44





Thanks Mike, got it, thanks a lot for such a nice explanation to a noob like me.

– ghufranne
Jan 19 at 12:44












0






active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54260004%2frecyclerview-vieholder-is-not-getting-recycled%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54260004%2frecyclerview-vieholder-is-not-getting-recycled%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Liquibase includeAll doesn't find base path

How to use setInterval in EJS file?

Petrus Granier-Deferre