旋转手机会导致内存不足

时间:2013-08-01 14:29:09

标签: android memory-leaks bitmap rotation drawable

我有一个非常简单的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;

    }
}

2 个答案:

答案 0 :(得分:1)

在每次旋转(纵向 - &gt;横向,反之亦然)时,将重新创建活动,并且某些设备在“原始”模式下没有足够的内存用于转换。您应该检查以下链接,因为它解释了如何以一种很好的方式处理它。 (IMHO)

Handling Android Rotation

答案 1 :(得分: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