Android自定义视图位图大小超过VM预算

时间:2010-11-13 17:05:19

标签: android out-of-memory

我正在制作一个包含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?这似乎是更方便的方式,因为重绘地图只使用资源而且它始终是相同的。

谢谢大家的帮助。这是阻止我完成项目的唯一因素。

3 个答案:

答案 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];