In my app I want to swap images at runtime when user clicks on it.
there are two imageviews when user click on first image and then click on second image at the same time I'm fetching bitmap of first imageview's image and assigning to second imageview for this I used following code:
public Bitmap createBitmap(ImageView imageview) {
imageview.setDrawingCacheEnabled(true);
imageview.buildDrawingCache(false);
if(imageview.getDrawingCache() != null) {
Bitmap bitmap = Bitmap.createBitmap(imageview.getDrawingCache());
imageview.setDrawingCacheEnabled(false);
return bitmap;
} else {
return null;
}
}
Code is working fine but the cache not cleared every time and the bitmap created with previous cache so how I can clear a bitmap cache?
This is a sample e.g. where i use to Free the native object associated with this bitmap.
Bitmap bitmap;
public Bitmap createBitmap(ImageView imageview) {
if (bitmap != null) {
bitmap.recycle();
bitmap = null;
}
bitmap = Bitmap.createBitmap(imageview.getDrawingCache());
// Your Code of bitmap Follows here
}
Before use of Bitmap just free the object.
use bitmap.recycle(); before valuating your bitmaps to clear their cache before recreating it.
Related
I have a NetworkImageView where I download images from a server. However, the requirement is to have the ability to rotate the image after downloading the image. I have a rotate image function but I need to get the Bitmap associated to the NetworkImageView in order for me to rotate the bitmap. How can I access the BitMap from a NetworkImageView?
You can use below method to get bitmap of any view.
Bitmap bitmap = viewToBitmap(mImageView);
Bitmap bitmap = viewToBitmap(mLinearlayout);
Bitmap bitmap = viewToBitmap(mRelativelayout);
public Bitmap viewToBitmap(View view) {
Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
Hope this helps
UPDATE
Create a volley image request.
After the image has been loaded and set inside the NetworkImageView this code will work. Else it will return a Null pointer since there is no view/image inside the NetworkImageView
mNetworkImageView = (NetworkImageView) view.findViewById(R.id.networkImage);
Bitmap bitmap = ((BitmapDrawable) mNetworkImageView.getDrawable()).getBitmap();
There is another way to use Volley and obtain the Bitmap.
Create an ImageRequest
inside the onResponse(), obtain the Bitmap and set it to your ImageView (Not NetworkImageView). I had mentioned this method in the comments above.
In my project I used (Volley + NetworkImageView) to download some images and texts and showing them in a list view.. till here, I don't have any problem.
Now, I want to get bitmaps form NetworkImageView and I tried many methods like the following, but non of them worked for me.
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
Bitmap bitmap = drawable.getBitmap();
Another method:
imageView.buildDrawingCache();
Bitmap bmap = imageView.getDrawingCache();
Non of them worked..
Any Help is appreciated,,
You cannot get the Bitmap reference as it is never saved in the ImageView.
however you can get it using :
((BitmapDrawable)this.getDrawable()).getBitmap();
beacuse when you set it with Volley u do this:
/**
* Sets a Bitmap as the content of this ImageView.
*
* #param bm The bitmap to set
*/
#android.view.RemotableViewMethod
public void setImageBitmap(Bitmap bm) {
// Hacky fix to force setImageDrawable to do a full setImageDrawable
// instead of doing an object reference comparison
mDrawable = null;
if (mRecycleableBitmapDrawable == null) {
mRecycleableBitmapDrawable = new ImageViewBitmapDrawable(
mContext.getResources(), bm);
} else {
mRecycleableBitmapDrawable.setBitmap(bm);
}
setImageDrawable(mRecycleableBitmapDrawable);
}
however if you set your default image or error image or any other image in any other way you may not get BitmapDrawable but NinePatchDrawable for example.
here is how to check:
Drawable dd = image.getDrawable();
if(BitmapDrawable.class.isAssignableFrom(dd.getClass())) {
//good one
Bitmap bb = ((BitmapDrawable)dd).getBitmap();
} else {
//cannot get that one
}
I am taking a bitmap, creating a new one from the original and then setting the new one to an ImageView, when I recycle the original I get that error, but I am not drawing the original? For more detail read my comments in my code. As you can see I recycle the bitmap that comes in which I do not draw, I always make a new bitmap called tile that I draw.
My Code:
public void tileImage(Bitmap bm){
if(bm==null){
Debug.out("Bitmap is null");
}
else{
Bitmap tile;
float tileWidth = bm.getWidth();
float tileHeight =1024;
//if my bitmap is too wide
if(bm.getWidth()>width){
Debug.out("Bitmap too wide: "+bm.getWidth());
//if this code runs I get no error, if not I get the error
bm = Bitmap.createScaledBitmap(bm,
(int)width,
(int)(bm.getHeight()*(float)(width/tileWidth)),
false
);
}
Debug.out("Bitmap height: "+bm.getHeight()+" adjusted width "+bm.getWidth());
//if my bitmap is too tall
if(bm.getHeight()>tileHeight){
for(int i = 0; tileHeight*i<bm.getHeight(); i++){
image = new ImageView(main);
//make tiles of the body
if((tileHeight*(i+1))<bm.getHeight()){
tile = Bitmap.createBitmap(
bm,
0,
(int)(tileHeight*i),
(int)bm.getWidth(),
(int)(tileHeight)
);
Debug.out("Tiling: "+i);
}
//tile the reaminder
else{
tile = Bitmap.createBitmap(
bm,
0,
(int)(tileHeight*i),
(int)bm.getWidth(),
(int)(bm.getHeight()%tileHeight)
);
Debug.out("Tiling: "+bm.getHeight()%tileHeight+" "+i);
}
image.setImageBitmap(tile);
tiles.addView(image);
}
}
//else its not too tall
else{
image = new ImageView(main);
Debug.out("No tiling");
tile = Bitmap.createBitmap(
bm,
0,
0,
(int)bm.getWidth(),
(int)bm.getHeight()
);
Debug.out("Bitmap too small height: "+bm.getHeight()+" width "+bm.getWidth());
image.setImageBitmap(tile);
tiles.addView(image);
}
}
//this is the trouble maker
bm.recycle();
}
Bitmap.createBitmap(params) Returns an immutable bitmap from the specified subset of the source bitmap. The new bitmap may be the same object as source, or a copy may have been made.
bitmap.recycle() method Frees the native object associated with this bitmap, and clear the reference to the pixel data. This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
onDraw() method takes some time to inflate the View. If you are passing the bitmap to draw a view and calling recycle() on the same reference then the bitmap is marked as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing.
You should call recycle() in onDestroy().
My Code:
public class MainActivity extends Activity {
private Bitmap mBitmap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView image1 = (ImageView) findViewById(R.id.image1);
mBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
image1.setImageBitmap(mBitmap);
tileImage();
}
private void tileImage() {
ImageView image2 = (ImageView) findViewById(R.id.image2);
Bitmap bm = Bitmap.createBitmap(mBitmap, 0, 0,
(int) mBitmap.getWidth(), (int) mBitmap.getHeight());
image2.setImageBitmap(bm);
}
#Override
protected void onDestroy() {
mBitmap.recycle();
super.onDestroy();
}
}
The answer above is not entirely correct. You should in fact not wait until onDestroy with recycle. Especially if you make heavy use of creating these bitmaps. You might not even get to onDestroy before you get an OOM error. What you should do is test the dimensions you're scaling it to to the dimensions of the bitmap. If they are equal, return the original bitmap and no recycle is necessary. Otherwise, you'll get a new bitmap object and you should immediately call recyle on the original bitmap.
see http://developer.android.com/training/displaying-bitmaps/manage-memory.html
If I create an object and assign it to a variable:
Obj obj1 = null;
obj1 = myFunction(params);
(here myFunction creates a complex object)
And later I reassign the variable:
obj1 = myFunction(otherparams);
Does in that moment a memory leak occur, because I did not destroy the previous object?
Here is the real situation:
Bitmap bmp;
bmp = drawMyBitmap(3);
//... some code
bmp = drawMyBitmap(4);
Will a memory leak happen here?
Of cource, I know that I must call bmp.recycle, but I can't do it, because the real code is the following:
Bitmap bmp;
bmp = drawMyBitmap(3);
imageView.setImageBitmap(bmp);
//... some code
// if I try to do recycle here - I receive java.lang.IllegalArgumentException: Cannot draw recycled bitmaps
// But I need to recreate bitmap every some minutes
bmp = drawMyBitmap(4);
imageView.setImageBitmap(bmp);
So, how can I recycle the bitmap and avoid memory leaks?
As I understand, your problem is just you can't recycle your Bitmap cause it's used.
It's pretty naive, so maybe it's wrong, but do this:
imageView.setImageBitmap(bmp);
//... some code
Bitmap tmp = bmp;
bmp = drawMyBitmap(4);
imageView.setImageBitmap(bmp);
tmp.recycle(); // As it's not anymore referenced by the ImageView, you can recycle the Bitmap safely
I didn't test it. Give feedback.
In the first case, you will release the first object's reference so the garbage collector will destroy it, leaving the second one live on memory because of new reference.
In the second case, if you are setting bitmaps to ImageViews, you can not recycle them because the view will not have the bitmap to draw the image and it will throw to you a bitmap recycled exception, so you are not "leaking" to much memory keeping 2 bitmaps on memory.
Try to use bitmap options to create them to optimise your memory comsuption if you want.
Bitmap bmp;
bmp = drawMyBitmap(3);
imageView.setImageBitmap(bmp);
//... some code
// if I try to do recycle here - I receive java.lang.IllegalArgumentException: Cannot draw recycled bitmaps
// But I need to recreate bitmap every some minutes
Bitmap temp = bmp; //try this
bmp = drawMyBitmap(4);
imageView.setImageBitmap(bmp);
temp.recycle();
I get an Bitmap from internal storage or from the internet. When I try to display it in a ImageView, it works on different Emulator versions but not on my Galaxy S I9000. The Bitmap simply doesn't show up.
protected void onPostExecute (Bitmap bmp) {
progressBar.setVisibility(View.INVISIBLE);
if (bmp != null) {
imageView.setImageBitmap(bmp);
imageView.setVisibility(View.VISIBLE);
}
}
This is highly likely to be an underlying issue with openGL not accepting textures larger than 2400x2400. It doesn't cause crashes, but fails by not displaying the Bitmap in an ImageView.
You should always ensure that the Bitmap is smaller than 2400 in both dimensions and resize it before setting it on your view if it is larger.
I used this method to help me in displaying the image
public static Drawable ToDrawable(Resources r, byte[] encodedString)
{
if (encodedString == null)
return null;
Bitmap bmp = BitmapFactory.decodeByteArray(encodedString, 0, encodedString.length);
Drawable drawable = new BitmapDrawable(r, bmp);
// bmp.recycle();
return drawable;
}