代码之家  ›  专栏  ›  技术社区  ›  Alin

Android自定义视图位图大小超出虚拟机预算

  •  0
  • Alin  · 技术社区  · 15 年前

    我正在制作一个有两个元素的自定义视图:一个由位图(如地图)和小资源图像(如pin)组成的背景,用于指示地图上的位置。在每个gps位置上更改我的视图get的更新,然后pin移动到新位置。

    首先它工作正常,过了一会儿我发现这个错误:

    错误/AndroidRuntime(416): java.lang.OutOfMemory错误:位图 大小超出虚拟机预算

    我想这和一直重新画地图有关。也许你有什么办法帮我?

    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();
     }
    }
    

    @我试过这样的方法:

    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 get经常被调用。图像地图为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)
    

    @彼得·克内戈,我在你的回答中编辑过。它在寻找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提到的,有没有一种方法可以让我只在创建时绘制地图图像,然后只在raw上更新pin位置?这似乎是更方便的方式,因为重新绘制地图只使用资源,而且一直都是一样的。

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

    3 回复  |  直到 15 年前
        1
  •  1
  •   Fedor    15 年前

    你在泄漏记忆。你试过resizedBitmap.recycle()吗?

    为什么你总是重画地图?也许你可以用一张地图作为静态图像,只重绘pin?

        2
  •  2
  •   Peter Knego    15 年前

    避免引用 Drawables (位图)-删除位图类型的类字段。

    阅读如何 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();
        }
    }
    
        3
  •  0
  •   Sando    15 年前

    使用解码资源(res、id、opts) 对于opts,使用下面的代码 BitmapFactory.Options bfOptions=新建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];