In the previous master classes, we covered installing the development environment, organization of an Android project, UI construction and simple interactions. In this class, we look at how to create an application that has more than one functional screen.
All non-trivial Android applications are made up of a number of different functional screens and hence multiple activities. Although multiple screens allow us to build complex applications, they also require careful management. In particular, we now need to deal with activities that are no longer visible since Android OS will place them into the background and may terminate activities that are not used for a long period of time. The use of multiple activities also requires us to think about the interaction and navigation model that the user will experience.
The Android framework provides a simple, yet highly flexible approach to work with multiple activities and offers us a powerful model for passing information between activities. The most interesting choice is the option of pass messages with data to any activity both within and outside of the application. For example, we can pass information between two activities within an application as well as pass on messages to an activity outside of the application boundary. Of course, the designers of Android have built a robust security model to ensure that this flexibility and power does not compromise data safety.
In this master class we will start with a short recap of the core concepts covered so far, and then move on to look at the lifecycle of an Android activity after which we will cover how Android applications are started by the runtime. Once these aspects are covered, we will explore how to build an application with multiple activities by walking through an application.
A Short Recap
Before getting into the details of handling multiple activities, here is a recap of the core Android concepts covered in the previous master classes. An Android application is made up of Activities, where an activity maps to an individual window/screen of functionality in an application. We place UI components (called Views) inside a container called a layout (also known as View Group) and attach this layout container to an activity. The UI is described in an XML file and paired with a Java class that provides the interactivity. The Android framework mandates conventions, which specify a set of folders that every Android project must have (see Table 1 below – Description of Android project folders). The conventions specify location of resource such as images and data for the development tools to process them and generate references to use these resources from within the XML UI description files and Java code.
As elaborated in the previous master class, the framework allows us to define the placement of UI components either absolutely or relative to other UI components. However, in most cases developers use the relative placement option as it permits the runtime to automatically layout the UI to work on different screen sizes and orientation. Developers also have the option to provide specific layout definitions in XML for different screen sizes as well as for portrait and landscape orientations. This ability allows developers to deal with the vast range of screen sizes that Android devices come in with minimal pain.
|src||Source Code – this is where the Java source code is placed. The Java files are organized into packages that directly map to the folder structure (i.e. the package apc.examples implies a folder structure \apc\examples)|
|gen||Android tools generate code to refer to resources from the Java and XML code. This generated code is placed in this folder.|
|Android 2.2||The Android framework library is stored here (generated when we create new project)|
|assets||Project specific assets are placed here. Developers often store application data files that do not need to change here.|
|res||This is the location to store resources used in the application. Specifically, images, layouts and strings.|
Table 1 – Description of Android project folders
Every Activity has a Life Cycle
Android OS supports full multi-tasking –with multiple active applications and background services. As indicated earlier, an application is made up of one or more activities. Intriguingly, designers of the Android OS made an interesting choice — they directly manage the life cycle of each individual activity, rather than the parent application.This is one of the key internal distinctions between iOS and Android. iOS manages the application life cycle rather than that of the internal screens. There are benefits in both approaches, but longer-term, there is more flexibility when life cycle is managed at the level of an activity in that the framework/architecture evolve more elegantly.
Figure 1 – Activities have a life cycle and are directly managed by the operating system
Every activity is allocated an OS level lightweight process each with a specific life cycle that determines how it behaves. In the simplest scenario, every activity starts life when created, it will then run for certain time, and it dies when we exit the application. Realistically, we tend to have a few active applications and to ensure a smooth/responsive experience the designers of Android have engineered a life cycle model that will keep track of the states of each activity (see Figure 2). This is better illustrated with an example — lets assume you receive a phone call while checking the weather. The weather application is moved into the background (paused, then stopped), and the application that handles phone calls is brought into the foreground (it is running). When the phone call ends, then the phone call application is stopped, and the weather application is back running (i.e.we can see it).
Figure – The life cycle of an Android activity (simplified)
The switching of states, allows the phone to keep a number of different applications active. However, only one activity (and hence one application) is in the running state, while the rest are stopped. So, how does the music player work if only one activity can be running at a time? This is possible because the Android framework allows us to create services (not activities) that can keep running in the background. In most cases, if you cannot see an activity, assume that it is stopped. If all activities in an application are stopped long enough (say many minutes), the OS may decide to kill the application for good and free resources.
Given an application is made up of activities, how are these activities started? The Android runtime allows us to start another activity. We can start an activity within an application as well as external to an application. When an activity starts another one, the caller will be placed in the background and the receiver goes into foreground (see Figure 3). The activity-chaining model ensures that only one activity remains in the running state.
The Android OS, like all mobile operating systems holds the dictatorial power and decides the life/death of all activities and hence, indirectly, the life of an application. It decides which activity gets to run, and for how long. This level of overarching power is needed to stop a running activity when the user receive a phone call, or when the battery is running low as the OS may need to clean up before shutting down.
Figure 3 – Only one activity can run in Android, the rest are paused
The Manifest File
In this master class series, so far, we built applications with only one activity. When we create an application with just one activity, we do not need to worry about telling the runtime what the entry point for our application is or modify this behavior. However, when we have an application with multiple activities, we need to let the runtime know what the “main” or entry-point activity is. In the Android framework, the manifest file provides the information about all of the activities that make up an application as well as define the activity that will serve as the entry point.
The manifest is an XML file that allows developers to describe an application including the permissions needed. Typically, we provide the following information in a manifest file.
- The name and display label of the application.
- The activities that make up an application.
- Launcher icon (via a reference to a drawable resource).
- Required permission (e.g. Internet access, GPS sensor etc.).
- The minimum Android version requirements on the device.
The manifest file is called “AndroidManifest.xml” and placed in the root directory of an Android project. An example of a manifest file is shown in the code snippet below.
<uses-sdk android:minSdkVersion=“8″ />
<uses-permission android:name=“android.permission.INTERNET” />
<activity android:name=“.MeDroid1Activity” android:screenOrientation=“portrait”
<action android:name=“android.intent.action.MAIN” />
<category android:name=“android.intent.category.LAUNCHER” />
<activity android:name=“.AboutActivity” android:label=“About Me” />
The manifest file also allows us the ability to lock orientation for any activity in the application. In the code snippet above, we have two activities (MeDroid1Activity and AboutActivity.
We tag the intent of the MeDroid1Activity with an action and category to indicate that this is the “main”activity and that this activities icon should show up on the launcher. We will explore the concept of intents in Android shortly, but the filter as defined in the XML allows us to nominate one activity to be the main entry point.
In the manifest XML file, we used a couple of string resources, for example:android:label=”@string/app_name”.These resources are very similar to the graphical resources that we access via an identifier (@drawable/icon). However, the string resources are defined in an XML file (res/values/strings.xml). This file contains the resources in a simple format – for example the application name is defined as: <stringname=“app_name”>MeDroid</string>
Android development guidelines recommend externalizing all strings and placing them in an external XML file. The real advantage of storing string values in the external file is evident when we need to internationalize the application. For example, if we need to support French, we only need to create a new strings.xml with French information and place that in the folder res/values-fr/ the framework will take care of the rest (further information on localization for other regions is discussed at http://bit.ly/qEgAvt). The Android framework also allows us to define an array of strings, as well as handle plurals if needed (seehttp://bit.ly/ncvdsB).
Application with Multiple Activities – A Checklist
The concepts that we covered so far (Activity life cycle, Manifest, and String resources) lay the foundation for building applications with multiple activities. Before we get into the code and explore an actual application with multiple activities, here is the list of the things we need to do:
- Create a new Java class for each new activity in the application.
- Create a new layout XML for each activity to define the UI.
- Create an entry in the Manifest file specifying the details of all activities in the application. We also need to tag the entry-point activity.
- Connect the activities up via the use of intents in Java code (more on how to do this shortly).
- Add permissions that we need for the application into the manifest(e.g. Internet use).
- Use the external string resource file for text values in the application
In the rest of this masterclass we will walk through the key elements of an application with multiple activities, in particular we will go through the above steps with the help of a working example. The application that we will walk through is the initial version of the “meDroid” application (see Figure 4). This application will allow you to publish a simple application about a company, an event, or an application about a person. For example, the code base can be modified/extended to build an app.for a local restaurant, a community event that you are organizing, or it can be an application that offers a creative profile of you.
The initial version that we will explore is built around Linus Torvalds – the creator of the Linux operating system upon which Android is built. The application has a main screen with icons/text that starts new activities (see Figure 4). In this initial version, we even create a hook into the Twitter feed using the built-in component (WebView) that will render a web page (see Figure 4). If you change the image resources (inres/drawable-hdpi) and provide a different twitter user name (in res/values/string.xml) you can make it work to your own needs without any changes to the Java code.
Figure 4 – The initial baseline version of the meDroid application
Follow these steps to get the example code:
- Download medroid1.zip – this file contains the Eclipse project we use in this master class.
- Open Eclipse IDE, then File -> Import…
- General > Existing Projects into Workspace.
- Select the archive file radio button option
- Browse and select the medroid1.zip file that you downloaded. This step will open the project.
- Run -> Run (or Ctrl-F11) to launch the application and check it is working properly.
When you run the application, if you click on the various icons they will launch new activities (it should be similar to Figure 4). To go back to the main screen, you will have to click the back button (curved arrow on the emulator button panel). Be careful, if you press the back button few too many times it will exit the application.
We will refer to various parts of the code from now on, so it is worth downloading the example and have it running.
Activities and associated Layout
In this application, there are four different activities (the Main start screen, plus three for the various functions). All of these classes extend the Activity class from the Android framework, and have their own layout XML file (see the Java code files in the src directory). Except for the main activity, the rest use linear layouts as covered in the last masterclass. The main activity (MeDroid1Activity) requires a more complex layout and it makes use of a Table layout combined with a nested linear layout. Each of the icon and text (e.g. Twitter Icon + Text) is first placed in a linear layout and then these are then put into a table. The table layout is set to stretch all of the columns to equal width via the use of the attributeandroid:stretchColumns=”*” (see main.xml in the layout folder for details of how the layout has been defined). If you are modifying the icons, be careful to ensure that all the icons have an identical height to ensure that the layout looks elegant. Especially since the table layout does not automatically adjust the sizing to make all icons the same height.
The activities, associated layout and the resources (image/string) used by these activities are presented in the table below.
Table 2 – - Activities, Layouts and Resources in meDroid
|Activity(src)||Layout(res/layout)||Drawable Resources(res/drawable-hdpi)||String Resources(from values/strings.xml)|
|MeDroid1Activity||main.xml||location.png, twitter.png, about.png, medroid.png|
Android framework uses asynchronous messages (similar to email) called Intents to communicate between activities, including for starting another activity. An activity can send intents to another activity directly (by naming it), or it can broadcast intents to all applications on the device. The broadcast option is very handy in some instances – consider for example an activity needs to play an audio file. In this case, you can broadcast a “play audio file” intent message to all applications and let the operating system find the most suitable player with the correct decoder. If multiple applications can service an intent, then the user is presented with the option to choose. Just like email, intents can be sent with an attachment that contains a block of data. Since every activity is treated as a separate process by the Android operating system, intents are the ideal and recommended inter-process communication (IPC) mechanism. So, this method is used widely in the Android eco-system for applications to communicate between activities (internal as well as external to an application).
The intent mechanism requires a minimal amount of Java code. In the meDroid example, all of the intent messages are created and dispatched when we click the icons on the main screen. The icons themselves are wired up to respond to the onClick event from the layout XML (refer to the second masterclass for details on how the onClickevents works).
The following code snippet shows how to create a new intent message and then use that to start another activity. The intent needs to know the sender as well as a receiver. In this code, we specify the sender via the getApplicationContext(), and directly indicate the receiver activity by specifying the class name. Once the intent is created, we dispatch it via the startActivity() method. The start activity method is provided by the Android framework and will handle the starting of the activity that we specified in the intent message.
publicvoid aboutMe(View v)
Intent intent = new Intent(getApplicationContext(), apc.examples.AboutActivity.class);
A critical component of the intent/activitymodel is the manifest file. For the above code to work properly, we need to specify the activities of this application in the manifest file (see AndroidManifest.xml for details of how activities are defined). A common error when one is new to the Android framework is to forget to add the activity details in the manifest file – I did quite a few times myself in the early days. If you miss it, you will get a rather nasty error message when you run the application and try to start an activity.
All of the activities are started from the main MeDroid1Activity in this application – see the Java code for the details on how the various activities are started. All activities are started via intents, but the code is very similar to the code snippet shown above.
In the previous masterclass, we covered how to provide different layout XML files to elegantly handle the portrait and landscape layouts. In this example, however, we have locked the orientation change on the MeDroid1Activity. If you wish to lock the orientation to a specific layout only this can be done via the manifest file. In our case, we lock the layout to portrait only by providing an attribute to the activity definition in the manifest file android:screenOrientation=“portrait”.
An interesting design feature of Android is that activities get restarted each time the orientation changes. Although, the choice seems rather odd it is to some extent unavoidable since each time the orientation changes the positions of the UI components has to be recomputed and then rendered again. In many Android devices, when we rotate the phone there is a clear refresh lag as the activities are reloaded back into memory and then rendered to the screen via a software library running directly on the CPU. Sadly, this approach is nearly 20 years old and made sense when computers did not often have a dedicated graphics card.
Unfortunately, the legacy of this method stayed with us all the way from the initial Android devices (without dedicated graphics acceleration) till the current generation of Android phones that are starting to incorporate dedicated graphics hardware. Thankfully, in the next major release of the Android operating systems, the plan is to correct this lag by moving the rendering and layout of the UI to the GPU – very similar to how iPhones have done pretty much since their first incarnation. Once the rendering is moved to the GPU, orientation changes will have that buttery smooth feel that we get in a properly engineered video game.
Using a Web View to display Tweets
In this application, we used the built-in Web View UI component to display the public tweets of a particular user. The code for this is in TwitterActivity (see code snippet below).
This block of code executes the following steps:
- Get a reference to the web view (as defined in the layout XML file).
- Get the user name from external string resource file (inres/values/strings.xml)
- Load the URL to display in the Web View (we use the mobile web site).
WebView mWebView = (WebView) findViewById(R.id.twitterWebView);
// get the user name from external string resource XML file
String username = getResources().getString(R.string.twitter_user_name);
The above code needs one more adjustment in the manifest file for it to work. More specifically, we need to request permission to access the Internet. This is defined using the following block of code in the XML:
The Android security model requires that the user allow access to various resources explicitly. However, when we are developing both the phone and the emulator will automatically grant the permission.
We did not directly step through the code for LocationActivity and the AboutActivity since these are based on concepts that were covered in earlier masterclasses. Please do browse through both the Java code and the layout XML files to get a better feel for how Android applications with multiple activities are structured. In particular, look at the manifest file closely. I would encourage you to extend the application by adding a few more activities. At a minimum, change the user name in the external resource file to grab your own tweets. In the next masterclass, we will start to extend the application by adding video/audio.