OutOfMemoryError is common in Android app development, and so we get lots of questions about it in Stack Overflow and other developer support sites .
OutOfMemoryError message may indicate that you are asking for a huge block of memory (e.g., 36000012 bytes). If so, check the stack trace. Often, the stack trace will indicate that the problem is coming from some sort of resource, whether applied to an
ImageView or as a background to some widget or container, such as:
java.lang.OutOfMemoryError: Failed to allocate a 36000012 byte allocation with 16777216 free bytes and 21MB until OOM … at android.support.v7.widget.AppCompatImageView.setImageResource(AppCompatImageView.java:72)
In this case, we know that the allocation is tied into loading some drawable resource.
Since the default is four bytes per pixel (a.k.a.,
ARGB_8888 ), you can come up with a rough idea of how large the image is, by dividing the allocation by 4 to get total pixels, then taking the square root to get the width and height of a square that consumes that number of pixels. In the case of 36000012, √(36000012/4) is about 3000. So, we know that the image is the equivalent of 3000px by 3000px, though it might be a rectangle (e.g., 4500px by 2000px) instead.
At this point, there are two possibilities. One is that the drawable resource really is that large, in which case the solution is to not have nearly that large of a drawable resource. Note that by “large”, I mean size in terms of resolution. Due to image compression, a high-resolution image might still be a fairly small PNG or JPEG file on disk.
However, more often than not, the actual drawable resource is not nearly that big, but it is put into the wrong resource directory. And, a common culprit there is
As I noted ina previous blog post,
res/drawable/ is an ancient synonym for
res/drawable-mdpi/ . Android will interpret resources in
res/drawable/ as being configured for
-mdpi screens (~160dpi). Unfortunately, too many developers do not understand the meaning of these suffixes, or think that
res/drawable/ is some sort of “universal drawable directory”, where density rules do not apply (for that, use
res/drawable-anydpi/ , as that earlier blog post described).
Let’s suppose that our 36000012-byte allocation is coming from a resource in
res/drawable/ . And, let’s suppose that we are testing this on an
-xxhdpi device. That’s around ~480dpi. Android assumes that
res/drawable/ holds images sized for
-mdpi . When Android sees that we are on
-xxhdpi , Android assumes that it needs to upscale the image, so that the drawable will be about the same physical size (inches, millimeters, parsecs, whatever). So, Android will upscale from ~160dpi (
-mdpi ) to ~480dpi (
-xxhdpi ), increasing both axes by a factor of three.
The 36000012-byte allocation is for the post-scaling image. The actual drawable resource, therefore, might be more like 1000px by 1000px, which then gets upscaled to 3000px by 3000px, and fails at that point.
Given all that, what do you do?
First, recognize that 1000px by 1000px (or the equivalent for a non-square image) is pretty big in its own right. Android runs on lots of low-end devices, for which this image would be too big anyway, even if we ignore density. Find ways to either get rid of the image or reduce its size in pixels.
Second, move the resource from
res/drawable/ to somewhere else. Where the “somewhere else” is depends on how you want the resource to be treated:
If you want the resource to be scaled based upon density, put this one in
res/drawable-mdpi/, to remind you that this is the
-mdpiversion of the image. Then, consider creating other renditions of this drawable, pre-scaled to other densities (e.g.,
-xxhdpi), so Android does not have to do that work at runtime and so you have greater control over the image quality.
If you do not want the resource to be scaled based upon density, put it in
res/drawable-anydpi(where I will steer you to that previous blog post for a discussion of the differences between those two)
Want an expert opinion on your Android app architecture decisions? Perhaps Mark Murphy can help !