3 Basic Rules Every Android Developer Should Know
This tutorial is originally published by the mentor here.
Android has been popular for a while, so a lot of developers are attracted towards Android app development. If you know the programming language, then you can create a working app. But making an app that just works does not make you a good developer. Apps that just works usually causes ANRs. That is not a very good scenario for users and they end up giving you one star ratings and a bad review. Until eventually, they will search for better alternative in the Play store if your app keeps getting them ANR.
So we have to focus on developing apps with better performance. Apps depend on following things to perform better:
- Screen responsive-ness
- How handy it is
- Supports stone-age phones (too).
- Android look and feel
So, if those are the things we have to focus on while developing an Android app, then what then are the rules?
1. Always use background threads for processes that take time
It's annoying for regular mobile users when the dialog above appears on the screen while using the app. It simply means that the user's action has a delayed feedback, and your app will stop responding. This leads to frustration, and a frustrated user can end up giving bad reviews.
So what's the problem? The main problem is that you are blocking the main thread with a time consuming process. Some examples of such processes are:
- Loading images from server
- Reading or writing data to your sdcard
- Database querying
- Network calling
- Bitmap loading
Remember to push these processes in the background thread. You can use AsyncTask for such activities.
2. Display bitmaps efficiently
“A picture is worth a thousand word,” as the saying goes. But in terms of engineering, there's also the saying, “Even a small picture is worth a thousand kilobytes”. Well there is not such saying, actually I made that up. But it's actually kind of true. Let's break it down. We use bitmap images to make our app's content look nice, and as per the saying above, it does speak a thousand words. Also, they are memory eaters.
And that’s pretty much the situation. We even have some proof for that. Let's take a 1000×1000 size image. Its pretty small right? We have to load this image into the memory before displaying it. The total memory needed to display an image is
memoryNeeded = 4 imgHeight imgWidth bytes
So the total memory needed to display the image above is 410001000. Which is 4 million bytes or nearly 4MB. So we have to allocate 4MB just for one small image. So let's take a simple xolo a500s. It's screen resolution is 480x800. If we try to show the same image in this mobile, then we shouldn't allocate more than 4x480x800 = 1.5MB of memory. So how do we do it? There are a couple of ways to do it. Let's take a look into these ways.
Load bitmap images in background thread
Bitmapfactory.decode: This method should not be executed on the main UI thread if the source data is read from disk or a network location (or really any source other than the memory). The time this data takes to load is unpredictable and depends on a variety of factors (speed of reading from disk or network, size of image, power of CPU, etc.). If one of these tasks blocks the UI thread, the system flags your application as non-responsive and the user has the option of closing it.
Load a scaled-down version into the memory
Sometimes we don’t need all the resolution the image is offering us. So we can just scale them down. Scaling an image down will help to cut down a lot of memory. If we scaled down a 1000x1000 image to 500x500, we can reduce the memory to be allocated from 4MB to just 1MB. That’s hell lot of relief for the memory.
Caching bitmap while using them on Listview, GridView, or Viewpager
In components like ListView, GridView, and Viewpager you have to load a larger set of images into the memory and most of them are not even displayed in the screen at a time. Memory usage is kept down with components like this by recycling the child views as they move off-screen. The garbage collector also frees up your loaded bitmaps, assuming you don’t keep any long-lived references. This is all good and well, but in order to keep a fluid and fast-loading UI, you want to avoid continually processing these images each time they come back on screen. A memory and disk cache can often help her; allowing components to quickly reload processed images.
3. Consider developing for all devices
There are millions of Android devices out on the market. They differ on the screen size, resolution, memory, and storage capacity. If you focus on building your app for a single mobile then the chances are high that it won't look as nice on other devices. Here are some tips to consider when handling different screen sizes in Android app development.
Your application achieves “density independence” when it preserves the physical size (from the user’s point of view) of user interface elements when displayed on screens with different densities. Maintaining density independence is important because, without it, a UI element (such as a button) appears physically larger on a low-density screen and smaller on a high-density screen. Such density-related size changes can cause problems in your application layout and usability. Lets see what happens to an application when it does not provide density independence.
Also let's see what happens to the application when it provides density independence.
See the many differences? The Android system helps your application achieve density independence in two ways:
- The system scales dp units as appropriate for the current screen density; and
- The system scales drawable resources to the appropriate size, based on the current screen density, if necessary.
In figure 2, the text view and bitmap drawable have dimensions specified in pixels (px units), so the views are physically larger on a low-density screen and smaller on a high-density screen. Because although the actual screen sizes may be the same, the high-density screen has more pixels per inch (the same amount of pixels fit in a smaller area). In figure 3, the layout dimensions are specified in density-independent pixels (dpunits). Because the baseline for density-independent pixels is a medium-density screen, the device with a medium-density screen looks the same as it does in figure 2. For the low-density and high-density screens, however, the system scales the density-independent pixel values down and up, respectively, to fit the screen as it deems appropriate.
In most cases, you can ensure density independence in your application simply by specifying all layout dimension values in density-independent pixels (dp units) or with
wrap_content, as appropriate. The system then scales bitmap drawables as appropriate in order to display at the appropriate size, based on the appropriate scaling factor for the current screen’s density.
However, bitmap scaling can result in blurry or pixelated bitmaps, which you might notice in the above screenshots. To avoid these artifacts, you should provide alternative bitmap resources for different densities. For example, you should provide higher-resolution bitmaps for high-density screens and the system will use those instead of re-sizing the bitmap designed for medium-density screens.
Support Multiple Screens
There are a couple of ways to do this, and these ways are:
Provide different layouts for different screen sizes
By default, Android resizes your application layout to fit the current device screen. In most cases, this works fine. In other cases, your UI might not look as good and might need adjustments for different screen sizes. For example, on a larger screen, you might want to adjust the position and size of some elements to take advantage of the additional screen space, or on a smaller screen, you might need to adjust sizes so that everything can fit on the screen.
The configuration qualifiers you can use to provide size-specific resources are small, normal, large, and xlarge. For example, layouts for an extra-large screen should go in
Beginning with Android 3.2 (API level 13), the above size groups are deprecated and you should instead use the
sw<N>dp configuration qualifier to define the smallest available width required by your layout resources. For example, if your multi-pane tablet layout requires at least 600dp of screen width, you should place it in
Provide different bitmap drawables for different screen densities
By default, Android scales your bitmap drawables (.png, .jpg, and .gif files) and Nine-Patch drawables (.9.png files) so that they render at the appropriate physical size on each device. For example, if your application provides bitmap drawables only for the baseline, medium screen-density (mdpi), the system will scale them up on a high-density screen, and scales them down when on a low-density screen. This scaling can cause artifacts in the bitmaps. To ensure your bitmaps look their best, you should include alternative versions at different resolutions for different screen densities.
Use 9-patch drawable
The main reason to use 9-patch drawable is for scaling purposes. They can scale up or down smoothly without distorting the pixels. It automatically resizes to accommodate the contents of the view and the size of the screen.
Following these rules might take some extra time but it is totally worth it. If the apps performance is better then the user might stick to your application for a longer time.