I am making a custom view that has 2 elemnts: a background that consists of a Bitmap like a map and a small resource image that acts like a pin, to indicate position on the map. On each gps position change my view get’s update and the pin moves in the new position.
First it woks fine, after a while I get this error:
ERROR/AndroidRuntime(416):
java.lang.OutOfMemoryError: bitmap
size exceeds VM budget
It has something to do with redrawing the map all the time, I guess. Maybe you have any idea to help me ?
public class MyMapView extends View {
private int xPos = 0;
private int yPos = 0;
private Bitmap trackMap;
private Paint backgroundPaint;
private Bitmap resizedBitmap;
private Bitmap position;
private Matrix positionMatrix;
private Paint positionPaint;
private int space=0;
public MyMapView(Context context) {
super(context);
init(context, null);
}
public MyMapView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public MyMapView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(final Context context, AttributeSet attrs) {
backgroundPaint = new Paint();
backgroundPaint.setFilterBitmap(true);
position = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.position);
positionMatrix = new Matrix();
positionPaint = new Paint();
positionPaint.setFilterBitmap(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
}
@Override
protected void onDraw(Canvas canvas) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (trackMap!=null)
{
resizedBitmap = Bitmap.createScaledBitmap(trackMap, height, height, true);
space = (width - resizedBitmap.getWidth())/2;
canvas.drawBitmap(resizedBitmap, space, 0, backgroundPaint);
}
if (xPos != 0 && yPos !=0)
{
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(xPos+space-position.getWidth()/2, yPos-position.getHeight()/2);
canvas.drawBitmap(position, positionMatrix, positionPaint);
canvas.restore();
}
}
public void updatePosition(int xpos, int ypos, Bitmap trackImage)
{
xPos=xpos;
yPos=ypos;
trackMap = trackImage;
invalidate();
}
}
@Fedor: I’ve tried something like this:
if (trackMap!=null)
{
if (resizedBitmap != null) {
resizedBitmap.recycle();
}
resizedBitmap = Bitmap.createScaledBitmap(trackMap, height, height, true);
space = (width - resizedBitmap.getWidth())/2;
canvas.drawBitmap(resizedBitmap, space, 0, backgroundPaint);
}
}
But still the same. I call updatePosition(..) from an activity each time GPS detects a location changed, so the onDraw get’s called very often. The image map is 400×400 pixels
11-13 20:29:42.121: ERROR/AndroidRuntime(213): Uncaught handler: thread main exiting due to uncaught exception
11-13 20:29:42.150: ERROR/AndroidRuntime(213): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.graphics.Bitmap.nativeCreate(Native Method)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.graphics.Bitmap.createBitmap(Bitmap.java:435)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:340)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at com.vorienteering.customcontrols.MyMapView.onDraw(MyMapView.java:71)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.View.draw(View.java:6274)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.drawChild(ViewGroup.java:1526)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.drawChild(ViewGroup.java:1524)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.View.draw(View.java:6277)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.drawChild(ViewGroup.java:1526)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.View.draw(View.java:6277)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.drawChild(ViewGroup.java:1526)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.drawChild(ViewGroup.java:1524)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1256)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.View.draw(View.java:6277)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.widget.FrameLayout.draw(FrameLayout.java:352)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:1883)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewRoot.draw(ViewRoot.java:1332)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewRoot.performTraversals(ViewRoot.java:1097)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.view.ViewRoot.handleMessage(ViewRoot.java:1613)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.os.Handler.dispatchMessage(Handler.java:99)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.os.Looper.loop(Looper.java:123)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at android.app.ActivityThread.main(ActivityThread.java:4203)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at java.lang.reflect.Method.invokeNative(Native Method)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at java.lang.reflect.Method.invoke(Method.java:521)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
11-13 20:29:42.150: ERROR/AndroidRuntime(213): at dalvik.system.NativeStart.main(Native Method)
@Peter Knego I edited like in your answer. It had problemes with finding positionRef and some others. I updated the code like this:
@Override
protected void onDraw(Canvas canvas) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (trackMapRef != null) {
Bitmap resizedBitmap = Bitmap.createScaledBitmap(trackMapRef.get(), width, height, true);
space = (width - resizedBitmap.getWidth()) / 2;
canvas.drawBitmap(resizedBitmap, space, 0, backgroundPaintRef.get());
}
if (xPos != 0 && yPos != 0) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
positionRef = new WeakReference<Bitmap>(BitmapFactory.decodeResource(getContext().getResources(), R.drawable.position));
positionMatrixRef = new WeakReference<Matrix>(new Matrix());
Paint posPaint = new Paint();
posPaint.setFilterBitmap(true);
positionPaintRef = new WeakReference<Paint>(posPaint);
canvas.translate(xPos + space - positionRef.get().getWidth() / 2, yPos - positionRef.get().getHeight() / 2);
canvas.drawBitmap(positionRef.get(), positionMatrixRef.get(), positionPaintRef.get());
canvas.restore();
}
}
Now it works for a little longer. Double the time. But still gives the out of memory error.
Thank you very much for your help.
I was thinking as Fedor mentioned, is there a way in which I draw the map image only on creation, and then only update the pin postion onDraw ? This would seem the more convenient way as redrawing the map only uses resources and it’s the same all the time.
THANK YOU ALL for your help. This is the only thing stopping me to finish to project.
You’re leaking memory. Have you tried resizedBitmap.recycle()?
Also why do you always redraw map? Maybe you could have a map as a static image and redraw only pin?