In my app I'm using model class containing a Bitmap for displaying an image on recyclerView
public class ImageModelClass {
Bitmap image;
public void setImage(Bitmap image) {
this.image = image;
}
public Bitmap getImage() {
return image;
}
Problem is when i am using Bitmap directly from my model class then this error is throwing Error, cannot access an invalid/free'd bitmap here!
and when i replace the Bitmap Class to a String in my model class and using encoded bitmap i converted bitmap Image into a String then this error isn't coming .
But encoding a Bitmap is taking lot of time load images.
public class ImageCollection extends AppCompatActivity {
public static final String IMAGE_SHARED_PREFS = "com.example.animproject_IMAGE_SHARED_PREFS";
public static final String IMAGE_DATA_KEY = "com.example.animproject_IMAGE_DATA_KEY";
private static final int REQUEST_CODE = 1;
RecyclerView recyclerView;
ImageCollectionAdapter adapter;
List<ImageModelClass> imageList;
FloatingActionButton fab;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_collection);
loadAlbumData();
recyclerView = findViewById(R.id.imageCollectionRecyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
adapter = new ImageCollectionAdapter(this,imageList);
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, GridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
recyclerView.setAdapter(adapter);
fab = findViewById(R.id.fabButton);
fab.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onClick(View view) {
Intent gallery = new Intent(Intent.ACTION_PICK);
gallery.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
gallery.setType("image/*");
startActivityForResult(gallery, REQUEST_CODE);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
assert data != null;
ClipData clipData = data.getClipData();
if (clipData != null) {
for (int i = 0; i < clipData.getItemCount(); i++) {
Uri imageUri = clipData.getItemAt(i).getUri();
InputStream inputStream;
try {
inputStream = getContentResolver().openInputStream(imageUri);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
ImageModelClass imageModelClass = new ImageModelClass();
imageModelClass.setImage(bitmap);
imageList.add(imageModelClass);
adapter.notifyDataSetChanged();
saveGalleryData();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
} else {
Uri imageUri = data.getData();
InputStream inputStream = null;
try {
assert imageUri != null;
inputStream = getContentResolver().openInputStream(imageUri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
ImageModelClass imageModelClass = new ImageModelClass();
imageModelClass.setImage(bitmap);
imageList.add(imageModelClass);
adapter.notifyDataSetChanged();
saveGalleryData();
}
}
}
private void saveGalleryData() {
SharedPreferences preferences = getSharedPreferences(IMAGE_SHARED_PREFS, MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
Gson gson = new Gson();
String json = gson.toJson(imageList);
editor.putString(IMAGE_DATA_KEY, json);
editor.apply();
}
private void loadAlbumData() {
SharedPreferences preferences = getSharedPreferences(IMAGE_SHARED_PREFS, MODE_PRIVATE);
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<ImageModelClass>>() {
}.getType();
String data = preferences.getString(IMAGE_DATA_KEY, null);
imageList = gson.fromJson(data, type);
if (imageList == null) {
imageList = new ArrayList<>();
}
}
}
Adapter
public class ImageCollectionAdapter extends RecyclerView.Adapter<ImageCollectionAdapter.MyViewHolder> {
List<ImageModelClass> list;
Context context;
public ImageCollectionAdapter(Context context,List<ImageModelClass> list) {
this.list = list;
this.context = context ;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new MyViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.image_collection_lists, parent, false));
}
@Override
public void onBindViewHolder(@NonNull
MyViewHolder holder, int position) {
ImageModelClass currentImage =
list.get(position);
Glide
.With(context.getApplicationContext)
.load(currentImages.getImages)
/* getting bitmap images from model
* class
*/
.into(holder.imageView);
}
@Override
public int getItemCount() {
return list.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
RoundedImageView imageView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.galleryPicture);
}
}
}
please help it will be appreciatable :)
I was facing the same issue when I was trying to get the bitmap from the textView. The textView was in the RecyclerView inside a bottom sheet. When I clicked on textView I was getting the bitmap and dismissing the bottom sheet.
I got the bitmap from the drawing cache of the textView. I checked it using the debugger the bitmap was retrieved correctly. But when I passed it to callback (from recyclerView's adapter to activity), the bitmap in the callback is not shown in the debugger and I got the error 'cannot access an invalid/free'd bitmap here!'.
As of my understanding, When textView is not shown/rendered on the screen (In my case when I dismissed the bottom sheet) the cached bitmap becomes unavailable. Gotta read the relevant docs to know the exact cause of the problem.
What I did to solve this issue is to create a copy of the bitmap and pass it to the callback and It worked!
You can copy the bitmap like this:
Bitmap copyBitmap = bitmap.copy(bitmap.getConfig(), false);
The full code of retrieving the bitmap and creating new one:
holder.textView.setDrawingCacheEnabled(true);
holder.textView.buildDrawingCache();
Bitmap bitmap = holder.textView.getDrawingCache();
Bitmap copyBitmap = bitmap.copy(bitmap.getConfig(), false);
holder.textView.setDrawingCacheEnabled(false);
callback.onWidgetSelected(copyBitmap);
I hope you'll find it helpful. I'll make sure to study the docs as well and will update the answer when I find the cause of this problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With