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

Android自定义视图位图内存泄漏

  •  18
  • Alin  · 技术社区  · 14 年前

    我有一个自定义视图,其中我需要绘制两个位图,一个是背景,表示一个地图的图像,另一个是将在画布的顶部/左侧位置绘制的图钉。

    这两个图像都是在Draw上绘制的,并且在包含视图的活动进行期间保持不变。过了一会儿我得到一个

    OutOfMemory错误:位图大小超过 虚拟机预算

    这意味着我有漏洞,位图不会被垃圾收集。我以前问过这个问题,但现在情况有了一点变化。我做了一个init方法,在那里我设置了我想使用的位图,但这仍然不是一个好的方法,错误出现在稍后,但仍然存在。

    这是密码

    public class MyMapView extends View {
    private int xPos = 0;
    private int yPos = 0;
    private int space = 0;
    
    private Bitmap resizedBitmap;
    private Bitmap position;
    private Bitmap mapBitmap;
    
    public void setMapBitmap(Bitmap value) {
        this.mapBitmap =  value;
    }
    
    public MyMapView(Context context) {
     super(context);
    }
    
    public MyMapView(Context context, AttributeSet attrs) {
     super(context, attrs);
    }
    
    public MyMapView(Context context, AttributeSet attrs, int defStyle) {
     super(context, attrs, defStyle);
    }
    
    public void init(final Context context) {
     Paint paint = new Paint();
        paint.setFilterBitmap(true);
    
        int width = getMeasuredWidth();
        int height = getMeasuredHeight();
    
        resizedBitmap = Bitmap.createScaledBitmap(mapBitmap, height, height, true);
        position = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.position);
        space = (width - resizedBitmap.getWidth()) / 2;
    }
    
    public void destroy()
    {
     resizedBitmap.recycle();
     resizedBitmap=null;
     position.recycle();
     position=null;
     mapBitmap.recycle();
     mapBitmap=null;
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
    
         if (mapBitmap != null) {
             canvas.drawBitmap(resizedBitmap, space, 0, null);
         }
    
         if (xPos != 0 && yPos != 0) {
             canvas.translate(xPos + space - position.getWidth() / 2, yPos - position.getHeight() / 2);
             canvas.drawBitmap(position, new Matrix(), new Paint());
    
         }
    }
    
     public void updatePosition(int xpos, int ypos)
     {
       xPos = xpos;
          yPos = ypos;
          invalidate();
     }
    }
    

    我在创建包含视图的活动时调用setMapBitmap()和init()。在那里我知道什么位图将被用作地图。活动暂停时,我调用视图的destroy()。我还是会犯错。 我读过这个 this 但我不知道如何适应我的情况

    我愿意接受任何其他解决办法。请注意,我需要调整地图位图的大小(基于mai活动中包含它的布局的高度),并且仍然能够在正确的位置绘制位置。

    2 回复  |  直到 14 年前
        1
  •  12
  •   Community CDub    8 年前

    你显然有内存泄漏。阅读如何 avoid memory leaks 以及如何 find memory leaks .

    下面是一个您的代码重构以使用WeakReference:

    public class MyMapView extends View {
        private int xPos = 0;
        private int yPos = 0;
        private int space = 0;
    
        private WeakReference<Bitmap> resizedBitmap;
        private WeakReference<Bitmap> position;
        private WeakReference<Bitmap> mapBitmap;
    
        public void setMapBitmap(Bitmap value) {
            this.mapBitmap = new WeakReference<Bitmap>(value);
        }
    
        public MyMapView(Context context) {
            super(context);
        }
    
        public MyMapView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MyMapView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public void init(Bitmap mapBitmap) {
            Paint paint = new Paint();
            paint.setFilterBitmap(true);
    
            int width = getMeasuredWidth();
            int height = getMeasuredHeight();
    
            resizedBitmap = new WeakReference<Bitmap>(Bitmap.createScaledBitmap(
                mapBitmap, width, height, true));
            position = new WeakReference(BitmapFactory.decodeResource(
                getContext().getResources(), R.drawable.position));
            space = (width - resizedBitmap.get().getWidth()) / 2;
        }
    
    //    public void destroy() {
    //        resizedBitmap.recycle();
    //        resizedBitmap = null;
    //        position.recycle();
    //        position = null;
    //        mapBitmap.recycle();
    //        mapBitmap = null;
    //    }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
                MeasureSpec.getSize(heightMeasureSpec));
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
    
            if (mapBitmap != null) {
                canvas.drawBitmap(resizedBitmap.get(), space, 0, null);
            }
    
            if (xPos != 0 && yPos != 0) {
                canvas.translate(xPos + space - position.get().getWidth() / 2, 
                    yPos - position.get().getHeight() / 2);
                canvas.drawBitmap(position.get(), new Matrix(), null);
    
            }
        }
    
        public void updatePosition(int xpos, int ypos) {
            xPos = xpos;
            yPos = ypos;
            invalidate();
        }
    }
    
        2
  •  4
  •   Jason Rogers    14 年前

    在你的代码中标记我的是你没有回收所有的位图。以下是一些可以帮助您的代码片段:

    bmp = getBitmapFromRessourceID(R.drawable.menu);
    ratio = (float)this.height/(float)bmp.getScaledHeight(canvas);
    temp = Bitmap.createScaledBitmap(bmp, (int)(bmp.getWidth()*ratio), (int) (bmp.getHeight()*ratio-10), false);
    bmp.recycle();
    bmp = temp;
    

    以及:

    Bitmap temp = BitmapFactory.decodeResource(context.getResources(), resource, opts);
    Bitmap bmp = Bitmap.createBitmap(temp, 0, 0, temp.getWidth(), temp.getHeight(), flip, true);
    temp.recycle();