Bitmap is a very basic data structure for image files. Although they are very common, they are also notoriously expensive memory-wise. If you’re not careful, bitmaps can easily eat up the majority of the available memory for a device. This is particularly important on mobile devices like smartphones since they have a much more limited system memory in the first place. Loading too many bitmaps the wrong way can lead to a complete system crash, which is definitely not a point in favor of any app.
There are, however, processes and tools for loading bitmaps onto Android applications that can walk the line between using bitmaps, even larger ones, and running out of memory to work with. This article is an overview of the challenges associated with using bitmaps in Android applications. We will go through several important processes and topics for making them work while avoiding memory leaks, crashing or any other unpleasant things.
There are a few challenges associated with loading bitmaps onto Android devices. A lot of these have to do with irreconcilable facts of Android’s architecture and proprietary loading order. The first of these is that, as mentioned earlier, mobile devices almost always have very limited physical resources, with as little as 16 MB of memory allocated to a single application.
There are guidelines in the Android Compatibility Definition Document (CDD) which specify the minimum application memory for different sizes of screen and densities of pixels. It’s important to design with these standards in mind in order to operate under the memory limit. The next challenge has to do with bitmaps themselves. They are notorious for taking up a lot of memory, especially in their most common formats, which are rich images like photographs. To give an example, a photo taken with the camera on a Galaxy Nexus which is then converted to a bitmap and then loaded will immediately exhaust the amount of memory per app of most Android devices.
Finally, the last challenge is that user interface features on Android devices often require that multiple bitmaps are shown at once and that potentially more are available close at hand. This means that not all the memory can be poured into a single image that will immediately reach the memory limit.
A first important method of loading bitmaps on Android devices is to optimize images for the screens that they will be displayed on. A lot of images out there are of a much larger size than they really need to be in order to be displayed on a mobile device of any kind, and loading the bigger image directly can also eat up valuable memory. The general idea here is to, instead of uploading the original larger image, upload a lower pixel-count version of the same image re-sized in order to fit the memory limits and screen size of a mobile device.
The first step in this endeavor is to use one of several decoding methods available in Android to read the bitmap’s dimensions and type. With this done, the next step is to use the image’s dimensions to decide if an image is small enough that it can be loaded into the memory without eating up all of it or if you need to create a subsampled version to be loaded instead. Some things to consider here are the amount of memory that the full image might use, the amount of memory you’re willing to commit to this image, given the other images and memory objects you’ll be employing at the same time, the dimensions of the UI you’ll be loading the image into, and the screen size and density of the device you’re developing for. With that information in mind, you can easily decide how to subsample the image in order to reduce its pixel count and make it the size you need for the purposes of memory or look.
Processing bitmaps off the UI thread is important because loading large objects like images can take an unpredictable amount of time depending on the current memory usage, processing speed of the device, and even relative battery levels. If a memory object takes too long to load, the app will become nonresponsive, and the user will be prompted to wait or close to app, and in most cases, users typically select closing the app.
In order to avoid this, you need to process things like bitmaps in the background of the app rather than attached directly to the UI – hence processing “off” the UI thread. There are a couple ways to accomplish this. One popular method is to assign it as an AsyncTask, which is a specific type of sub-process that runs without dependence to other functions on the UI, but can still report back to it. You’ll want to be careful about assigning too many things off of the UI thread, though, because they can quickly eat up processing power.
Since bitmaps are, largely, a memory issue, employing proper memory management when designing with bitmaps in mind is kind of a no-brainer. In general, it’s a good idea to check your version of Android and then compare it against the standard practices of memory management. On Android 2.2 (API level 8) or lower, app threads are dropped when garbage memory is collected and deleted, which can increase lag and degrade overall performance. Android 2.3 and 3.0 both improved their garbage memory collection, although a peculiarity of 2.3 occasionally causes crashes when deleting garbage data because it was stored natively rather than through another application. Take what version you’re using into consideration when designing how an app empties its garbage memory.
Display in UI
As we’ve mentioned earlier, the UI is an important consideration when designing with bitmaps because it, more than anything determines what kind of bitmaps you’ll need to resize or subsample to make them fit into it. Most UI patterns in Android are of the standard grid view swipe gallery type, which is good for creating a standardized and easy to use UI. Tools like ViewPager backed by a PageAdapter are suitable for this kind of thing, although there are plenty of other tools out there to use for it, too.
Finally, the last concern for bitmaps is caching images. Caching is a process that takes images or files and loads them in a group, or cache. Loading a single image is and has always been straightforward, but caching is where you can do things like load an entire UI or gallery at once. Memory caches provide the fastest access to items, although (especially with bitmaps in the mix) they can take a lot of a device’s memory. An alternative to a memory cache is a disk cache, which uses a repository on a hard disk. This is useful because its memory is persistent and using up more of it won’t result in a poor running speed, although in general, this kind of caching is slower to access than memory caching.