1

I'm a beginning Android developer, and as a practice project, I'm trying to make and activity which can take a picture, save it to the external storage, and display it in an ImageView. Almost everything seems to be working, however, I appear to have a memory leak.

When the screen orientation changes, I believe the activity is destroyed, then recreated. In order to continue displaying the image, I am storing the path to the image as a string in a bundle in onSaveInstanceState and resetting the image in onResotreInstanceState. Taking 5mp pictures, I can rotate the screen once, and then on the second rotation, the app crashes, and LogCat reports an out of memory error. With lower resolution images, I get more rotations, but the app still eventually crashes and I get the same error.

I've been reading up on android memory leaks, and everything seems to say not to use static Drawables, which can have references to the view, and prevent the vm from releasing the memory. As far as I can tell, I'm not doing anything like this that would keep holding references. If any one can help me locate my error, I'd really appreciate it. Here is the code:

public class CameraTestsActivity extends Activity {

private Uri fileUri;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}

public void takePicture(View view){
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    fileUri = getOutputImageFileUri();
    intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

    startActivityForResult(intent, 0);
}

private static Uri getOutputImageFileUri() {
    return Uri.fromFile(getOutputImageFile());
}

private static File getOutputImageFile(){
    Log.d("CameraTestsActivity", "Storage state: " + Environment.getExternalStorageState());
    if (Environment.getExternalStorageState().equals("mounted")){
        File mediaStorageDirs = new File (Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pictures" + "/CameraTestsActivity");
        if (! mediaStorageDirs.exists()){
            if (! mediaStorageDirs.mkdirs()){
                Log.d("CameraTestsActivity", "Failed to create directories");
                mediaStorageDirs = null;
                return null;
            }
        }

        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        File imageFile = new File(mediaStorageDirs.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
        mediaStorageDirs = null;
        return imageFile;


    }
    Log.d("CameraTestsActivity", "Storage state bad");
    return null;
}



@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == RESULT_OK){
        if (requestCode == 0){
            setImage();
        }
    }
    else{
        super.onActivityResult(requestCode, resultCode, data);
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    fileUri = Uri.parse(savedInstanceState.getString("uri"));
    setImage();
    super.onRestoreInstanceState(savedInstanceState);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("uri", fileUri.toString());
    super.onSaveInstanceState(outState);
}

private void setImage(){
    ImageView image = (ImageView)findViewById(R.id.imageView1);
    File file = new File(fileUri.toString().substring(7));
    if (!file.exists())
        Log.d("CameraTestsActivity", "File " + fileUri.toString().substring(7) + "does not exist");
    image.setImageURI(fileUri);
}

}
Chris
  • 343
  • 3
  • 9

2 Answers2

1

While displaying bitmap we should be careful that its size does not exceed the Heap size or say VM Budget. Although you do not have any memory leak but when you have changing your orientation then may be its taking some time to clean previously loaded bitmap so you get memory overflow error. For avoiding this error please read this How to display bitmap efficiently

Tofeeq Ahmad
  • 11,935
  • 4
  • 61
  • 87
  • Thanks, this really helped. I realized that it only crashed if I turned the device back and forth too quickly - if I did it slowly, it didn't run out of memory, making me think that the GC was not happening when I needed it to, and that the images were too large to be handled so quickly. I'll keep playing with it, but scaling the images seems to have really helped. – Chris May 21 '12 at 21:01
  • about bitmaps , on API 10- (below honeycomb) , you should call recycle() . otherwise , the OS should take care of the bitmap and free it for you just fine. – android developer Jul 15 '12 at 23:25
0

Try analyze your app's memory to find where the leaks are, here are some links: link1, link2

You can also try manually release the previous Activity's Bitmap by removing it from your ImageView and calling Bitmap.recycle() when your Activity's onStop() is called, since pre Honycomb the time that a Bitmap's backing buffer can be released is quite indeterministic.

Community
  • 1
  • 1
Kai
  • 15,284
  • 6
  • 51
  • 82
  • Thanks, I had seen the first link before, but the second really helped me get everything set up to examine the heap. Ultimately, it looks like there wasn't really a leak, because if I rotated infrequently enough, there was no issue. Now that I am scaling the image before it gets loaded, the issue is completely gone - I no longer see an escalating heap size in DDMS. – Chris May 21 '12 at 22:46