Previous  | Next  | Home

Application Fundamentals


 

This is a brief synopsis of the structure of an Android application (see this more extensive discussion). In learning to program within a new software development kit it is always important to understand such structure. However, this is particularly so for a language like Android, which is designed to operate on devices with constrained resources that may not be very tolerant of poorly constructed programs.

 

Overview of an Android Program

Android programs are written in Java, supplemented by various resources supplied in terms of XML layout and data files, image files, raw data files, and so on. Much of the standard Java API is accessible, except for those classes that don't have an obvious use on mobile devices, or those whose functionality is better implemented through classes optimized specifically for mobile applications. For example, the usual Java layout tools such as the AWT and Swing are not implemented because they are replaced by Android layout tools optimized for Android devices.

Although not essential to a functioning Android program, best practices dictate that the presentation layer should be separated from the computing layer when feasible. The model for accomplishing this in Android is to place as much as is convenient of the user interface (UI) layout specification in XML resource files, with the Java coding reserved for defining event handlers for the UI widgets and methods to accomplish the required tasks. This is different from the usual programming of Java, where one would commonly use tools like Swing to lay out the UI within the Java code.


One can lay out the UI in Android using only Java, but it is not considered best practice because it entangles presentation with logic and computing, which complicates maintenance and extension of an application. Of course, mindless adherence to rules is a poor way to program and for more complex and dynamical layouts it may make perfectly good sense to handle much or all of the layout in Java. We shall give many examples of both approaches.

The compiled Java code, and any data and resource files required by the code, are bundled automatically by the Android development tools into an Android package, which is an archive file with a .apk extension. All the code in a single .apk file is one Android application (or app). Installing an app on a mobile device consists of installing the corresponding .apk file on that device.

 

The Android Security Model: Each Application is an Island

Central to the Android security model is that each application is isolated from all other applications unless explicit permissions are given to allow them to communicate or share resources. By default:

This is termed the principle of least privilege: by default, each app has access only to the components that it requires for its functionality and no more. Thus, the crash of an application is generally not of direct consequence to any other application because each app is sandboxed in a separate virtual machine. There are mechanisms to change these defaults and to allow different apps to share data and resources, but to do so requires explicit override of the defaults. For example, two applications can be made to share the same ID, in which case they can see each other's files, and if they share the same ID they can we arranged to share the same VM by running under the same Linux process.

 

Components of an Application

One of the first questions an experienced Java or C programmer looking at an Android application for the first time might ask is, "where is the main() method"? In a stand-alone Java program written for a desktop computer one would typically have a main() method that defines the (single) entry point for the program. Android is structured differently because of the nature of its target deployment environment: hardware systems that use Android (phones, tablet computers, embedded devices ...) typically have limited resources relative to desktop computers. In an Android application there is no main() method. Rather, Android is structured so that (if permissions are given) applications may use relevant pieces of other applications, without having to load all of the other application.

For example, in the project Sharing with Intents, we illustrate by creating as part of the overall application a class that implements a rudimentary web browser, and providing the capability for a completely different app to use that browser to display webpages without invoking any of the rest of our application. This flexibility is important in implementing "lean and mean" applications that use minimal resources, and implies that well-written Android applications often will be highly modular, with multiple entry points. These applications are constructed from four types of components:

  1. Activities

  2. Services

  3. Broadcast receivers

  4. Content providers

all of which run by default on the main thread of the UI. Let us now summarize the nature of these four basic ingredients that make up any Android application.

When an application is initiated by executing its initial component, Android starts a corresponding Linux process with a single execution thread. By default, all components of the application run in that process and thread. However, components can be arranged to run in other processes, and any process can spawn additional threads. As we will discuss further below, implementing such options is important for operations that have the potential to block the main UI thread, if one wants to retain responsiveness in an application.

 

Activities

An activity is a single, focused "activity"; the closest analogy in normal computing is probably to that of a window on a desktop computer screen. For example, a simple activity might present a set of choice buttons to a user. An application could consist of a single activity, but more often will involve a sequence of activities, each of which is independent of the others, but all organized toward the common goal of the application. The typical motif for accomplishing this is to designate one activity as the screen first presented to the user, with movement between this and subsequent activities accomplished by having a current activity call another (with the visual consequence most often being that one visible screen is replaced by another on the device). For example, clicking a button on the first screen might replace that screen with another screen containing other widgets, a web page, a movie, or text information. Visually, the window corresponding to an activity represents a hierarchy of view objects derived from the base class View, as we discuss more extensively in Android User Interfaces.

Activities are typically created by subclassing (extending) the Android class Activity. Since most activities will be expected to interact with the user, the Activity class automatically takes care of creating a window for you in which you can place your user interface (UI) with the method setContentView(View). Activities most often are associated with full-screen windows, but they can also be floating windows (by implementing a theme with windowIsFloating set) or they can be embedded inside another activity (using ActivityGroup).

There are several methods of the Activity class that most subclasses will override (provide their own custom implementations of):

All activity classes require a corresponding <activity> declaration in the AndroidManifest.xml file for their package. Activities, like the other basic Android components, run on the main thread of the UI.

 

Services

A service extends the base class Service and corresponds to a process that runs in the background without a visual user interface. These are similar to Unix daemons. A service typically is used when an application wishes to perform a long-term operation that does not require (or desire) explicit user interaction, or wishes to supply continuous functionality for other applications to use. Examples of tasks for which you might want to use a service include a GPS tracking program that monitors the location of the device and transmits it to a server, a program that plays music in the background while a user is performing other tasks, or a program continuously monitoring a data feed in real time for sports scores or stock market quotes.

You can start a service if it is not running using Context.startService(), connect (bind) to a running service using Context.bindService(), and communicate with the running service through an interface that the service exposes. Each service class must have a corresponding <service> declaration in the AndroidManifest.xml file for its package.

By default, a service is not a separate process, nor is it a separate execution thread; services, like all UI components, run by default on the main thread of the application process. Therefore, if services involve blocking operations like network access, or computationally intensive tasks like repetitive loops, you will want to consider putting these operations on background threads so that they don't block the main UI.

 

Broadcast Receivers

A broadcast receiver extends the base class BroadcastReceiver and has only one function: to listen for and respond to broadcast announcements. Applications can initiate broadcasts (e.g., a message for other interested applications that some data have been received over the network), but often broadcasts involve things like timezone changes or low-battery alerts that originate at the System level.

An application can have any number of broadcast receivers. A broadcast receiver does not display an explicit user interface but will typically generate a visual cue for the user by initiating an activity or by using a NotificationManager to display a persistent icon in the status bar of the device, vibrate, play a sound, flash lights, etc. to indicate to the user that something significant has happened in the background.

 

Content Providers

By default the data for an application in Android are private and not visible to other applications. However, an application may choose to use a content provider that extends the ContentProvider class and makes a specified part of the application's data available to other applications (see the Content Providers document for a more extensive discussion). Other applications wishing to receive data from a ContentProvider do not call its methods directly but rather instantiate a ContentResolver object and use its methods to retrieve the data from the content provider. The ContentResolver object has the ability to cooperate with any content provider to manage any interprocess communications that are necessary.

Android supplies by default a number of content providers for common data types like audio, video, images, or personal contact information (see the android.provider package). Your application can query these packages for the data they contain if it has acquired the appropriate permissions. There are two ways to make data from your own application public:

  1. Add your data to an existing Android provider, if there is one that controls the same type of data and your app has permission to write to it.

  2. Create your own content provider by subclassing ContentProvider.

How to do each is described in Content Providers.

 

Sharing Components

As noted above, a powerful feature of the Android operating system is that any app can start a component of another app (subject to appropriate permission being given). For example, if you want in your app to give the user the capability to make a barcode scan, there is likely another app on the device that has this functionality and instead of incorporating code in your app to do the scan you can simply appropriate the functionality of the other app to implement it.

You don't need to incorporate or even reference in any direct way the code from the other app that does the scan. Nor does your app need to know much about the internal workings of the other app, other than that it has declared itself to be capable of handling your request. Instead, you effectively start the activity in the app that captures a barcode scan, and when the scan is complete it can be returned to your app so you can use it. To the user, it seems as if the barcode scanner is a component of your app.


In reality, the above description captures the essence of the user experience but is somewhat bogus with respect to what takes place under the hood. Your app cannot activate the other app directly, because of the sandboxing in the Android security model where each app runs in a separate process with file permissions restricting access to other apps. To activate a component in another app, your app delivers a message to the Android system specifying an intent to start a particular component and the Android system then activates the component of the other app. This clever but simple scheme enables very powerful interoperability while maintaining security, since the two apps can share substantial functionality without talking directly to each other or revealing details of their inner workings.

When the Android system starts a component, it starts the process for that app and instantiates the required classes. For example, if your app starts the activity in the barcode scanner app, that activity runs in the process that belongs to the barcode app, not in the process corresponding to your app. As we have noted, this means that Android apps typically don't have a single entry point.

In the project Sharing with Intents we shall implement various examples both of

You will find that this capability of sharing components among apps within a secure environment with no knowledge of the interal workings of the sharing app required to be an extremely useful feature of Android.

 

The Android Manifest File

Android must know that a component of an application exists before it can start it. This is accomplished by having applications declare their components in an XML manifest file that is always called AndroidManifest.xml, and that every application must contain. As we shall find, the manifest file does many other things, such as naming any required external libraries and identifying any permissions the application expects to be granted, but its most fundamental task is to inform Android about the application's components. Here is an example of a simple manifest file


<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lightcone.webviewdemo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".Webscreen" android:label="Web"> </activity> </application> <uses-permission android:name="android.permission.INTERNET" /> </manifest>

in which two activities, WebViewDemo and Webscreen are declared. Additional features of this manifest file, such as the permissions tags, will be discussed later.


Activities, services, and content providers that are not declared in the manifest are not visible to the system and are consequently never run. However, broadcast receivers can either be declared in the manifest or created dynamically in code.

Under Android Studio in newer versions of Android various information that was entered in the manifest file for earlier versions of Android is now found in a Gradle file with a typical name build.gradle (Module: app). For example, the minimum version of the SDK expected to run the app properly can still be specified in the manifest file, but now Android Studio will by default put it in the build.gradle file. In some cases the framework will now supply information that formerly was put explicitly in the manifest file. For example, in the project Map Example we will need a permission to access the internet but will find that it is not necessary to place it explicitly in the manifest file because the Google Play framework will supply it at runtime.

We also note that in newer versions of Android the permissions model has evolved. Now some permissions that were specified statically entirely in the manifest file have, as of API 23, been converted to runtime permissions that must be specified in the manifest file (for backward compatibility), but must also be obtained from the user at the first usage of the permission in the app. An example of these runtime permissions will be given in the project Map Example.

For a more extensive discussion of the manifest file, see the App Manifest document.

 

Using Intents to Activate Components

The basic organization of an Android application is that components call each other. How is that implemented? We have just seen that a content provider is activated when it is targeted by the methods of a ContentResolver object. The other three types of Android application components (activities, services, and broadcast receivers) are activated by asynchronous messages called intents that are objects of the Intent class.

An intent is a passive data structure that holds an abstract description of an operation to be performed (or, in the case of BroadcastReceivers, it may hold a description of something that has happened and is being announced), and can provide late runtime binding between code in different applications. It can be used in conjunction with the methods

The most common use of an intent is in the launching of activities, where it can be thought of loosely as the "glue" between activities.

 

Explicit and Implicit Intents

Intents in Android fall into two broad categories:

  1. Explicit intents, where the target component is designated explicitly by name.

  2. Implicit intents, where the target component is not named explicitly and the Android system is expected to determine the best component to implement the intent.

To resolve implicit intents, Android relies on intent filters, which are described in the next section.

 

Intent Filters

An intent can name a target component but if a target is not named explicitly Android locates the best component to respond to the intent by comparing the intent object to the intent filters of potential targets. A component's intent filters tell Android which kinds of intents the component is able to handle. (It is this capability that enables the sharing of components described above.) The intent filters of a component are declared in the manifest file, as illustrated by the intent-filter tags in the above listing of a sample Manifest.xml file. A more extensive discussion of intents and intent filters may be found in Intents and Intent Filters.

 

Using Intents to Launch Google Applications

A programmer can use an Intent to launch various Google applications in particular ways, for example to dial a specific phone number or open a streetview on a particular location. Here is a partial list of possibilities.

Last modified: July 25, 2016


Previous  | Next  | Home