1

I have a program that is supposed to grab an image from a user's gallery and then display the image in a subsequent activity. As part of my testing, I try to do the operation twice in a row: in ViewImageActivity I click on the button that takes me to the gallery; pick an image; then see the image in FriendsActivity. Then I click the back button and repeat the process. The second time around I always get a ava.lang.OutOfMemoryError. I am including the error log:

04-26 09:43:57.911: W/dalvikvm(30841): threadid=1: thread exiting with uncaught exception (group=0x40c231f8)
04-26 09:43:57.918: E/AndroidRuntime(30841): FATAL EXCEPTION: main
04-26 09:43:57.918: E/AndroidRuntime(30841): java.lang.OutOfMemoryError
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:493)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:299)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:324)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at com.example.game.utils.FileUtils.imageFromGallery(FileUtils.java:87)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at com.example.game.utils.FileUtils.unmarshallBitmap(FileUtils.java:71)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at com.example.game.FriendsActivity.onCreate(FriendsActivity.java:46)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.app.Activity.performCreate(Activity.java:4638)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1051)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1940)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2001)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.app.ActivityThread.access$600(ActivityThread.java:129)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1153)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.os.Handler.dispatchMessage(Handler.java:99)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.os.Looper.loop(Looper.java:137)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at android.app.ActivityThread.main(ActivityThread.java:4516)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at java.lang.reflect.Method.invokeNative(Native Method)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at java.lang.reflect.Method.invoke(Method.java:511)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:558)
04-26 09:43:57.918: E/AndroidRuntime(30841):    at dalvik.system.NativeStart.main(Native Method)
04-26 09:44:00.997: I/Process(30841): Sending signal. PID: 30841 SIG: 9

My program is written like so:

ViewImageActivity dispatch intent to get photo from image gallery; then onActivityResult calls FriendsActivity. FriendsActivity in turn calls FileUtils's static method unmarshallBitmap to get the image for displaying. See following snippets:

ViewImageActivity:

public void dispatchGalleryIntent(View view) {
    Intent gallery = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(gallery, LOAD_IMAGE_REQUEST_CODE);
}

 @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        if (requestCode == LOAD_IMAGE_REQUEST_CODE) {
            imageUri = data.getData();
        }
        dispatchIntentToFriendsActivity();
    }
}

private void dispatchIntentTo FriendsActivity() {
    Intent intent = new Intent(this, FriendsActivity.class);
    intent.putExtra(IMAGE_URI, imageUri);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    startActivity(intent);
}

FriendsActivity

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_friends);
    Uri imageUri = (Uri) getIntent().getExtras().get(IMAGE_URI);
    myImage = FileUtils.unmarshallBitmap(imageUri, getContentResolver());

      ...
}

FileUtils

public static Bitmap unmarshallBitmap(Uri imageUri, ContentResolver resolver) {
   return imageFromGallery(imageUri, resolver);
}

private static Bitmap imageFromGallery(Uri imageUri, ContentResolver resolver) {
        try {
            String[] filePathColumn = { MediaStore.Images.Media.DATA };
            Cursor cursor = resolver.query(imageUri, filePathColumn, null, null, null);
            cursor.moveToFirst();
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            String picturePath = cursor.getString(columnIndex);
            cursor.close();
            return BitmapFactory.decodeFile(picturePath);
        } catch (Exception x) {
            return null;
        }
    }

BTW: I already saw How to solve the Out of Memory issue while displaying image in android?. My image loads fine the first time. The problem is if user change their mind and decide to choose a different image (back button and repeat), then the app crash as explained above.

Community
  • 1
  • 1
learner
  • 11,490
  • 26
  • 97
  • 169
  • 2
    You should prepare the image for display by loading it with options.inJustDecodeBounds = true; and then adjusting the samplesize :http://developer.android.com/training/displaying-bitmaps/load-bitmap.html but apart from that, are you calling bitmap.recycle() when you no longer need the bitmap? The first could still be in memory when you load the second, causing the error. – Matt Apr 26 '13 at 17:33
  • @Matt besides in onDestroy which does not help and in onResume which complains about trying to use a recycled bitmap, where else might I call recycle for it to work? – learner Apr 26 '13 at 17:58
  • @Matt many thanks for the link. I am reading ... – learner Apr 26 '13 at 18:10
  • @Matt, it's working now :). Do you mind posting your comment as I response so I may mark this post as resolved? Thanks. – learner Apr 26 '13 at 19:25

2 Answers2

1

Recycle the currently loaded bitmap image when the user presses the back button and goes of a different image

myImage.recycle();
deepdroid
  • 633
  • 5
  • 26
  • I already tried `myImage.recycle();` in onDestroy [on effect, still out of memory]; in onResume [app complains about trying to use a recycled bitmap]. – learner Apr 26 '13 at 17:56
  • Are you recreating the bitmap every time ? Once it is recycled you can not use the same bitmap again as it is already marked dead. Can you try doing this in onResume: myImage = FileUtils.unmarshallBitmap(imageUri, getContentResolver()); check condition as !isRecycled() and != null before the operation – deepdroid Apr 26 '13 at 18:17
1

You should prepare the image for display by loading it with options.inJustDecodeBounds = true; and then adjusting the samplesize.

See: http://developer.android.com/training/displaying-bitmaps/

Apart from that, are you calling bitmap.recycle() when you no longer need the bitmap? The first could still be in memory when you load the second, causing the error.

Matt
  • 492
  • 2
  • 12