【问题标题】:Rotating the phone causes memory out of memory旋转手机导致内存不足
【发布时间】:2013-08-02 13:02:17
【问题描述】:

我有一个非常简单的Activity,它在Canvas 中的另一个drawable 上绘制一个drawable,最后在ImageView 上显示它。我知道创建位图需要太多内存并按比例缩小,我知道通过保持对上下文的引用来泄漏,但无法检测到它发生在哪里。现在经过 4-5 次旋转后,我出现内存不足错误。 (我已经在代码中指定了)

你能帮我找出发生泄漏的地方吗?

这是我的代码:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DisplayMetrics displaymetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
        int height = displaymetrics.heightPixels;
        int width = displaymetrics.widthPixels;


        // image view in main layout to be filled by raw bitmaps combination
        ImageView imageView = (ImageView) findViewById(R.id.image);

        // Get your images from their files
        // raw bitmaps taken from drawable folder
        //Bitmap bottomImage = BitmapFactory.decodeResource(getResources(), R.drawable.arsenal);
        //Bitmap topImage = BitmapFactory.decodeResource(getResources(), R.drawable.setare);

        Log.v("imageView", String.valueOf(imageView.getWidth()) + " " + imageView.getHeight());

        Bitmap bottomImage = decodeSampledBitmapFromResource(getResources(),
                R.drawable.arsenal, width, (int)0.75 * width);

        Bitmap topImage = decodeSampledBitmapFromResource(getResources(),
                R.drawable.setare, 400, 400);

        // a copy of the below bitmap that is mutable.

        Bitmap temp = bottomImage.copy(Bitmap.Config.ARGB_8888, true); /// ?! I get error here! 

        // not necessary, only for testing whether is possible to diractly cache a text view or not
        TextView t = (TextView) findViewById(R.id.text);
        t.setDrawingCacheEnabled(true);

        // canvas for drawing functions
        Canvas comboImage = new Canvas(temp);
        // Then draw the second on top of that
        comboImage.drawBitmap(topImage, 400f, 400f, null);


        // a paint to determine style of what would be drawn in canvas.
        Paint p = new Paint();
        p.setColor(Color.YELLOW);
        p.setStyle(Style.FILL);
        p.setTextSize(70);

        // manually draw a text on canvas
        comboImage.drawText("Ehsan Mirza Razi", 100, 100, p);

        // draw text view directly on canvas. 
        //by now causes out of memory exception 
        //comboImage.drawBitmap(t.getDrawingCache(), 1000f, 200f, new Paint());

        // drawing the temp drawable edited in canvas, on ImageView
        imageView.setImageDrawable(new BitmapDrawable(getResources(), temp));

        // To write the file out to the SDCard:
        OutputStream os = null;
        try {
            os = new FileOutputStream("myNewFileName.png");
            bottomImage.compress(CompressFormat.PNG, 100, os);
            topImage.recycle();
            bottomImage.recycle();
            temp.recycle();
        } catch(IOException e) {
            e.printStackTrace();
        }

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        Log.v("shrink", String.valueOf(inSampleSize));
        return inSampleSize;
    }

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
            int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();

        //ImageView = null;

    }
}

【问题讨论】:

    标签: android memory-leaks bitmap rotation drawable


    【解决方案1】:

    这是因为您正在创建位图。并且每当您旋转设备时,它将再次创建而不回收以前的位图(因为在您旋转设备时再次调用 onCreate())。 所以试试这种方式-

     public class MainActivity extends Activity {
      Bitmap bottomImage,topImage,temp;
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        if(bottomImage!=null){
           bottomImage.recycle();
           bottomImage=null;
         }
          if(topImage!=null){
           topImage.recycle();
           topImage=null;
         }
         if(temp!=null){
           temp.recycle();
           temp=null;
         }
    
        DisplayMetrics displaymetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
        int height = displaymetrics.heightPixels;
        int width = displaymetrics.widthPixels;
    
    
        // image view in main layout to be filled by raw bitmaps combination
        ImageView imageView = (ImageView) findViewById(R.id.image);
    
        // Get your images from their files
        // raw bitmaps taken from drawable folder
        //Bitmap bottomImage = BitmapFactory.decodeResource(getResources(), R.drawable.arsenal);
        //Bitmap topImage = BitmapFactory.decodeResource(getResources(), R.drawable.setare);
    
        Log.v("imageView", String.valueOf(imageView.getWidth()) + " " + imageView.getHeight());
    
        bottomImage = decodeSampledBitmapFromResource(getResources(),
                R.drawable.arsenal, width, (int)0.75 * width);
    
        topImage = decodeSampledBitmapFromResource(getResources(),
                R.drawable.setare, 400, 400);
    
        // a copy of the below bitmap that is mutable.
    
        temp = bottomImage.copy(Bitmap.Config.ARGB_8888, true); /// ?! I get error here! 
    
        // not necessary, only for testing whether is possible to diractly cache a text view or not
        TextView t = (TextView) findViewById(R.id.text);
        t.setDrawingCacheEnabled(true);
    
        // canvas for drawing functions
        Canvas comboImage = new Canvas(temp);
        // Then draw the second on top of that
        comboImage.drawBitmap(topImage, 400f, 400f, null);
    
    
        // a paint to determine style of what would be drawn in canvas.
        Paint p = new Paint();
        p.setColor(Color.YELLOW);
        p.setStyle(Style.FILL);
        p.setTextSize(70);
    
        // manually draw a text on canvas
        comboImage.drawText("Ehsan Mirza Razi", 100, 100, p);
    
        // draw text view directly on canvas. 
        //by now causes out of memory exception 
        //comboImage.drawBitmap(t.getDrawingCache(), 1000f, 200f, new Paint());
    
        // drawing the temp drawable edited in canvas, on ImageView
        imageView.setImageDrawable(new BitmapDrawable(getResources(), temp));
    
        // To write the file out to the SDCard:
        OutputStream os = null;
        try {
            os = new FileOutputStream("myNewFileName.png");
            bottomImage.compress(CompressFormat.PNG, 100, os);
            topImage.recycle();
            bottomImage.recycle();
            temp.recycle();
        } catch(IOException e) {
            e.printStackTrace();
        }
    
    }
    
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    
    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
    
        if (height > reqHeight || width > reqWidth) {
    
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
    
            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        Log.v("shrink", String.valueOf(inSampleSize));
        return inSampleSize;
    }
    
    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
            int reqWidth, int reqHeight) {
    
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
    
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }
    
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
    
        //ImageView = null;
    
    }
    }
    

    see this also

    【讨论】:

    • 感谢您帮助我。它有效,但对我提出了一个问题:bitmap.recycle() 和 bitmap = null 的效果是什么?如果活动在旋转时破坏,位图必须是垃圾。
    【解决方案2】:

    在每次旋转(纵向 -> 横向,反之亦然)时,都会重新创建活动,并且某些设备没有足够的内存来进行“粗略”模式下的转换。您应该检查以下链接,因为它解释了如何以一种很好的方式处理这个问题。 (恕我直言)

    Handling Android Rotation

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多