转动手机会导致内存不足



我有一个非常简单的Activity,它在Canvas中的另一个可绘制对象上绘制可绘制对象,最后在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;
    }
}

这是因为您正在创建位图。无论何时旋转设备,它都将在不回收以前位图的情况下再次创建(因为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;
}
}

另请参阅

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

处理Android旋转

最新更新