Android Best Practices: Part 1
With Android reaching 75% of the global smartphone market, it's no wonder so many people are trying to get in on the action. Google has recently launched a new IDE called Android Studio which helps developers build their apps while adhering to many common best practices. Along with this new tool, Android’s boom in popularity has also produced a wealth of helpful information for new developers. Unfortunately this information is not always complete, nor is it always what is best for your app.
This series of blog posts will attempt to alleviate this by compiling and discussing some of the best practices that are universal to all Android apps. Some topics are so involved that they will have an entire post dedicated to them. This post will touch on some of these topics but will also cover more base level knowledge that can help you get started with your app. For additional information on these topics and more visit the Google Developers Guide.
For the Developer - Comments and Modularization
Commenting
One of the simplest thing a developer can do to any project to immediately make it easier to build and debug is to add good commenting. Comments allow developers to describe the flow of their project inside the code which then assists in their ability to decide how future code will best be integrated. It allows us to describe our thought process when we wrote a piece of code that may not make sense later. In this way we are able to alter the new code to fit with the previous piece, or update the old code to match what we just wrote.
Probably the most helpful aspect of commenting code comes when writing complex functions that can be confusing to ourselves let alone outside eyes. Here we can describe what information we expect going in and what we expect to be coming back out.
/** Explanation of doSomethingFunction / @Param 1 - What information do we expect to come into this function / @Param 2 - What other information do we expect to come into this function / Return - If the function returns information describe it here */ public function doSomethingFunction(param1, param2){ }
All of this combines to make a complex project much more navigable, but it can still be better, which is where modularization comes in.
Modularization
Those large confusing functions that are written all over the place can likely be broken down into smaller reusable functions. This modularization makes it easier to read and maintain your code. Modularization cuts down on repetition thus shrinking the overall size of the project and allows us to write to more precise comments. If you create and manipulate an object over and over, consider making it a model with its own methods for easy access. By using subtask in this way you allow yourself the ability to easily spot potential bugs and memory hogging processes.
For the Device - Hardware and OS
Backwards Compatibility
Before running off to create your app you must consider your audience. Will your app run on only the newest of devices? Or will it be design to run on the largest amount of devices? If you chose the former than your app will potentially start out with a rather limited market, but that will only grow with time. On the other hand, if you choose the latter you will start with a large market that has the ability to continue to use your app as the user base purchase devices with newer operating systems. But this option has a drawback as well.
Making your app backwards compatible takes time, time that could be spent on fixing bugs or adding features users may request after your app's initial launch. By the time you have finished creating a backwards compatible app, the percentage of the market using that old OS will have already shrunk, potentially to a size that is so small it does not outweigh the effort put into compatibility. If the newest OS has only recently come onto the market than backwards compatibility is a great idea. But, if it has been out for a year or so then the percentage of the market that is ready to upgrade will be quite large.
At the time of writing this the newest OS is 4.2.x (Jelly Bean). The most popular OS percentages are Jelly Bean 4.1x/4.2x(33%), Ice Cream Sandwich 4.0x(25.6%), and Gingerbread 2.3x(36.4%). This means that nearly 60% of the Android market is running off of 4.0 or newer. As such the decision to make a backwards compatible app right now would be based on the speed at which it can reach the market. In this situation in would likely not be worth the effort to worry about version 2.3.x compatibility if your app is looking at 6 months or more of production time.
Device Compatibility
Once you have decided what OS you are developing for, it's time to take a look at the devices running on it. Today an ever increasing percentage of the Android market belongs to tablet devices, which means screen sizes vary anywhere from 2 to 10 inches. Targeting these devices not only broadens your potential user base but also opens up several design options. Since tablet devices are several times larger than the average smartphone there is greater real estate for development. Among these option is the use of Fragments.
Fragment are essentially modular activities that have their own life cycle independent from other fragments but still dependent up the main activities life cycle. An example of the use of a Fragment based layout is an app that on a table shows column of items on the left and a detailed view of the selected item on the right. When implemented correctly this same app, when used on a smartphone, would display a single page of consisting of that same column of items which then transitions to the detailed page upon item selection. The use of Fragments in apps for any size device is extremely suggested as it simplifies the flow of your app from one stage to the next.
Along with implementing designs that expand your user base, you must also make sure your app will run on all of these devices. For example if your app is based around sending and receiving phone calls then it would be pointless to install it on a device that only supports a wi-fi connection. A key way of giving access to only users that are capable of using your app is through the use of features. Take for example an app that uses a light sensor. In the androidmanifest you should include the line:
<uses-feature android:name=”android.hardware.sensor.light”/>
When a user does not have a device that supports this feature searches Google Play, your app will not appear to them. This results in less unhappy users that downloaded an app that they can’t use in the first place.
For the User - Speed and Seamlessness
Progress Bars
You can build an app around the greatest idea imaginable, but if it isn't user friendly it will get so few users, it might as well not exist at all. Users can be forgiving to a slightly visually unappealing app, but one of the things users will not put up with is a slow app. Not only are these kinds of app hard to use, if it is slow enough your users may think it has frozen and just shut it down and delete it.
When it comes to apps, users will begin to notice a delay if it is more than 1/10th of a second (100ms). There are two main types of loading indicators, the progress bar and the spinner. If your app is having to load user requested data for a second or more then it will likely need a progress bar to inform the user how much time is left before the process completes. If the process is going to be less than a second but will cause a noticeable delay to the user a spinner will likely be fine. Do this will allow the user to know that something they have requested to happen is in process and that the app has not indeed frozen up on them. Unless the user has purposefully requested something that involves a large amount of data your app should avoid situations where it takes several seconds to perform an action.
View Hierarchy
If your app is slow all the time, another aspect that might be slowing down your app is the way in which you render your UI. Consider an app that shows an image, followed by some text, followed by an image, then more text. It might sound fine to just have it render this way but it’s really not.
When your view is being rendered the app must first render an Image, then a Text field, back to the Image, and then back to the Text field. All this jumping around uses up memory that can slow down your app. Instead always try to render view of the same types of data one after the other and then switch to views of the next type. This will allow the views to load quicker and save on memory than can be used elsewhere. In a future post I will cover more best practices specifically related to design implementation.
Activity Life cycle
The activity life cycle is just that, it is the stage that an app goes through from creation to destruction and everything in between. This is a very important part of being able to manage your app to the best of its abilities and will thus be covered more in-depth in a future installment of this series.
At this point in time the main parts of the life cycle you should concern yourself with are:
onCreate(); - This method is called when the activity is first created. You should override this method to set up your apps main content view and perform any other initial setup, such as creating views, binding data to lists, etc.
onStart(); - This method is called when the activity is about to become visible to the user. You should override this method if your app needs to perform any specific tasks right before an activity becomes visible.
onPause(); - This method is called when the system is about to put the activity into the background. You should override this method if your app needs to commit unsaved changes to persistent data or cleanup other objects that are consuming resources.
onResume(); - This method is called when the activity starts interacting with the user again after being paused. When this method is called the activity moves to the top of the activity stack.
onStop(); - This method is called when the activity is no longer visible to the user, because another activity has been resumed or started and is covering this one. You should override this method if your app needs to perform any specific tasks before it is destroyed or rebuilds the UI after an orientation change.
onDestroy(); -This is the final method that is called on an activity before it’s destroyed. This is the last opportunity to save state data as the OS will permanently destroy the state data of an activity after this method runs.
Asynch Tasks
The last thing that will be covered in this post is possibly the most important. When your app runs, it is trying to manage and manipulate data along with keeping the UI up to date with any changes. Trying to run all of this at the same time will cause both to suffer and the whole app will slow to a crawl and/or freeze until both processes are done. This is also a topic that deserves its own in depth post, but for now I will just cover the basics.
The main thread that your app runs in is often called the UI thread because that is where all modifications to the user interface happen. When you need to run a data gathering, storing, or manipulation processes it it always best to do so in a separate thread. Asynch Tasks let you run these background threads simultaneously to the UI thread, thus allowing for near seamless functionality.
A great example of this kind of use is an RSS reader. As the background thread accesses and parses out the data from a feed it can send that information back to the UI thread allowing the app to populate new incoming data with little perceived delay to the user. If the user chooses to refresh an entire feed then the background task can be used to update a progress bar or loading spinner informing the user that the action is taking place and that the app has not frozen.
In our next post we will cover some of the best practices associated with the UI. In it, I will further discuss ways to layout your app to minimize latency, as well as make it more user friendly. I will also cover some things you can do to make the design process cleaner from a development standpoint. If you have any questions or would like to request a post covering other Android related topics just let us know. I've got the knowledge if you've got the questions!