Four Ways To Build A Mobile Application, Part 2: Native Android
This article is the second in a series of four articles covering four ways to develop mobile applications. The last article covered how to accomplish this using native iOS development tools. In this article, we’ll look at how to build the same sort of application using native Android tools.
We’ve been building a simple tip calculator. As with the iOS application, this one contains two screens: a main view and a settings view. The settings view persists the default tip percentage to local storage using Android’s SDK support. (The source code for each app is available on GitHub.)
Here’s the UI we’re following for this project:
Native Android Development: The Tools
For the Android platform, the Java language is used, along with the Eclipse integrated development environment (IDE). Google provides the Android Developer Tools (ADT) plugin for the standard Eclipse IDE to support things like graphical layout definition and debugging. As of the time of writing, Google has also released a new IDE, called Android Studio, in early-access preview. This product is based on JetBrains’ IntelliJ IDE and will eventually replace Eclipse as the official development tool for Android applications. Because Android Studio is still in prerelease form, we’ll use Eclipse to develop this application. Eclipse is very stable, and many Android training resources using Eclipse abound.
The Android development environment is supported on Windows, Mac and Linux. There is no charge for the development tools, and applications may be freely deployed to devices. To submit apps to the Google Play marketplace, developers must sign up for publishing and pay a one-time fee of $25. Submitting apps to Google Play is notably less involved than for iOS. When your application is submitted to Google, it is scanned for malware and common threats. Generally, the application becomes available to the general public within a few hours.
An overview of the toolset can be found on the Android Developers website. Successful Android development entails use of the following tools:
- Eclipse IDE
- Android SDK
- Android ADT
- Android system images for the Android emulator
- an Android device (technically not required but highly recommended)
Grab An Installation Bundle To Ease Your Pain
In the past, you had to download, install and configure the tools individually. This took a fair amount of time and often led to confusion for new Android developers. To make the process easier, Google now offers “bundles,” which simplify the installation process. Bundles are offered for each operating system and are available on the page where you download the developer SDK.
Installing Eclipse, the SDK, the emulator and so on is as easy as unpacking a ZIP file into a directory. If you have an existing installation of Eclipse and would prefer to use that instead, there are instructions for adding Android support to it.
Loading An Existing Application Into Eclipse
Once the Android development tools have been installed, you may wish to import an existing project, such as the source code for our sample app. In Eclipse, go to File → Import
in the menu, which will display the following dialog:
Once you’ve chosen the option to import an existing Android project, click the “Next” button, whereupon you will be able to specify the directory where the code that you wish to work with in Eclipse is located.
Once you’ve selected a directory via the “Browse” button, Eclipse will automatically find any Android project in that directory and show it in the list of projects to import. Simply click the “Finish” button, and the project will appear in your list of projects along the left side of the IDE.
Steps To Building An Application
There are several steps to building an Android application. We’ll cover each in detail later in this article. The steps include the following:
- Define the user interface. The UI for an application is generally defined as a series of layout files. These are XML-based files that describe the controls on a screen and the relationship of their layouts relative to one another.
- Add image assets, language translations and other resources. Android refers to non-code assets of a project as resources. These are placed in the project in a directory structure defined by the Android SDK. At runtime, Android dynamically loads content from this directory structure. We’ll see later on how different assets and layouts can be loaded to support the wide variety of Android device configurations available in the market today.
- Write Java code to respond to various events that occur from the controls on a given screen and from changes in the lifecycle of an application. Java code is also responsible for loading the layout and menu files associated with each screen. And it’s used to control the flow from one screen to the next.
- Export the completed Android application as a file that can be uploaded to Google Play or shared with others directly.
The Eclipse ADT For Android Development
The Eclipse IDE provides the standard source-code editing tools, along with a source-level debugger that allows applications to be debugged on both the simulator and a physical device. In contrast to the storyboards used with iOS, a layout editor is used to define screen layouts:
The layout editor works on a single screen at a time and is not able to define segues between screens, as in the iOS storyboard editor. However, the Android layout editor does have a very flexible layout system that supports the wide range of screen sizes and resolutions of Android devices.
Unlike the storyboard editor in iOS, you can edit the layouts in either visual mode, as shown above, or an XML-based editor. Simply use the tabs at the bottom of the layout editor to switch between the two views. As the layouts become more complex, being able to edit the layout files directly comes in handy. A snippet of layout XML looks like this:
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<EditText
android:id="@+id/billAmtEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:ems="10"
android:gravity="right|center_vertical"
android:hint="@string/billAmount"
android:inputType="number|numberSigned|numberDecimal" >
<requestFocus />
</EditText>
<Button
android:id="@+id/calcTipButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/billAmtEditText"
android:layout_centerHorizontal="true"
android:layout_marginTop="19dp"
android:text="@string/calculateTip" />
<TextView
android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/billAmtEditText"
android:layout_below="@id/calcTipButton"
android:layout_marginTop="18dp"
android:text="@string/tipPercentage"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</RelativeLayout>
Android Resources: Support For Various Device Capabilities
The Android SDK was designed from the outset to support a wide variety of device capabilities. Much has been written about the “fragmentation” of devices on the Android platform. However, from the beginning, the Android SDK was designed to support this variety of device attributes, including screen size, pixel density and Android API versions.
Screen layouts, image assets (called drawables), styles and other configuration files are all incorporated in a series of subdirectories underneath a master “resource” directory. The Android SDK documentation illustrates how multiple versions of the same file can be placed in uniquely named directories within this resource structure, so that the proper one is loaded depending on the capabilities and orientation of the device at runtime.
Consider this directory structure:
The structure above shows uniquely named directories for drawable assets, with suffixes of -hdpi
, -ldpi
, -mdpi
and so on. This permits the developer to supply different image content to correspond with the DPI resolution of a given screen. Similar capabilities extend to things like the layout files, which may supply unique content according to screen size, landscape or portrait orientation, etc. In the example above, we see unique folders for values-v11
and values-v14
. This allows for two different styles.xml
files to be used for version 11 (Android 3.x) and version 14 (Android 4.x) of the Android operating system.
Note, also, the regular values
directory. Android versions prior to version 11 would obtain their styles.xml
from this directory. This fallback mechanism is in place for all resources. The SDK will attempt to find the resource in the particular directory and then fall back to a more “generic” resource if it’s not available. All of this occurs without the developer having to write any special code. Simply drop the resources into the proper directories, and the Android runtime will take care of the rest. Each of your resources may be accessed from both the XML and Java files in your application.
The resource system is quite powerful and supports many types of resources. In addition to the drawable, layout, menu and styles for your application, it can also be used to hold application constants for arrays or dimensional values in your application. In this manner, you can load different constant values for various device configurations simply by placing the files in the proper directory structure. You can also use this system to support localization. This is accomplished through the strings.xml
file. Here’s an example of the strings.xml
file associated with our application:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">FasTip</string>
<string name="hello_world">Hello World!</string>
<string name="menu_settings">Settings</string>
<string name="billAmount">Bill Amount</string>
<string name="calculateTip">Calculate Tip</string>
<string name="tipAmount">Tip Amount</string>
<string name="totalAmount">Total Amount</string>
<string name="title_activity_settings">Settings</string>
<string name="saveSettings">Save Settings</string>
<string name="tipPercentage">Tip Percentage</string>
<string name="percentSymbol">%</string>
</resources>
The base string file is placed in the res/values
directory of the application. If you wanted to offer a Spanish version of the same file, you would place it in the res/values-es
directory.
Resource IDs
In the process of defining the layouts, you should associate an ID value with each control (or UI widget) that you wish to reference from code. This can be done by specifying an ID value in the properties inspector in the layout editor:
Layout Managers
Android layout files may use a number of layout managers, or ViewGroups, to arrange the controls on the screen. This is Android’s approach to laying out views, much like the constraints system we saw in iOS. Here are some of the more common ViewGroups used in Android apps:
- LinearLayout This is used to lay out a series of controls in either horizontal or vertical orientation.
- RelativeLayout This is used to position controls relative to one another or to the bounds of their parents’ layout. This is a flexible layout system and is often used as an alternative to nesting linear layouts.
- ListView This is a view group that presents a series of vertically scrolling items, much like a
UITableView
for those familiar with iOS. In Android, you write a ListAdapter to provide a view for each row of data in a data source. - GridView This is similar to a list view, but it provides items in a two-dimensional, scrollable grid. Just like the list view, it also uses an adapter to provide view contents for each cell in the grid.
In our sample application, two layout files have been created: activity_main.xml
for the main screen and activity_settings.xml
for the settings screen. So far, we’ve just defined the appearance of things. Unlike the iOS storyboard editor, Android has no “assistant” tool to directly link the controls in the visual layout editor to the code. We’ll need to write some code to connect these components together and build the application.
Android: Activities And Intents
In iOS, we loaded the logic specific to a given screen into a ViewController
. In Android, these separate screens are treated as separate “activities.” Just like in an iOS UIViewController
, there is a defined life cycle for an activity that governs when the activity starts, pauses, resumes and stops.
The diagram above comes straight from the Android Developers documentation and shows the lifecycle of an activity. It’s up to the developer to place code in the various methods of the activity to respond to the various states of the lifecycle.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tipPctTextView = (TextView)this.findViewById(R.id.tipPctTextView);
tipAmountTextView = (TextView)this.findViewById(R.id.tipAmtTextView);
totalAmountTextView = (TextView)this.findViewById(R.id.totalAmtTextView);
calcTipAmountButton = (Button)this.findViewById(R.id.calcTipButton);
billAmountTextView = (EditText)this.findViewById(R.id.billAmtEditText);
calcTipAmountButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
calculateTip();
}
});
}
The code above is executed early on in the activity’s lifecycle. In this case, we are loading the layout file that was defined earlier, via this statement:
setContentView(R.layout.activity_main);
Upon successfully loading the layout, we obtain references to the various controls that make up the layout. Notice that we are referencing the layouts and the IDs via the R.
notation. These are the resource IDs that we defined earlier in the layout manager. Additionally, we attach a click listener to define what code should run when the user taps on the “Calculate Tip” button.
In the iOS example, we used NSUserDefaults
to save and restore the user’s preference for a tip percentage. In Android, the equivalent is sharedPreferences. This can be seen in the code snippet below, where we restore the persisted value for tipPercentage
in the local variable tipPctString
. Notice that the first time the user runs this app, a default value of 15.0
is used.
private void loadTipPercentage() {
SharedPreferences preferences =
this.getSharedPreferences("AppPreferences", MODE_PRIVATE);
String tipPctString = preferences.getString("tipPercentage", "15.0");
tipPctTextView.setText(String.format("%s%%", tipPctString));
tipPercentage = Double.parseDouble(tipPctString) / 100;
}
Defining A Menu For Our Activity
Android applications do not use the NavigationController
approach of iOS applications. In Android 4.x devices, the choices that appear in the upper-right of the screen are part of the Android “action bar.”
The action bar is the dark header in this screenshot. Note the settings button in the upper-right, which is defined by the menu.
We populate the action bar by defining a series of menu options in an XML file, just as we did with the screen layout. The menu is placed in a menu directory within the resources directory. Here are the contents of the menu file used on the main screen of our application:
<menu xmlns:android="https://schemas.android.com/apk/res/android" >
<item
android:id="@+id/menu_settings"
android:orderInCategory="100"
android:showAsAction="ifRoom"
android:title="@string/menu_settings"
android:icon="@android:drawable/ic_menu_preferences"/>
</menu>
Note that, in this case, we have just one menu option to access the settings screen. If more options were placed in the menu than could fit on the screen, then the remaining items would flow into a drop-down menu, accessible via the three vertical dots often seen in Android menus. To use the menu in our activity, we must load it at the correct time. This is done in the onCreateOptionsMenu
method:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
// Respond to menu selections
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.menu_settings:
this.startSettings();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Notice how the onOptionsItemSelected
method is used to determine what code is run when a given menu option is chosen.
Starting Another Activity
As we’ve seen from the code above, when the user chooses the settings icon from the action bar, the startSettings()
method is invoked. This is the code that launches the second activity to display the settings screen:
private void startSettings() {
Intent settingsIntent = new Intent();
settingsIntent.setClass(this, SettingsActivity.class);
startActivity(settingsIntent);
}
Android launches activities via an intent object. In this simple example, we are creating a very specific intent to launch the SettingsActivity
class, which contains the code to load and drive the settings screen. Most Android applications use this technique to move the user from one screen to the next in an application.
Using Intents For App Integration
Intents are considerably more flexible than this. It’s possible to use intents to launch a variety of activities. A common intent is to send an item to be shared via social networking apps. If you’ve used an Android device before, then you’ll know that doing something like this typically brings up a menu of applications that support a sharing intent. This list often contains applications such as Facebook and Twitter.
This particular sharing intent is known as ACTION_SEND. All of the applications that support sharing are listening for a common intent. When an Android application is installed on a device, its manifest file describes the intents it can process. In this way, several applications may be installed, each of which supports the same intent.
If only one application supports the intent, then that activity is immediately launched. Whereas if multiple applications are installed for the same intent, then Android would show a menu of options, from which the user would make a selection before the intent is sent to the chosen activity.
The intent system in Android is a unique feature that can be used to create workflows across several applications. When building for the Android platform, you should keep this capability in mind because it offers a way to integrate your application with others.
The Settings Screen
Much of what occurs here has already been covered in the section on the main activity. This activity has similar code in the onCreate
method to load the appropriate layout file and obtain references to the controls. When the “Save Settings” button is pressed, the following code is run to save the tip percentage value to storage:
private void saveSettings() {
if (validateSettings()) {
SharedPreferences preferences =
this.getSharedPreferences("AppPreferences", MODE_PRIVATE);
SharedPreferences.Editor prefEditor = preferences.edit();
prefEditor.putString("tipPercentage",
tipPercentageEditText.getText().toString());
prefEditor.commit();
this.finish();
}
}
Notice that the last line in the saveSettings
method calls the finish()
method. This causes the current activity to stop running and returns the user to the prior activity, which in our case is the main screen. Contrast this with iOS’ use of the NavigationController
and the way we popped the current view controller off the stack of view controllers.
Running Your Application
Android provides two basic ways to test an application: in the Android emulator or on a regular device such as a phone or tablet. Unlike with iOS, you don’t have to pay any fees to deploy your app to your own device or to share it with other Android users. The only fee that is required is if you wish to feature your app in Google Play.
The emulator enables you to emulate a variety of Android versions, as well as different screen aspect ratios and resolutions. While testing your application on a real device is always a good idea, the emulator helps you to test on device configurations that are not readily available.
To use the emulator, you must create an Android Virtual Device (AVD). Think of this as a virtual phone or tablet. Creating multiple AVDs, each with a unique configuration, is possible to test your app on different screen sizes and memory capacities. Android Developers provides details on how to use the AVD manager to set up these devices.
Use Hardware Virtualization To Speed Up That Slow Emulator
The Android emulator has long been maligned for its poor performance on many machines. This is because, in the past, the emulator has been running actual code compiled for the ARM processor on a phone, rather than for the x86 CPU that’s likely on your development machine.
Recently, an x86 hardware-virtualized version has been made available. This version starts up and runs considerably faster than the traditional emulator. The hardware-virtualized version will generally run on a Windows or Mac OS X machine with an Intel Core i3 or later processor that supports hardware virtualization. AMD processors are currently supported only on Linux.
Creating an AVD that uses hardware virtualization requires that you set up Intel’s HAXM support first. The developer documentation provides details on the process to follow for your operating system. Make sure that you are running the latest HAXM support; if you’re on Mac OS X Mavericks, a recently released hotfix resolves issues there as well.
Testing On A Device
To test the application on your own device, you’ll need to enable USB debugging on the device. Google provides detailed instructions on setting up your device for debugging.
For Android 4.0 devices, go to Settings → Developer Options
in the menu and you’ll see a checkbox to enable this. If you’re on Windows, you might need to install some USB drivers for your device; these can usually be downloaded from the manufacturer’s website. For Mac OS X, you usually will not need any drivers and can just plug in the device via a USB cable.
The Eclipse IDE provides a “targets” option that enables you to specify which device, or AVD, should be used to run your application. This can be found in Run → Run Configurations
and Run → Debug Configurations
in the menu. The “Targets” tab allows you to select which AVD should be used by default or to set a prompt each time to select a run or debug destination for the application:
If you select “Always prompt to pick device,” then the IDE will present this dialog each time you choose to run or debug the application:
Every time you run or debug the application, an APK file is built and written to the /bin
directory of your project’s root directory. This is the application distribution package used for Android devices. It’s analogous to the IPA file for iOS applications. When you select a deployment target via the process above, the Android development tools will automatically install the APK on the target device or emulator and run it.
Sharing Your Application With Others
Submitting an application to Google Play is beyond the scope of this article. The documentation provides an overview of the process. You can also use the export wizard in Eclipse to export an APK file. The resulting APK file can then be shared with others outside of Google Play via open distribution, and it is very useful when sharing copies of your application for testing prior to general release.
Android: Learning Resources
As with iOS, some excellent resources exist for getting started with Android. I recommend the following books and websites:
- The Busy Coder’s Guide to Android Development, Mark L. Murphy This book is comprehensive, and the writing style is easy to follow. The author even offers a free version of the book based on an earlier version (Android 3.x) of the SDK.
- Beginning Android 4 Application Development, Wei-Meng Lee For those looking for a shorter book, this does a good job of covering the basics, without as much detail or scope as Murphy’s book above.
- Vogella This series of brief tutorials shows how to perform common operations in Android applications.
- Android Weekly This weekly newsletter will help you stay current on new blog posts and libraries of interest to the Android development community.
Summary
That wraps up our coverage of developing a native app for Android. We’ve seen some unique capabilities of the platform, such as the resource system, layouts, and the way that activities and intents work together. In the next article, we’ll take the tip calculator cross-platform, looking at what’s involved in developing our app for both iOS and Android using PhoneGap.
Further Reading
- What Every App Developer Should Know About Android
- How To Design For Android
- Designing For A Maturing Android
- An Extensive Guide To Progressive Web Applications