Regardless of your prior programming experiences, be it Windows, macOS, Linux, or even iOS based, the chances are good that Android development is quite unlike anything you have encountered before.
Therefore, this chapter’s objective is to provide an understanding of the high-level concepts behind the architecture of Android applications. In doing so, we will explore in detail the various components that can be used to construct an application and the mechanisms that allow these to work together to create a cohesive application.
Those familiar with object-oriented programming languages such as Java, Kotlin, C++, or C# will be familiar with the concept of encapsulating elements of application functionality into classes that are then instantiated as objects and manipulated to create an application. This is still true since Android applications are written in Java and Kotlin. Android, however, also takes the concept of reusable components to a higher level.
Android applications are created by combining one or more components known as Activities. An activity is a single, standalone module of application functionality that usually correlates directly to a single user interface screen and its corresponding functionality. An appointment application might, for example, have an activity screen that displays appointments set up for the current day. An appointment application might have an activity screen that displays appointments set up for the current day. The application might also utilize a second activity consisting of a screen where the user may enter new appointments.
Activities are intended as fully reusable and interchangeable building blocks that can be shared amongst different applications. An existing email application may contain an activity for composing and sending an email message. A developer might be writing an application that is also required to send an email message. Rather than develop an email composition activity specifically for the new application, the developer can use the activity from the existing email application.
Activities are created as subclasses of the Android Activity class and must be implemented so as to be entirely independent of other activities in the application. In other words, a shared activity cannot rely on being called at a known point in a program flow (since other applications may use the activity in unanticipated ways), and one activity cannot directly call methods or access instance data of another activity. This, instead, is achieved using Intents and Content Providers.
By default, an activity cannot return results to the activity from which it was invoked. If this functionality is required, the activity must be started explicitly as a sub-activity of the originating activity.
As described above, an activity typically represents a single user interface screen within an app. One option is constructing the activity using a single user interface layout and one corresponding activity class file. A better alternative, however, is to break the activity into different sections. Each section is a fragment consisting of part of the user interface layout and a matching class file (declared as a subclass of the Android Fragment class). In this scenario, an activity becomes a container into which one or more fragments are embedded.
Fragments provide an efficient alternative to having each user interface screen represented by a separate activity. Instead, an app can have a single activity that switches between fragments, each representing a different app screen.
Intents are the mechanism by which one activity can launch another and implement the flow through the activities that make up an application. Intents consist of a description of the operation to be performed and, optionally, the data on which it is to be performed.
Intents can be explicit, in that they request the launch of a specific activity by referencing the activity by class name, or implicit by stating either the type of action to be performed or providing data of a specific type on which the action is to be performed. In the case of implicit intents, the Android runtime will select the activity to launch that most closely matches the criteria specified by the Intent using a process referred to as Intent Resolution.
Another type of Intent, the Broadcast Intent, is a system-wide intent sent out to all applications that have registered an “interested” Broadcast Receiver. The Android system, for example, will typically send out Broadcast Intents to indicate changes in device status, such as the completion of system start-up, connection of an external power source to the device, or the screen being turned on or off.
A Broadcast Intent can be normal (asynchronous) in that it is sent to all interested Broadcast Receivers at more or less the same time or ordered in that it is sent to one receiver at a time where it can be processed and then either aborted or allowed to be passed to the next Broadcast Receiver.
Broadcast Receivers are the mechanism by which applications can respond to Broadcast Intents. A Broadcast Receiver must be registered by an application and configured with an Intent Filter to indicate the types of broadcast it is interested in. When a matching intent is broadcast, the receiver will be invoked by the Android runtime regardless of whether the application that registered the receiver is currently running. The receiver then has 5 seconds to complete required tasks (such as launching a Service, making data updates, or issuing a notification to the user) before returning. Broadcast Receivers operate in the background and do not have a user interface.
Android Services are processes that run in the background and do not have a user interface. They can be started and managed from activities, Broadcast Receivers, or other Services. Android Services are ideal for situations where an application needs to continue performing tasks but does not necessarily need a user interface to be visible to the user. Although Services lack a user interface, they can still notify the user of events using notifications and toasts (small notification messages that appear on the screen without interrupting the currently visible activity) and are also able to issue Intents.
The Android runtime gives Services a higher priority than many other processes and will only be terminated as a last resort by the system to free up resources. If the runtime needs to kill a Service, however, it will be automatically restarted as soon as adequate resources become available. A Service can reduce the risk of termination by declaring itself as needing to run in the foreground. This is achieved by making a call to startForeground(). This is only recommended for situations where termination would be detrimental to the user experience (for example, if the user is listening to audio being streamed by the Service).
Example situations where a Service might be a practical solution include, as previously mentioned, the streaming of audio that should continue when the application is no longer active or a stock market tracking application 80
The Anatomy of an Android Application that needs to notify the user when a share hits a specified price.
Content Providers implement a mechanism for the sharing of data between applications. Any application can provide other applications with access to its underlying data by implementing a Content Provider, including the ability to add, remove and query the data (subject to permissions). Access to the data is provided via a Universal Resource Identifier (URI) defined by the Content Provider. Data can be shared as a file or an entire SQLite database.
The native Android applications include several standard Content Providers allowing applications to access data such as contacts and media files. The Content Providers currently available on an Android system may be located using a Content Resolver.
The Application Manifest
The Application Manifest file is the glue that pulls together the various elements that comprise an application. Within this XML-based file, the application outlines the activities, services, broadcast receivers, data providers, and permissions that comprise the complete application.
In addition to the manifest file and the Dex files containing the byte code, an Android application package typically contains a collection of resource files. These files contain resources such as strings, images, fonts, and colors that appear in the user interface, together with the XML representation of the user interface layouts. These files are stored in the /res sub-directory of the application project’s hierarchy by default.
When an application is compiled, a class named R is created containing references to the application resources. The application manifest file and these resources combine to create what is known as the Application Context. This context, represented by the Android Context class, may be used in the application code to gain access to the application resources at runtime. In addition, a wide range of methods may be called on an application’s context to gather information and change the application’s environment at runtime.
A number of different elements can be brought together to create an Android application. In this chapter, we have provided a high-level overview of Activities, Fragments, Services, Intents, and Broadcast Receivers and an overview of the manifest file and application resources.
Maximum reuse and interoperability are promoted by creating individual, standalone functionality modules in the form of activities and intents while implementing content providers to achieve data sharing between applications.
While activities are focused on areas where the user interacts with the application (an activity essentially equating to a single user interface screen and often made up of one or more fragments), background processing is typically handled by Services and Broadcast Receivers.
The components that make up the application are outlined for the Android runtime system in a manifest file which, combined with the application’s resources, represents the application’s context.
Much has been covered in this chapter that is likely new to the average developer. Rest assured, however, that extensive exploration and practical use of these concepts will be made in subsequent chapters to ensure a solid knowledge foundation on which to build your own applications.