How to avoid null pointer in View.getDrawingCache()? - android

My app has this code snippet
mView.setDrawingCacheEnabled(true);
mBitmap = Bitmap.createBitmap(mView.getDrawingCache());
Where mView is an instance of a custom subclass of FrameLayout. The app has worked well for 6 months now with thousands of users. Recently I got few reports (possibly from same user but I cannot confirm) about a null pointer passed to createBitmap(). I don't know what hardware or android version the user uses.
What can cause getDrawingCache() to return null even though I enabled the cache just before it? Is it related to hardware acceleration in new phones? Is it because of insufficient ram for the bitmap? Anything else? Can it be prevented?
Edit: I found this solution here http://tinyurl.com/7yranvj . Is it a reasonable workaround?
mBitmap = Bitmap.createBitmap(mView.getWidth(), mView.getHeight(),
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(mBitMap);
mView.draw(canvas);

actually your guess was right. If you are using hardware accelerated layer, you can not use drawing cache. New devices has hardware acceleration on by default, so it could be your problem. you can turn hardware acceleration off for single view like this.
if(android.os.Build.VERSION.SDK_INT >= 11) frame.setLayerType(LAYER_TYPE_SOFTWARE, null);
frame.setDrawingCacheEnabled(true);

Related

Rendering path with Canvas.drawPath() in ICS with hardware acceleration

On ICS device, I tried the following code to draw two rectangles.
Path p1 = new Path();
p1.moveTo(0, 0);
p1.lineTo(0, 100);
p1.lineTo(100, 100);
p1.lineTo(100, 0);
p1.close;
Path p2 = new Path();
Matrix scaling = new Matrix();
scaling.preScale(2, 2);
p1.transform(scaling, p2);
canvas.drawPath(p1);
canvas.drawPath(p2);
Running the above code on ICS device with hardware acceleration enabled (as it is by default), p1 is drawn where as p2 is not.
In general, what happened to me is, as long as a Path is not hand-wired (i.e. by calling lineTo(), quadTo(), etc.), but obtained by copying or transforming (i.e. by calling the copy constructor, transform(matrix, dest), translate(x, y, dest), etc.), it is not drawn.
I found a "widely known" issue that is similar but not exactly the same as my problem: https://groups.google.com/forum/#!msg/android-developers/eTxV4KPy1G4/tAe2zUPCjMcJ
Therefore, can anyone tell me what is the issue I am running into? In my case, I have to resort to path transformation otherwise code complexity will be greatly increase. Thanks!
try setting android:layerType="software" in xml on the view to see if that fixes it. some methods aren't available with hardware acceleration on all apis.
the list is here:
http://developer.android.com/guide/topics/graphics/hardware-accel.html
note that if changing the layer type fixes it, you should create a separate layout for the newer APIs for optimal performance

Skewed images in ImageView on Android 4.1.2

I have an app that displays an image in an ImageView, and am experiencing problem specifically for Android 4.1.2. It is confirmed to not work on three separate 4.1.2 devices, while working on 2.3.7, 4.2.1, 4.3 and 4.4.2. The error occurs for several different images, but not all. There seems to be something about some specific JPEG-files that doesn't work as intended.
How it actually looks, and how it shows on Android 4.1.2:
The above image (left) is one such problematic image file.
A summary of the code behind setting the displayed image is:
Bitmap bitmap, background;
ImageView imageView = (ImageView)findViewById(R.id.imageView);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPurgeable = true;
options.inInputShareable = true;
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog, options);
background = bitmap.copy(Bitmap.Config.RGB_565, true);
Canvas canvas = new Canvas(background);
canvas.drawBitmap(bitmap, 0, 0, null);
// Some calls to canvas.drawText(....) here, but doesn't have to happen for the error to occur
imageView.setImageBitmap(background);
I've figured that I am able to resize and re-save the above photo in Photoshop to make it work, without knowing why. Since I have several I'd prefer not having to do so.
I am wondering what is the source of this error on Android 4.1.2, and if there might be some programmatic way of fixing it?
I've tried my luck on Google view "skewed", "tilted", "distorted" and similar, but there are very few mentions of it and no fixes. This is the mention with screenshot I've found:
Anyone else getting distorted album art in Play Music? (Screenshot)
Based on the comment of rupps I changed from:
bitmap.copy(Bitmap.Config.RGB_565, true)
To:
bitmap.copy(Bitmap.Config.ARGB_8888, true)
This did in fact solve the problem for 4.1.2 devices, while remaining similar in functionality for all other tested devices. This does programatically solve my issue. Note however that it does require double the memory, as each pixel is stored on 4 bytes instead of 2 bytes.
As for the source of the problem, I read from the documentation of RGB_565 that:
This configuration can produce slight visual artifacts depending on the configuration of the source.
I think this mostly relates to banding/color/dithering issues and this does not explain the version specific problem, but perhaps why this setting is troublesome.

Android Bitmap too heavy error

I have the following problem that some of you must know on my android app :
3288-byte external allocation too large for this process.
Out of memory: Heap Size=5959KB, Allocated=3922KB, Bitmap Size=18614KB
VM won't let us allocate 3288 bytes
Facts :
I'm creating a bitmap of the screen (so quite huge) and I manipulate it (changing size etc ...) for doing a flipping page animation.
It crashes only on a desire HTC : on galaxy s2 and kindle fire, no problems.
I'm already desallocating the current Bitmap everytime I create a new one with the following code :
Bitmap old = this.bitmap;
this.bitmap = bitmap;
this.invalidate();
if(old != null)
old.recycle();
I also tryied to call this function :
public void recycle() {
if (this.bitmap!=null)
this.bitmap.recycle();
System.gc();
Runtime.getRuntime().gc();
}
Severals time in my code, and sometimes it gets slightly better (like it crashes a little later), but that's still not good.
I spent a lot of time on this problem, and I don't really get how to fix it. It's like on forum there is a lot of misinformation, so I'm kinda lost.
Thanks, ask for more precision.
Edit :
Here is a code called a lot :
//set the foreground image with the current day
Bitmap b = Bitmap.createBitmap(visibleLayout.getWidth(), visibleLayout.getHeight(),Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
visibleLayout.draw(c);
viewBitmapNext.setBitmap(b);
viewBitmapNext.setVisibility(View.VISIBLE);
Where viewBitmapNext is an overwritted element of the View class. The setBitmap function is described above.
About the resizement, I do this line of code :
viewBitmapPrevious.setLayoutParams(new RelativeLayout.LayoutParams((int) (iterator - ((totalWidth - iterator) - activity.getResources().getDimension(R.dimen.margin_right))/2), RelativeLayout.LayoutParams.WRAP_CONTENT));
Again, tell me if you you want to know more.
I found out what was the problem. It will not be interresting for anyone, because it's a dumb error closely related to my project, but I say it anyway.
I actually had 2 errors :
one loop creating elements infinitly.
Two big pictures I put as a background after a certain action performed on a cheap phone ( I'm still on it but it should be easy to solve). I'll edit this answer when it's done.
To everyone that helped me, you couldn't find out the problem's solution (wasn't related to the bitmap-screen I do), but still it was helpful on it's way.
Thanks.

When (if at all) should I use Bitmap.recycle()?

According to Android Developers site, the Bitmap.recycle() method definition is:
Free the native object associated with this bitmap, and clear the reference to the pixel data
I've developed some applications which are creating / decoding a lot of bitmaps, and put the result bitmap objects to ImageViews. Sometimes I've got the famous exceptions such as:
bitmap size excceded vm budget
and
out of memory error
Also I'm sure I don't have any memory leaks that can cause that.
After a lot of searches, I discoverd the "recycle" method, and used it to free the bitmap's native memory when no longer needed. It seems like it helped a lot.
I'm asking if that's something I'm supposed to do on this situation, because
I know the system is doing this anyway without calling it explicitly (is it? maybe I'm wrong).
Should I use this method in situations like this?
In what situations should I use this method?
Should I use this method at all?
thanks in advance.
UPDATE:
google posted this guide recently, which says:
On Android 2.3.3 (API level 10) and lower, using recycle() is recommended. If you're displaying large amounts of bitmap data in your app, you're likely to run into OutOfMemoryError errors. The recycle() method allows an app to reclaim memory as soon as possible.
in what situations should I use this method?
The Bitmaps are GC'ed by GC whenever it decides.But in some situations it may get delayed.
And always remember thumb rule in java (Maybe it applies to othe P.L also).The speed of recycling objects by GC may not be same as speed of creating objects.So sometimes the GC is slow to in recycling.
so recycle() means If you want to free memory ASAP you should call recycle()
should I use this method at all??
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.But if you are facing the issues like bitmap size exceeded vm budget or out of memory error then you need to use this.
I do use it in operations where i know that bitmap is not going to be used anymore.
public static Bitmap getMaskedContactImage (Context context, Bitmap contactImageBitmap, int maskToBeApplied) {
Bitmap mask = BitmapFactory.decodeResource(context.getResources(), maskToBeApplied);
Bitmap output = Bitmap.createBitmap(mask.getWidth(),mask.getHeight(), Config.ARGB_8888);
final Rect finalRect = new Rect(0, 0, contactImageBitmap.getWidth(), contactImageBitmap.getHeight());
final Rect originRect = new Rect(0, 0, mask.getWidth(), mask.getHeight());
Canvas canvas = new Canvas(output);
Paint xferPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
xferPaint.setColor(Color.BLACK);
xferPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
canvas.drawBitmap(contactImageBitmap, finalRect, originRect, null);
canvas.drawBitmap(mask, originRect, originRect, xferPaint);
contactImageBitmap.recycle();
mask.recycle();
return output;
}
In places like that one, im sure im not going to use the mask or the contactImage.
I found a really good resource for Bitmap processing that can be helpfull Displaying bitmaps.
Regards,
Alex

Canvas in SurfaceView - hardware acceleration

I'm developing on ICS and trying to understand why a
Canvas.isHardwareAccelerated() will always return FALSE when using a
Canvas inside a SurfaceView.
I've tried a very basic example like this:
http://android-coding.blogspot.com/2011/05/drawing-on-surfaceview.html
Or this one: http://jmsliu.com/199/android-canvas-example.html
I even modified them to not have any canvas calls inside the draw loop
thinking that I might have got in some unsupported HW accel operations
for certain drawing calls.
I checked this list under "Unsupported Drawing Operations"
developer.android.com/guide/topics/graphics/hardware-accel.html
but I'm not doing any of that.
I am using the Force GPU rendering flag inside the Developer Options
together with
inside the manifests, also I'm specifying minSDK/targetSDK >= 14.
The View.isHardwareAccelerated will return TRUE, while the
Canvas.isHardwareAccelerated will return always FALSE.
I've seen that Canvas.java has hard-codeded return false for isHardwareAccelerated(), while HardwareCanvas.java has return true. I guess for some reasons I'm not getting the HW path, why?
Can a Canvas inside a SurfaceView be HW accelerated?
Thanks
A Canvas returned by SurfaceView.lockCanvas() cannot be hardware accelerated at the moment.
it seems that there is a new hide api method unlockHardwareCanvas in android.view.Surface
and here is the example:
http://androidxref.com/5.1.0_r1/xref/frameworks/base/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java
you can use relfecation in production environments.
Have you stated in your android manifest, that your app wants to use hw acceleration?
This would be something like
<application android:hardwareAccelerated="true" ...>
Depending on what you want to do it might also be useful to only activate acceleration only for an activity or window, see http://developer.android.com/guide/topics/graphics/hardware-accel.html for details.
Rudi

Categories

Resources