First I create a new bitmap
Bitmap image = BitmapFactory.decodeResource(Game.panel.context.getResources(),R.drawable.dinofront)
Then I stretch the image, after this I check if the bitmap is null, it is not
image = Bitmap.createScaledBitmap(image, Game.tileWidth, Game.tileHeight, false);
Later in the draw() method, before I do anything with the bitmap, I check again and it is now null. Does anyone have any idea why this is happening? Is there anything special I have to do that I am missing?
Full classes: http://pastebin.com/t95MVvv0
I can think of two possible reasons:
It may simply be that the image variable may be changed by other part of your code
Thread synchronization - remember that codes may not be executed in the order specified in the Java source code, but can be reorder as the JVM deemed fit, which includes assigning the instance variable before the constructor is run. This is especially likely when you are doing extremely time consuming Bitmap op within the constructor, and I assume your object creation code is on a different thread than your drawing code. To check for this possibility you can declare a volatile boolean variable that is only set to true when the constructor is finished, and check its value within your draw() method
Wow I feel stupid. Because there is the image for the class and the image that is brought in with the constructor when I call image = Bitmap.create... it is only setting the constructor's image not this.image so if I change it to this.image =... it all works fine
Related
I am new to Android development and I have originally set my graphics for the user to drag using SetImageResource. However later in development it does not have methods I required so I ended up using SetImageDrawable later on for dragging images and placing them in new spots.
What are the core differences between these two? I have been looking online and am still uncertain on these key differences and how they will affect my program.
Example of how I set the images originally on the view using setImageResource:
case 'J':
if (tempFlip[9]) {
GameBoardImages[k].setImageResource(temp[9]);
} else {
temp[9] = symbolTilesID[k];
tempFlip[9] = true;
GameBoardImages[k].setImageResource(symbolTilesID[k]);
}
break;
Then how I have it set in OnDragListener using setImageDrawable:
target = GameBoardImages[ImageNumber]; //Spot getting dragged to
dragged = (ImageView) myDragEvent.getLocalState(); //Dragging item
target_draw = target.getDrawable();
dragged_draw = dragged.getDrawable();
dragged.setImageDrawable(target_draw);
target.setImageDrawable(dragged_draw);
The method setImageResource() takes in a drawable resource ID (which is an int).
PRO: You are passing in an integer, which is very small in terms of memory. If there are several method calls before your call to setImageResource(), they can easily pass around this int with no memory issues.
CON: Because you are passing in an integer, the setImageResource() method must perform Bitmap reading and decoding in order to display the image. The method performs this on the UI thread, which could cause a 'latency hiccup' depending on the size of the image and the processing power of the device. This would result in your user seeing a lag or funny behavior on the screen as the image is processed.
The method setImageDrawable() takes in the actual Drawable object.
PRO: You already have the actual Drawable, so there is no need to process this data in order to display it. For this method you won't see the latency issues on screen.
CON: The drawback here is that you might end up passing a drawable around between other methods in order to have access to it to make the setImageDrawable() method call. Passing around Drawable objects a lot isn't a great idea, as they can be large.
I have an object that overwrites the Application object. In it, I have a member variable which is a LongSparseArray where the key is some identifier of type long and the value is an object with 2 member variables: a Bitmap and a long which is used as a timestamp.
This is my global image cache. Occasionally, a function is ran that looks at the timestamps and ages things that are over an hour old.
By "age" I mean that it removes that entire entry from the LongSparseArray.
Here is my question:
Suppose I have an Activity with a ListView. Each row in the ListView has an ImageView that is populated with an image from the cache.
Bitmap image = ((MyApp)getApplicationContext()).getImage(id);
holder.imgImage.setImageBitmap(image);
Now, suppose the user clicks some button which takes them to a new Activity. While on this new Activity, the image previously assigned to a row in the ListView in the previous Activity ages.
So, to recap, that Bitmap key/value entry now no longer exists in the global LongSparseArray.
Is that Bitmap really able to be reclaimed by Java? Isn't it still being referred to by the ImageView in the ListView of the previous Activity? Assuming, of course, that Android hasn't reclaimed the memory used by that Activity.
The reason I'm asking about this is my previous aging function would also call .Recycle() on the Bitmap. In this scenario, when the user hit the back button and returned to the previous Activity which was using that Bitmap, the application would crash, presumably because that Bitmap was not only missing from the cache, but also from memory. So I just removed the .Recycle() call.
By the way, once the Bitmap is removed from the cache, and an object with that id shows up on screen again, the application will download the Bitmap again and place it in the cache. If the previous one stayed in memory, you could see how this would present a problem.
Also, does anyone have any ideas for a more effective solution?
What would happen if I set myImageView.setDrawingCacheEnabled(false);?
There are 2 Activities which use this image caching. One is a search screen that displays a list of items (and their images) after the user performs a search. The other is a list of those items the user has then selected to keep.
Issue: Once recycle() method is called on a bitmap, the bitmap should never be used again. If an attempt is made to draw the bitmap, then an exception will be thrown. From docs:
You should use recycle() only when you are sure that the bitmap is no
longer being used. If you call recycle() and later attempt to draw the
bitmap, you will get the error: "Canvas: trying to use a recycled
bitmap".
In this specific case, you have recycled the bitmap, but the ListView item's ImageView has a strong reference to the bitmap. When you return to the Activity, the ListView item attempts to draw the bitmap, hence the exception is thrown.
Bitmap memory management: Prior to Android 2.3.3, the backing pixel data of a bitmap was stored in native memory and bitmap itself in Dalvik memory. Hence to release the native memory, recycle method has to be called.
Here is Bitmap.recycle function definition:
public void recycle() {
if (!mRecycled) {
if (nativeRecycle(mNativeBitmap)) {
// return value indicates whether native pixel object was actually recycled.
// false indicates that it is still in use at the native level and these
// objects should not be collected now. They will be collected later when the
// Bitmap itself is collected.
mBuffer = null;
mNinePatchChunk = null;
}
mRecycled = true;
}
}
Post Android 3.0, the backing pixel data is also stored in Dalvik memory. When the bitmap is no longer required, we need to ensure we don't hold any strong reference to the bitmap, so that it is garbage collected.
Solution: If you are still supporting Android 2.3.3 and lower version, you still need to use recycle to release the bitmap.
You can use reference counting to track whether the bitmap is currently being referenced by the ListView item, so that even it is aged, you don't call recycle on the bitmap.
ListView adapater's getView method is the place where the bitmap is assigned to the ImageView. Here you increment the reference count. You can attach setRecyclerListener to the ListView to know whenever the listview item is put into recycle bin. This is the place you would decrement the reference count of the bitmap. The aging function need to recycle the bitmap only if the reference count is zero.
You can also consider using LruCache for caching, as mentioned in docs.
setDrawingCacheEnabled: By calling this method with true param, the next call to getDrawingCache will draw the view to a bitmap. The bitmap version of view can be rendered on to the screen. Since it is just a bitmap, we cannot interact with it as done with an actual view. Couple of use cases are:
When ListView is being scrolled, the bitmap of the displayed items view is captured and rendered. So that the views being scrolled don't undergo measure and layout pass.
View hierarchy feature in DDMS.
Is that Bitmap really able to be reclaimed by Java? Isn't it still
being referred to by the ImageView in the ListView of the previous
Activity? Assuming, of course, that Android hasn't reclaimed the
memory used by that Activity.
The Bitmap is stilled used in the ListView (a strong reference) so dalvik can't reclaim its memory.
Apparently you can't call recycle on the Bitmap or bad things will happen(app crash, e.g.).
What would happen if I set myImageView.setDrawingCacheEnabled(false);?
If you disable drawing cache, every time your view needs to be redrawn, the onDraw method will be called.I'm not very familiar with ImageView , you can go and read its source for a deep understanding.
(Note: the usage of drawing cache is different when hardware accerleration is enabled/disabled, here I just assume you're using software rendering).
For the solution, you can try the following:
when the Bitmap cache become stale, you remove it from the cache array(and then you app will try to get a new one, I think).
In ListView.getView, you can check whether currently used Bitmap ages. It should be easy because you know the timestamp when you call setImageBitmap the first time and the latest timestamp. If they are not same, you call setImageBitmap again using the new Bitmap and the old one will be reclaimed.
Wish this helps.
Regarding, "Also, does anyone have any ideas for a more effective solution?"
The Picasso library would help solve the problems you are facing http://square.github.io/picasso/
Picasso is "A powerful image downloading and caching library for Android"
"Many common pitfalls of image loading on Android are handled automatically by Picasso:
Handling ImageView recycling and download cancelation in an adapter.
Automatic memory and disk caching."
I have overridden the onDraw() method in a custom View. My method looks like following:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
data.lockData(); // uses reentrantlock
Bitmap bm = data.getBitmap();
if (bm != null) {
canvas.drawBitmap(bm,0,0,mPaint);
}
data.unlockData();
}
There is another thread that updates the bitmap, and this thread also uses the lockData() method to ensure serialized access to the bitmap. However, I see that the bitmap being drawn contains incomplete updates, i.e. it is being drawn to the screen while being in the middle of update by the other thread. I do not know the details of the Android drawing pipeline with sufficient detail (or actually with any detail:), but I assume this is due to that drawBitmap() does not actually draw the bitmap, but just places it into a draw operations queue with the pointer to the bitmap, and later another part of the system will use this pointer to copy the required part of the bitmap pixels to the buffer that will be displayed on screen. Therefore the actual drawing is not protected by the mutex.
There is a classical solution, i.e. double buffering, which I think could work in this type of situation. However, I still think that my draw thread might be so fast that it actually comes to update the buffer that is being drawn again before the actual draw has finished. Furthermore, my bitmap can be quite large, and allocating a separate buffer would be somewhat wasteful.
My questions are:
Is my above stated understanding about the reason for incomplete draws correct, i.e. the drawBitmap returns before the bitmap data has actually been copied to the display? Could you provide a reference for details on how the drawing works?
Is there a way to know when the bitmap has actually been drawn, and therefore it is again safe to modify the bitmap?
If double buffering is the only solution, how can I ensure that buffer1 has finished drawing before allowing the buffer1 to be updated again with new data?
I was not able to make the View drawing synchronized. One way it would have worked is to do the calculation from within the onDraw() method, i.e. Bitmap bm = data.getBitmap(); would do calculate the updated bitmap. The drawback of this is that this will block the UI thread for the duration of the calculation, resulting or example in missed input events, etc.
One way this can be overcome is to use a SurfaceView instead. There one can do the calculation and drawing in a separate thread, thus serializing the two operations, while still leaving the UI thread free.
would
image.setDrawingCacheEnabled(true);
do any good? Setting this to true enables you to pull a bitmap from the newly drawn canvas. Seems like that would be synchronized with the actual drawing so that when you subsequently do:
imageBitmap = image.getDrawingCache();
you would get a fully drawn image
In my Android app, when I run one of my activities a second time, I get an OutOfMemoryError. I think I need to delete the bitmaps from the first time when the activity runs. But I don't know how I can do this. Thank you.
Try reading your Bitmaps from within your code. Place the Bitmaps in the res/drawable-hdpi folder. (there are different folders for different image qualities). Set up the Bitmap fields in your code:
Bitmap alpha;
Bitmap foo;
Now initialize the Bitmaps in the onResume():
Options options = new Options();
alpha = BitmapFactory.decodeResource(game.getResources(), R.drawable.youBitmapName, options);
foo = BitmapFactory.decodeResource(game.getResources(), R.drawable.youBitmapName2, options);
Options will give you the ability to downsample. (I'm not sure how big your images are, but you might also want to use the scaling methods then).
In the onPause, clean up resources by calling:
alpha.recycle();
alpha = null;
foo.recycle();
foo = null;
As soon as the onResume() method is called, the bitmaps will reinitialize.
Normally the garbace collector would call Bitmap.recycle() as soon as no more references point to that instance.
If you want to 'force' clean up your bitmaps, call recycle() on your own.
But I would suggest to look for a memoryleak first.
Null your bitmaps and call the Garbage Collector: System.gc(); and/or Runtime.getRuntime().gc();
Also provide more details about your code for better answers.
After going through few articles about performance,
Not able to get this statement exactly.
"When a Drawable is attached to a view, the view is set as a callback on the drawable"
Soln: "Setting the stored drawables’ callbacks to null when the activity is destroyed."
What does that mean, e.g.
In my app , I initialize an imageButton in onCreate() like this,
imgButton= (ImageButton) findViewById(R.id.imagebtn);
At later stage, I get an image from an url, get the stream and convert that to drawable, and set image btn like this,
imgButton.setImageDrawable(drawable);
According to the above statement, when I am exiting my app, say in onDestroy()
I have to set stored drawables’ callbacks to null, not able to understand this part ! In this simple case what I have to set as null ?
I am using Android 2.2 Froyo, whether this technique is required, or not necessary.
You would have to do this only if you kept the drawable as a static field somewhere, or in a cache of some sort. In this particular situation, there's no reason to set the callback to null.
Here is exactly what was the case in example you cited:
Phone orientation has been changed, and this should mean that old activity should be "dumped" and new one created
If you have stored reference to bitmap as a static field, it has reference to old activity that was supposed to be dumped (drawable has reference to TextView, view has reference to activity)
New activity is created, but your drawable still has the reference to old one, so old one can't be dumped.
Of course, all this is right if you store drawable as static like in cited example:
private static Drawable sBackground;