Android Memory Leak, no static variables

2020-04-21 07:14发布

问题:

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);
}

}

回答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



回答2:

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.