0

My app sometimes crashes with an outOfMemoryError. I'm trying to handle it gracefully rather than crash the app. Here is the Logcat of the error, and then my try/catch block:

EDIT This question is about exception handling, not displaying images efficiently. I am sampling my images to an efficient size upon loading. However on an old phone it still may crash, as the user is able to add images manually to the activity. I want a message to come up saying they have added their limit instead of a crash. Thanks.

02-14 09:46:11.833: E/dalvikvm-heap(8495): Out of memory on a 2424016-byte allocation.
02-14 09:46:11.833: I/dalvikvm(8495): "main" prio=5 tid=1 RUNNABLE
02-14 09:46:11.833: I/dalvikvm(8495):   | group="main" sCount=0 dsCount=0 obj=0x40fc3508 self=0x40fb2ff8
02-14 09:46:11.833: I/dalvikvm(8495):   | sysTid=8495 nice=0 sched=0/0 cgrp=apps handle=1074818864
02-14 09:46:11.838: I/dalvikvm(8495):   | schedstat=( 10854840575 3262089934 15014 ) utm=971 stm=113 core=1
02-14 09:46:11.838: I/dalvikvm(8495):   at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
02-14 09:46:11.838: I/dalvikvm(8495):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:625)
02-14 09:46:11.838: I/dalvikvm(8495):   at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:478)
02-14 09:46:11.838: I/dalvikvm(8495):   at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:501)
02-14 09:46:11.838: I/dalvikvm(8495):   at com.btf271.imagehelper.ImageHelper.decodeSampledBitmapFromResource(ImageHelper.java:218)
02-14 09:46:11.838: I/dalvikvm(8495):   at com.btf271.multitouchimages.MultitouchImagesView$Img.load(MultitouchImagesView.java:285)
02-14 09:46:11.838: I/dalvikvm(8495):   at com.btf271.multitouchimages.MultitouchImagesView.loadImages(MultitouchImagesView.java:134)
02-14 09:46:11.838: I/dalvikvm(8495):   at com.btf271.fashionassistant.DressingRoomActivity.onResume(DressingRoomActivity.java:125)
02-14 09:46:11.843: I/dalvikvm(8495):   at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1199)
02-14 09:46:11.843: I/dalvikvm(8495):   at android.app.Activity.performResume(Activity.java:5280)
02-14 09:46:11.843: I/dalvikvm(8495):   at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2629)
02-14 09:46:11.843: I/dalvikvm(8495):   at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2667)
02-14 09:46:11.843: I/dalvikvm(8495):   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1279)
02-14 09:46:11.843: I/dalvikvm(8495):   at android.os.Handler.dispatchMessage(Handler.java:99)
02-14 09:46:11.843: I/dalvikvm(8495):   at android.os.Looper.loop(Looper.java:137)
02-14 09:46:11.843: I/dalvikvm(8495):   at android.app.ActivityThread.main(ActivityThread.java:4921)
02-14 09:46:11.843: I/dalvikvm(8495):   at java.lang.reflect.Method.invokeNative(Native Method)
02-14 09:46:11.843: I/dalvikvm(8495):   at java.lang.reflect.Method.invoke(Method.java:511)
02-14 09:46:11.843: I/dalvikvm(8495):   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
02-14 09:46:11.843: I/dalvikvm(8495):   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
02-14 09:46:11.843: I/dalvikvm(8495):   at dalvik.system.NativeStart.main(Native Method)
02-14 09:46:11.843: A/libc(8495): Fatal signal 11 (SIGSEGV) at 0x0000bed8 (code=1), thread 8495 (ashionassistant)
02-14 09:46:22.023: I/ActivityThread(8849): Pub com.btf271.ImageHelper.contentproviders.media: com.btf271.imagehelper.MediaContentProvider

Method causing the error: MultitouchImagesView.loadImages(MultitouchImagesView.java:134):

/** Called by activity's onResume() method to load the images */
public void loadImages(Context context) {
    Resources res = context.getResources();
    int n = mImages.size();
    for (int i = 0; i < n; i++){
        try{
        mImages.get(i).load(res);
        }
        catch(OutOfMemoryError oom){
            Log.d("out of memory", "out of memory");
        }
        catch( Exception ex) {
            Log.d("general exception", ex.getMessage());
        }
    }
}

NKN's answer explains why it crashes well. So is there a solution?

More information about the app. The user adds one image at a time to an activity, for as many images as they want. So I will cap it off at the 6th image to avoid outOfMemory exceptions. But can I check for outOfMemory exceptions each time they add an image to the activity, just to be fail-safe, in case one phone cannot handle 6 images? (Some phones can handle 30 images, but some might only handle around 6).

user3164083
  • 1,023
  • 6
  • 18
  • 34

4 Answers4

0

It crashes despite the try/catch block because you already have run out of memory. Some of your image(s) is/are consuming all the memory you have left and when that happens, though you try to handle it, there's no way as you haven't any free memory so your app simply dies.

I'd remove every image in your drawable folder and go one by one adding them so you can easily detect what's the painful image and manage it correctly. I recommend reading this.

nKn
  • 13,452
  • 9
  • 38
  • 58
  • Thanks, I've read that lesson you posted. I don't think I can remove the images and add them one by one because they have a lot of positioning and size information that needs to be kept when loaded. Is there any other approach? – user3164083 Feb 13 '14 at 20:58
  • You could simply make a copy of your drawable folder, and replace every image with a "dummy" 1Kb image with the same name than the one you're replacing. It doesn't matter if layout is not loaded correctly, it's just for debugging. If you replace the images one by one you'll quickly see which one's producing that OOM error. – nKn Feb 13 '14 at 21:00
  • Hmm. I'm not sure this will work for my app. I think you might need more information about my app. I am adding images to an activity manually as a user. The user can just keep going adding more and more, but I will enforce a stop at say, the 6th image. But Since each phone is different I would like to check for exceptions each time the user adds one, just in case they cannot make it to 6 images. Is what you were saying a valid way of doing it in my situation? Thanks. – user3164083 Feb 13 '14 at 21:05
  • The user adds them one at a time. So I do know what one causes the exception. – user3164083 Feb 13 '14 at 21:09
  • So I was thinking more along the lines of catching the error somewhere else, like earlier, which is way less work. If that is possible. Thanks – user3164083 Feb 13 '14 at 21:17
  • In that case seems more difficult to debug this. In any case, you could simply measure how much each object is in size, and although I don't know the circumstances of the image causing that OOM error, probably it could have something to do with the image size, so limiting it is an option. – nKn Feb 13 '14 at 21:23
  • Thanks. I have limited each of the images added size by sampling it, a technique used in the lesson you posted. I know the size of each image. At some point though, it will crash on all phones. On some after 8 images, on some after 50 images. I could cap it at 6 images and should be fine, but I'm wanting to double check. I will update my question with more info about my app and see if there is a solution. Thanks for your input and identifying the problem. – user3164083 Feb 13 '14 at 21:29
  • You should definitely put that limit on, as you say, there will always be one point on all phones that will make it crash, so it's up on you to limit it. Keep in mind that an app has normally 16Mb of heap, so do some numbers and you'll have an average estimation. Having that, you could simply store the images into some external storage, this way you don't have them loaded into memory but this also means that each time the user wants to see one, you'd need to load it and that can make your app pretty slow. – nKn Feb 13 '14 at 21:34
  • My app generally needs to use more than 16Mb of memory. It gets to 30Mb pretty quick. I have made it "minSdkVersion = 11" to avoid having the older devices using it. Your idea about the external storage could be a good one. I don't mind about performance too much. I will have a look into that as I have no idea how to do it. – user3164083 Feb 13 '14 at 21:44
  • can they be stored externally only and be displayed on the screen? If so do you have a tutorial in mind? Thanks. – user3164083 Feb 13 '14 at 21:46
  • ehhh probably not actually as I move the images around and resize them with my fingers, so they are drawn rapidly to the screen to show the changes smoothly. Thanks anyway – user3164083 Feb 13 '14 at 21:49
  • Sure. I'd store the images you're not showing in the current layout and, if needed, simply delete them once the user closes your app, so it would be somewhat a temporary storage. This link helped me so much with it http://stackoverflow.com/questions/7887078/android-saving-file-to-external-storage – nKn Feb 13 '14 at 21:49
  • The images I'm not showing are not in my layout so they are not taking up any memory. They are only added to the layout when the user adds them. They are just stored within my app resources in the drawable folder, sitting there doing nothing. My setup of my activity is a bit unique like that. So this technique won't help, but thanks for having a think about some other approaches to resolving the issue. – user3164083 Feb 13 '14 at 21:56
0

Try dumping your app's garbage from memory by defining a global exception hanlder

public class MyUncaughtExceptionHandler implements
        Thread.UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (ex.getClass().equals(OutOfMemoryError.class)) {
            try {
                android.os.Debug.dumpHprofData("/sdcard/dump.hprof");
            } catch (IOException e) {
                Log.d("SD ExceptionHandler", ex.getMessage().toString());
            }
        } else {
            Log.d("SD ExceptionHandler", ex.getMessage().toString());
        }
    }

}

Then in your Activity's onCreate call:

Thread.currentThread().setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
mmbrian
  • 1,677
  • 2
  • 14
  • 23
  • Thanks. This code is a bit foreign to me, as I'm a student. Would I copy this word for word, even the "/sdcard/dump.hprof"? Does this code dump the garbage from memory, so, it would be good to use it in the onCreate() of each of my activities? – user3164083 Feb 13 '14 at 21:40
  • Thanks! I get this warning: Do not hardcode "/sdcard/"; use Environment.getExternalStorageDirectory().getPath() instead. Should I do that? – user3164083 Feb 13 '14 at 22:06
  • yeah that's a better idea. but it's just a warning ;) – mmbrian Feb 13 '14 at 22:08
  • Thanks. I got another warning that it should be accessed in a static way when I called it. I originally had Thread.currentT.... Then I changed it to `Thread.currentThread(); Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());` as per the Eclipse suggestion. – user3164083 Feb 13 '14 at 22:10
  • did that help with the bug? – mmbrian Feb 13 '14 at 22:56
  • Not particularly, but I will still use the code, it might be doing some cleanup. Thanks. I don't really have a bug. I just need to do exception handling in case exceptions are thrown, as the user can add lots of images if they want, I'd like to make it fail-safe rather than crudely capping it at 6 images and hoping for the best. – user3164083 Feb 13 '14 at 23:12
  • It never seems to catch. It doesn't fire or log anything when the app crashes. – user3164083 Feb 13 '14 at 23:46
0

You need to set this flag in your manifest as an Application attribute:

android:largeHeap="true"

since you are targeting SDK 11 or higher, it should work.

Android Heap memory increase

Community
  • 1
  • 1
Jim
  • 9,902
  • 1
  • 23
  • 35
0

Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the camera on the Galaxy Nexus takes photos up to 2592x1936 pixels (5 megapixels). If the bitmap configuration used is ARGB_8888 (the default from the Android 2.3 onward) then loading this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the per-app limit on some devices.

http://developer.android.com/training/displaying-bitmaps/index.html

You are running out of memory because you are not handling images carefully. Take a look at following question/answer for a better idea what happens when you try to load an image: Strange out of memory issue while loading an image to a Bitmap object

If you are a new developer then I would recommend you to just use any good library that does loading part for you otherwise it would be very hard to load images.

Android-Universal-Image-Loader: Widely used library https://github.com/nostra13/Android-Universal-Image-Loader

Alternative Picasso Library: One line of code does the job for you: http://square.github.io/picasso/

Community
  • 1
  • 1
Sharj
  • 14,733
  • 12
  • 55
  • 87
  • People keep thinking I'm not displaying bitmaps efficiently, but I have read the article on Android Developer Training and implemented the techniques. Even with Picasso I could get the exception thrown after the user has added a lot of images. I just need exception handling to let them know when they have added their limit of images. Any phone will crash at some point. Thanks – user3164083 Feb 14 '14 at 01:19
  • @user3164083 Just so you know I handle 1000+ photos in one activity using Android-Universal-Image-Loader and without crashing. – Sharj Feb 14 '14 at 01:24
  • That will be a gridview, I do that without any library in this app. I need these images to be in memory at all times for this activity because it's not a grdview, they never go off the screen. Much different story. – user3164083 Feb 14 '14 at 01:26
  • you might find this usefull http://stackoverflow.com/questions/2679330/catching-java-lang-outofmemoryerror – Sharj Feb 14 '14 at 01:28
  • Thanks that was useful. I think in my scenario it is fine to catch the error, eg, When the error is thrown - when the user has added their limit of images, don't add the last one that will crash the app. Maybe Picasso would help but I don't see why, when I am already sampling the image, and I cannot recycle them or do anything like that anyway, as they stay on the screen, all that could be done is sampling. – user3164083 Feb 14 '14 at 01:40
  • My experience is that you should try to avoid that error instead of handling it. Application should not be crashing just by showing 6-10 photos on screen all the time. You either have to optimize your code or use library which has optimized code contributed by hundreds of developers. – Sharj Feb 14 '14 at 01:48
  • Thanks. It should not crash even if those photos are not thumbnails, they are full photos (smapled to around 400x400 pixels)? – user3164083 Feb 14 '14 at 01:52
  • Yes, even if they are 400x400 application should not crash. You can try yourself, Picasso needs just one line of code to test. – Sharj Feb 14 '14 at 01:54
  • Thanks for your suggestion. I will keep Picasso in mind. When open my app, then click one button to go to a different activity with no images, it grows the heap to 11mb. All the activity has is a gradient background and 2 editTexts and a button. I don't think my images are inefficient. The heap grows a lot without any images. Maybe it's just how apps are. My app doesn't even crash, I was just being careful, I think it will be fine how it is. It's finished now so to change something like how I load images is a huge job. – user3164083 Feb 14 '14 at 02:04