我正在制作一个包含2个元素的自定义视图:一个背景,它包含一个像地图一样的位图和一个像针一样的小资源图像,用于指示地图上的位置。在每个gps位置更改我的视图得到更新并且引脚移动到新位置。
首先,它很好,一段时间后我得到了这个错误:
ERROR / AndroidRuntime(416): java.lang.OutOfMemoryError:位图 大小超过VM预算
我猜,这与重绘地图有关。也许你有任何想法可以帮助我?
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:我尝试过这样的事情:
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);
}
}
但仍然如此。每次GPS检测到位置发生变化时,我都会从活动中调用updatePosition(..),因此经常会调用onDraw。图像映射为400x400像素
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我在你的回答中编辑了。找到positionRef和其他一些有问题。我更新了这样的代码:
@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();
}
}
现在它可以工作一段时间了。加倍的时间。但仍然给出了内存不足的错误。 非常感谢你的帮助。
我在想Fedor提到的,有没有一种方法我只在创作时绘制地图图像,然后只更新引脚位置onDraw?这似乎是更方便的方式,因为重绘地图只使用资源而且它始终是相同的。
谢谢大家的帮助。这是阻止我完成项目的唯一因素。
答案 0 :(得分:2)
避免保留对Drawables
(位图)的引用 - 删除Bitmap类型的类字段。
阅读avoid memory leaks的方法。
<强>编辑:强>
试试这个:
public class MyMapView extends View {
private int xPos = 0;
private int yPos = 0;
private WeakReference<Bitmap> trackMapRef;
private WeakReference<Paint> backgroundPaintRef;
private WeakReference<Bitmap> positionRef;
private WeakReference<Matrix> positionMatrixRef;
private WeakReference<Paint> positionPaintRef;
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) {
Paint paint = new Paint();
paint.setFilterBitmap(true);
backgroundPaintRef = new WeakReference<Paint>(paint);
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);
}
@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 (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);
canvas.translate(xPos + space - positionRef.get().getWidth() / 2, yPos - positionRef.get().getHeight() / 2);
canvas.drawBitmap(positionRef.get(), positionMatrixRef.get(), positionPaintRef.get());
canvas.restore();
}
}
public void updatePosition(int xpos, int ypos, Bitmap trackImage) {
xPos = xpos;
yPos = ypos;
trackMapRef = new WeakReference<Bitmap>(trackImage);
invalidate();
}
}
答案 1 :(得分:1)
你正在泄露记忆。你试过resizedBitmap.recycle()吗?
另外,为什么你总是重绘地图?也许你可以将地图作为静态图像并仅重绘针脚?
答案 2 :(得分:0)
使用decodeResource(res,id,opts) 并且opts使用下面的代码 BitmapFactory.Options bfOptions = new BitmapFactory.Options();
bfOptions.inDither=false; //Disable Dithering mode
bfOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
bfOptions.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
bfOptions.inTempStorage=new byte[32 * 1024];