how to recyle the bitmap which runs multi-thread - android

the activity displays the bitmap which load from the internet ,when I slip the ImageView,another bitmap will occur.I set the bitmap to ImageView and after recycle it, sometimes,the error has occurs.
the code:
mImageView.setImageBitmap(loadedBitmap);
if(loadedBitmap!=null && !loadedBitmap.isRecycled()){
loadedBitmap.recycle();
loadedBitmap=null;
}
It runs multi-thread,I can not put the operation of Bitmpa recycle ahead of setImageBitmap.
how should I do?

Well basically you cannot recycle a bitmap that you're currently using in your ImageView.
If you want to swap the bitmap in your ImageView then I would do it like this:
I would keep a reference to the Bitmap (in for example mLoadedBitmap), and when you download another one you do something like this:
final Bitmap oldBitmap = mLoadedBitmap;
mLoadedBitmap = downloadedBitmap;
mImageView.setImageBitmap(mLoadedBitmap);
if(oldBitmap!=null && !oldBitmap.isRecycled()){
oldBitmap.recycle();
}

You can write a multi-threaded application but do all the UI updates from the main thread.

Related

Reinitialize recycled Bitmap Android

What I'm trying to do is to reuse a Bitmap after recycling it. For doing that, I know that I have to initialize de Bitmap again, I'm doing it this way after calling recycle():
mapBitmap = Bitmap.createBitmap(map, 0, 0, map.getWidth(), map.getHeight());
but when I try to use it, i get
06-12 20:41:01.628 615-1470/com.example.project W/System.err: java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap#5f1fba3
By the way, I have another Bitmap which I have to recycle and use later on, but this one works perfectly, the only difference between them is that I'm initiallizating this one using decodeFile() as follows:
bm = BitmapFactory.decodeFile(url);
Your problem is that you are using a recycled Bitmap to initialize another one.
In this line:
mapBitmap = Bitmap.createBitmap(map, 0, 0, map.getWidth(), map.getHeight());
you are using the map object which is a recyled Bitmap, and you can't create a new Bitmap using a recycled one, be sure to initialize it properly before calling Bitmap.createBitmap(map, ...) or don't call map.recycle() somewhere in your code before you have finished using it.

Why doesn't bitmap data from TextureView.getBitmap update in an ImageView?

I have a piece of code that displays camera preview in a TextureView. Once in a while (let's say the user presses a button) I obtain a bitmap from the TextureView and set it for display in an ImageView, like this:
private void freezeFrame() {
textureView.getBitmap(freezeBitmap);
imageView.setImageBitmap(freezeBitmap);
}
The freezeBitmap is pre-allocated and reused on multiple calls to the above method.
The problem is that the ImageView keeps showing the state of the bitmap from the first call to freezeFrame(). Calling imageView.invalidate() doesn't help.
The problem disappears when I do one of the following:
set imageView to use a software layer (this leads me to believe the bitmap is correctly obtained from the textureView but not uploaded to the GPU in hardware mode),
create a new bitmap on each call to getBitmap(),
modify the bitmap, e.g. with freezeBitmap.setPixel(0, 0, 0).
So I wonder: why doesn't ImageView "notice" that the bitmap has been updated by TextureView.getBitmap yet it does notice the change performed by setPixel()?
Is there a good way to force the update?

BitmapFun sample has a caching issue

I am using the Android BitmapFun sample code to manage bitmaps in my application. I have been experiencing garbled or duplicated images in a ViewPager. I have tracked this down to the following code in ImageCache.java:
/**
* Notify the removed entry that is no longer being cached
*/
#Override
protected void entryRemoved(boolean evicted, String key,
BitmapDrawable oldValue, BitmapDrawable newValue) {
if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
// The removed entry is a recycling drawable, so notify it
// that it has been removed from the memory cache
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
} else {
// The removed entry is a standard BitmapDrawable
if (Utils.hasHoneycomb()) {
// We're running on Honeycomb or later, so add the bitmap
// to a SoftRefrence set for possible use with inBitmap later
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
}
}
}
The bitmap is added to the reusable bitmap list when it is removed from the cache. In this case the bitmap is still in use by a ViewPager view. When a later view is created the bitmap (still in use) is reused and the bitmap appears in two positions in the ViewPager.
A bitmap that is removed from the LruCache isn't necessarily available for reuse. I have disabled the reuse of bitmaps in this code and am no longer having an issue. This problem doesn't occur with lower resolution images because the bitmaps aren't removed from the cache while in the range of the ViewPager's offscreen limit. I don't have an issue with 60 DPI images but see this issue frequently at 160 DPI. I think this would show up in the original BitmapFun sample with higher resolution images.
Anyone else experienced this problem or I am not understanding the issue properly?
Kevin
What I think the problem with the code is in the line
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
That line adds a bitmap that was removed from LRU cache to a reusable bitmap set to be used for inBitmap re-use. It doesn't check whether it is still being used by an ImageView or not. If you try to re-use a bitmap that is still being used by an ImageView, the underlying bitmap will be replaced with another bitmap making it not valid anymore. My suggestion is to track whether a bitmap is still being used by an ImageView before adding it to the reusable bitmap set. I've created a sample github project for this issue. Tell me what you think with my solution.

Is it safe to call native function that locks pixels of Bitmap from non UI thread?

I'm implementing list that contains preview of different image filters (grayscale, sepia, etc.)
I want to move image processing out of UI thread, but I'm not sure is it safe. For example, when I'm calling AndroidBitmap_lockPixels for Bitmap of ImageView what will happen if UI thread will try to redraw ImageVIew?
EXAMPLE
public void someMethod(){
ImageView mImageView = /*initialization*/
final Bitmap bmp = ((BitmapDrawable) mImageView.getDrawable()).getBitmap();
new Thread(new Runnable(){
#Override
public void run(){
applyFilter(bmp);
}
}).start();
}
public native void applyFilter(Bitmap bmp);
I haven't tried this case yet; however, you're trying to modify the current being-used Bitmap on ImageView. I think when AndroidBitmap_lockPixels is called, some abnormal will happen, like crash on ImageView while trying to invalidate or something similar to Access Violation.
You can make a copy of the image, process it then apply the new Bitmap to ImageView; it's totally safe.
In that case, there's a chance that the Bitmap is directly referencing something in that ImageView. Otherwise, the Bitmap becomes just another set of 1s and 0s. You might be able to make a copy of the Bitmap and update the ImageView with the new image once you are done. Be careful about images on devices before Honeycomb (3.0) because they handle bitmaps weird, and you have to manage them almost like you would in a C++ program.
It shouldn't, but there's sometimes hidden things in Android, so watch out.
Update: there might be a way to modify the image without copying, but I'm basing this more off general graphics and not Android-specific

How to recycle Bitmaps in gridview adapter?

I have a Grid View in my android app.I am loading images to the Grid view from server.I am using lazy loading. I have to recycle all bitmaps created here.How to do Bitmap.recycle() in Adapter or Grid view. I am getting out of memory, please help me.
You will need to show some code (your adapter at a minimum). If you're getting out of memory errors, you probably are not implementing view recycling correctly, or otherwise have a memory leak. Its also possible that you're simply loading too many large bitmaps at once, but if you can load the view at all, its much more likely you have a memory leak.
No one is going to be able to track down a memory leak without looking at some code. Bitmap.recycle() is not a solution, the garbage collector will work well enough without it if the rest of your code is ok.
See: http://www.youtube.com/watch?v=_CruQY55HOk for a great talk on managing memory in android and finding memory leaks.
Also try: http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html for an example of how to download or otherwise correctly asynchronously load images into a list like view.
Edit: also check out an image loading library I wrote, so you don't need to worry about any of this: https://github.com/bumptech/glide
Check out the Displaying Bitmaps Efficiently Android Training class. It has a lesson, Displaying Bitmaps in your UI, that covers displaying bitmaps in a GridView using a background thread and a memory and disk cache.
There is a really simple way that works very well:
First, you must create a custom ImageView like this:
public class ImageViewRecyclable extends ImageView
{
private Bitmap bitmap;
public ImageViewRecyclable(Context context)
{
super(context);
}
#Override
public void setImageBitmap(Bitmap bm)
{
super.setImageBitmap(bm);
if (bitmap != null) bitmap.recycle();
this.bitmap = bm;
}
}
Our ImageViewRecyclable keep a pointer to the bitmap and recycle the old one before setting the new one.
Second, you must check in the getView of the adapter to see if convertView is null or not. if it's not null cast to our custom ImageViewRecyclable and set the bitmap on it. this way the old bitmap is recycled before setting the new one.
This is the getView code of the adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ImageViewRecyclable imageView = (convertView == null) ? new ImageViewRecyclable(ActivityMain.this) : (ImageViewRecyclable) convertView;
byte[] bytes = ....
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
imageView.setImageBitmap(bitmap);
return imageView;
}
I tested this code with 1000 100x100 images. The original ImageView failed after showing 50 image with memory error but this code works very well until the end of the grid.
This code works very great for small offline images, but online and large images need caching and other things. This may be useful but some changes must be applied.

Categories

Resources