Application Fundamentals: in This Document
Application Fundamentals: in This Document
In this document
1. Application Components
1. Activating components: intents
2. Shutting down components
3. The manifest file
4. Intent filters
2. Activities and Tasks
1. Affinities and new tasks
2. Launch modes
3. Clearing the stack
4. Starting tasks
3. Processes and Threads
1. Processes
2. Threads
3. Remote procedure calls
4. Thread-safe methods
4. Component Lifecycles
1. Activity lifecycle
2. Service lifecycle
3. Broadcast receiver lifecycle
4. Processes and lifecycles
Key classes
1. Activity
2. Service
3. BroadcastReceiver
4. ContentProvider
5. Intent
Android applications are written in the Java programming language. The compiled Java
code — along with any data and resource files required by the application — is bundled
by the aapt tool into an Android package, an archive file marked by an .apk suffix. This
file is the vehicle for distributing the application and installing it on mobile devices; it's
the file users download to their devices. All the code in a single .apk file is considered to
be one application.
It's possible to arrange for two applications to share the same user ID, in which case
they will be able to see each other's files. To conserve system resources, applications
with the same ID can also arrange to run in the same Linux process, sharing the same
VM.
Application Components
A central feature of Android is that one application can make use of elements of other
applications (provided those applications permit it). For example, if your application
needs to display a scrolling list of images and another application has developed a
suitable scroller and made it available to others, you can call upon that scroller to do the
work, rather than develop your own. Your application doesn't incorporate the code of the
other application or link to it. Rather, it simply starts up that piece of the other
application when the need arises.
For this to work, the system must be able to start an application process when any part
of it is needed, and instantiate the Java objects for that part. Therefore, unlike
applications on most other systems, Android applications don't have a single entry point
for everything in the application (no main() function, for example). Rather, they have
essential components that the system can instantiate and run as needed. There are four
types of components:
Activities
An activity presents a visual user
interface for one focused endeavor
the user can undertake. For example,
an activity might present a list of menu
items users can choose from or it
might display photographs along with
their captions. A text messaging
application might have one activity
that shows a list of contacts to send
messages to, a second activity to
write the message to the chosen
contact, and other activities to review
old messages or change settings.
Though they work together to form a
cohesive user interface, each activity
is independent of the others. Each
one is implemented as a subclass of
the Activity base class.
One activity often starts the next one. If it expects a result back from the activity it's
starting, it calls startActivityForResult() instead of startActivity(). For example, if
it starts an activity that lets the user pick a photo, it might expect to be returned the
chosen photo. The result is returned in an Intent object that's passed to the calling
activity's onActivityResult() method.
• A service is started (or new instructions
are given to an ongoing service) by passing
an Intent object to Context.startService().
Android calls the service's onStart() method
and passes it the Intent object.
Similarly, an intent can be passed to Context.bindService() to establish an ongoing
connection between the calling component and a target service. The service receives
the Intent object in an onBind() call. (If the service is not already running,
bindService() can optionally start it.) For example, an activity might establish a
connection with the music playback service mentioned earlier so that it can provide the
user with the means (a user interface) for controlling the playback. The activity would
call bindService() to set up that connection, and then call methods defined by the
service to affect the playback.
A later section, Remote procedure calls, has more details about binding to a service.
• An application can initiate a broadcast
by passing an Intent object to methods like
Context.sendBroadcast(),
Context.sendOrderedBroadcast(), and
Context.sendStickyBroadcast() in any of
their variations. Android delivers the intent to
all interested broadcast receivers by calling
their onReceive() methods.
For more on intent messages, see the separate article, Intents and Intent Filters.
Activities, on the other hand, provide the user interface. They're in a long-running
conversation with the user and may remain active, even when idle, as long as the
conversation continues. Similarly, services may also remain running for a long time. So
Android has methods to shut down activities and services in an orderly way:
Components might also be shut down by the system when they are no longer being
used or when Android must reclaim memory for more active components. A later
section, Component Lifecycles, discusses this possibility and its ramifications in more
detail.
The manifest is a structured XML file and is always named AndroidManifest.xml for all
applications. It does a number of things in addition to declaring the application's
components, such as naming any libraries the application needs to be linked against
(besides the default Android library) and identifying any permissions the application
expects to be granted.
But the principal task of the manifest is to inform Android about the application's
components. For example, an activity might be declared as follows:
android:icon="@drawable/small_pic.png"
android:label="@string/freneticLabel"
. . . >
</activity>
. . .
</application>
</manifest>
The name attribute of the <activity> element names the Activity subclass that
implements the activity. The icon and label attributes point to resource files containing
an icon and label that can be displayed to users to represent the activity.
The other components are declared in a similar way — <service> elements for
services, <receiver> elements for broadcast receivers, and <provider> elements for
content providers. 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 they can be created
dynamically in code (as BroadcastReceiver objects) and registered with the system by
calling Context.registerReceiver().
For more on how to structure a manifest file for your application, see The
AndroidManifest.xml File.
Intent filters
An Intent object can explicitly name a target component. If it does, Android finds that
component (based on the declarations in the manifest file) and activates it. But if a
target is not explicitly named, Android must locate the best component to respond to the
intent. It does so by comparing the Intent object to the intent filters of potential targets. A
component's intent filters inform Android of the kinds of intents the component is able to
handle. Like other essential information about the component, they're declared in the
manifest file. Here's an extension of the previous example that adds two intent filters to
the activity:
android:icon="@drawable/small_pic.png"
android:label="@string/freneticLabel"
. . . >
<intent-filter . . . >
<action
android:name="android.intent.action.MA
IN" />
<category
android:name="android.intent.category.
LAUNCHER" />
</intent-filter>
<intent-filter . . . >
<action
android:name="com.example.project.BOUN
CE" />
<data
android:mimeType="image/jpeg" />
<category
android:name="android.intent.category.
DEFAULT" />
</intent-filter>
</activity>
. . .
</application>
</manifest>
The second filter declares an action that the activity can perform on a particular type of
data.
A component can have any number of intent filters, each one declaring a different set of
capabilities. If it doesn't have any filters, it can be activated only by intents that explicitly
name the component as the target.
For a broadcast receiver that's created and registered in code, the intent filter is
instantiated directly as an IntentFilter object. All other filters are set up in the
manifest.
For more on intent filters, see a separate document, Intents and Intent Filters.
The stack contains objects, so if a task has more than one instance of the same Activity
subclass open — multiple map viewers, for example — the stack has a separate entry
for each instance. Activities in the stack are never rearranged, only pushed and popped.
A task is a stack of activities, not a class or an element in the manifest file. So there's no
way to set values for a task independently of its activities. Values for the task as a whole
are set in the root activity. For example, the next section will talk about the "affinity of a
task"; that value is read from the affinity set for the task's root activity.
All the activities in a task move together as a unit. The entire task (the entire activity
stack) can be brought to the foreground or sent to the background. Suppose, for
instance, that the current task has four activities in its stack — three under the current
activity. The user presses the HOME key, goes to the application launcher, and selects
a new application (actually, a new task). The current task goes into the background and
the root activity for the new task is displayed. Then, after a short period, the user goes
back to the home screen and again selects the previous application (the previous task).
That task, with all four activities in the stack, comes forward. When the user presses the
BACK key, the screen does not display the activity the user just left (the root activity of
the previous task). Rather, the activity on the top of the stack is removed and the
previous activity in the same task is displayed.
The behavior just described is the default behavior for activities and tasks. But there are
ways to modify almost all aspects of it. The association of activities with tasks, and the
behavior of an activity within a task, is controlled by the interaction between flags set in
the Intent object that started the activity and attributes set in the activity's <activity>
element in the manifest. Both requester and respondent have a say in what happens.
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
The following sections describe what some of these flags and attributes do, how they
interact, and what considerations should govern their use.
If an .apk file contains more than one "application" from the user's point of view, you will
probably want to assign different affinities to the activities associated with each of them.
Launch modes
There are four different launch modes that can be assigned to an <activity> element's
launchMode attribute:
In contrast, the "singleTask" and "singleInstance" modes mark activities that are
always at the root of a task. They define a task; they're never launched into another
task.
• Whether there can be multiple
instances of the activity. A "standard" or
"singleTop" activity can be instantiated many
times. They can belong to multiple tasks, and
a given task can have multiple instances of
the same activity.
In contrast, "singleTask" and "singleInstance" activities are limited to just one
instance. Since these activities are at the root of a task, this limitation means that there
is never more than a single instance of the task on the device at one time.
• Whether the instance can have other
activities in its task. A "singleInstance"
activity stands alone as the only activity in its
task. If it starts another activity, that activity
will be launched into a different task
regardless of its launch mode — as if
FLAG_ACTIVITY_NEW_TASK was in the intent. In
all other respects, the "singleInstance"
mode is identical to "singleTask".
The other three modes permit multiple activities to belong to the task. A "singleTask"
activity will always be the root activity of the task, but it can start other activities that will
be assigned to its task. Instances of "standard" and "singleTop" activities can appear
anywhere in a stack.
For example, suppose a task's activity stack consists of root activity A with activities B,
C, and D on top in that order, so the stack is A-B-C-D. An intent arrives for an activity of
type D. If D has the default "standard" launch mode, a new instance of the class is
launched and the stack becomes A-B-C-D-D. However, if D's launch mode is
"singleTop", the existing instance is expected to handle the new intent (since it's at the
top of the stack) and the stack remains A-B-C-D.
If, on the other hand, the arriving intent is for an activity of type B, a new instance of B
would be launched no matter whether B's mode is "standard" or "singleTop" (since B is
not at the top of the stack), so the resulting stack would be A-B-C-D-B.
As noted above, there's never more than one instance of a "singleTask" or
"singleInstance" activity, so that instance is expected to handle all new intents. A
"singleInstance" activity is always at the top of the stack (since it is the only activity in
the task), so it is always in position to handle the intent. However, a "singleTask"
activity may or may not have other activities above it in the stack. If it does, it is not in
position to handle the intent, and the intent is dropped. (Even though the intent is
dropped, its arrival would have caused the task to come to the foreground, where it
would remain.)
When an existing activity is asked to handle a new intent, the Intent object is passed to
the activity in an onNewIntent() call. (The intent object that originally started the activity
can be retrieved by calling getIntent().)
Note that when a new instance of an Activity is created to handle a new intent, the user
can always press the BACK key to return to the previous state (to the previous activity).
But when an existing instance of an Activity handles a new intent, the user cannot press
the BACK key to return to what that instance was doing before the new intent arrived.
For more on launch modes, see the description of the <activity> element.
There's another way to force activities to be removed from the stack. If an Intent object
includes the FLAG_ACTIVITY_CLEAR_TOP flag, and the target task already has an instance
of the type of activity that should handle the intent in its stack, all activities above that
instance are cleared away so that it stands at the top of the stack and can respond to
the intent. If the launch mode of the designated activity is "standard", it too will be
removed from the stack, and a new instance will be launched to handle the incoming
intent. That's because a new instance is always created for a new intent when the
launch mode is "standard".
FLAG_ACTIVITY_CLEAR_TOP is most often used in conjunction with
FLAG_ACTIVITY_NEW_TASK. When used together, these flags are a way of locating an
existing activity in another task and putting it in a position where it can respond to the
intent.
Starting tasks
An activity is set up as the entry point for a task by giving it an intent filter with
"android.intent.action.MAIN" as the specified action and
"android.intent.category.LAUNCHER" as the specified category. (There's an example of
this type of filter in the earlier Intent Filters section.) A filter of this kind causes an icon
and label for the activity to be displayed in the application launcher, giving users a way
both to launch the task and to return to it at any time after it has been launched.
This second ability is important: Users must be able to leave a task and then come back
to it later. For this reason, the two launch modes that mark activities as always initiating
a task, "singleTask" and "singleInstance", should be used only when the activity has a
MAIN and LAUNCHER filter. Imagine, for example, what could happen if the filter is missing:
An intent launches a "singleTask" activity, initiating a new task, and the user spends
some time working in that task. The user then presses the HOME key. The task is now
ordered behind and obscured by the home screen. And, because it is not represented in
the application launcher, the user has no way to return to it.
For those cases where you don't want the user to be able to return to an activity, set the
<activity> element's finishOnTaskLaunch to "true". See Clearing the stack, earlier.
Processes
The process where a component runs is controlled by the manifest file. The component
elements — <activity>, <service>, <receiver>, and <provider> — each have a
process attribute that can specify a process where that component should run. These
attributes can be set so that each component runs in its own process, or so that some
components share a process while others do not. They can also be set so that
components of different applications run in the same process — provided that the
applications share the same Linux user ID and are signed by the same authorities. The
<application> element also has a process attribute, for setting a default value that
applies to all components.
All components are instantiated in the main thread of the specified process, and system
calls to the component are dispatched from that thread. Separate threads are not
created for each instance. Consequently, methods that respond to those calls —
methods like View.onKeyDown() that report user actions and the lifecycle notifications
discussed later in the Component Lifecycles section — always run in the main thread of
the process. This means that no component should perform long or blocking operations
(such as networking operations or computation loops) when called by the system, since
this will block any other components also in the process. You can spawn separate
threads for long operations, as discussed under Threads, next.
Android may decide to shut down a process at some point, when memory is low and
required by other processes that are more immediately serving the user. Application
components running in the process are consequently destroyed. A process is restarted
for those components when there's again work for them to do.
When deciding which processes to terminate, Android weighs their relative importance
to the user. For example, it more readily shuts down a process with activities that are no
longer visible on screen than a process with visible activities. The decision whether to
terminate a process, therefore, depends on the state of the components running in that
process. Those states are the subject of a later section, Component Lifecycles.
Threads
Even though you may confine your application to a single process, there will likely be
times when you will need to spawn a thread to do some background work. Since the
user interface must always be quick to respond to user actions, the thread that hosts an
activity should not also host time-consuming operations like network downloads.
Anything that may not be completed quickly should be assigned to a different thread.
Threads are created in code using standard Java Thread objects. Android provides a
number of convenience classes for managing threads — Looper for running a message
loop within a thread, Handler for processing messages, and HandlerThread for setting
up a thread with a message loop.
An RPC interface can include only methods. By default, all methods are executed
synchronously (the local method blocks until the remote method finishes), even if there
is no return value.
In brief, the mechanism works as follows: You'd begin by declaring the RPC interface
you want to implement using a simple IDL (interface definition language). From that
declaration, the aidl tool generates a Java interface definition that must be made
available to both the local and the remote process. It contains two inner class, as shown
in the following diagram:
The inner classes have all the code needed to administer remote procedure calls for the
interface you declared with the IDL. Both inner classes implement the IBinder interface.
One of them is used locally and internally by the system; the code you write can ignore
it. The other, called Stub, extends the Binder class. In addition to internal code for
effectuating the IPC calls, it contains declarations for the methods in the RPC interface
you declared. You would subclass Stub to implement those methods, as indicated in the
diagram.
Typically, the remote process would be managed by a service (because a service can
inform the system about the process and its connections to other processes). It would
have both the interface file generated by the aidl tool and the Stub subclass
implementing the RPC methods. Clients of the service would have only the interface file
generated by the aidl tool.
Here's how a connection between a service and its clients is set up:
This brief description omits some details of the RPC mechanism. For more information,
see Designing a Remote Interface Using AIDL and the IBinder class description.
Thread-safe methods
In a few contexts, the methods you implement may be called from more than one
thread, and therefore must be written to be thread-safe.
This is primarily true for methods that can be called remotely — as in the RPC
mechanism discussed in the previous section. When a call on a method implemented in
an IBinder object originates in the same process as the IBinder, the method is executed
in the caller's thread. However, when the call originates in another process, the method
is executed in a thread chosen from a pool of threads that Android maintains in the
same process as the IBinder; it's not executed in the main thread of the process. For
example, whereas a service's onBind() method would be called from the main thread of
the service's process, methods implemented in the object that onBind() returns (for
example, a Stub subclass that implements RPC methods) would be called from threads
in the pool. Since services can have more than one client, more than one pool thread
can engage the same IBinder method at the same time. IBinder methods must,
therefore, be implemented to be thread-safe.
Similarly, a content provider can receive data requests that originate in other processes.
Although the ContentResolver and ContentProvider classes hide the details of how the
interprocess communication is managed, ContentProvider methods that respond to
those requests — the methods query(), insert(), delete(), update(), and getType()
— are called from a pool of threads in the content provider's process, not the main
thread of the process. Since these methods may be called from any number of threads
at the same time, they too must be implemented to be thread-safe.
Component Lifecycles
Application components have a lifecycle — a beginning when Android instantiates them
to respond to intents through to an end when the instances are destroyed. In between,
they may sometimes be active or inactive,or, in the case of activities, visible to the user
or invisible. This section discusses the lifecycles of activities, services, and broadcast
receivers — including the states that they can be in during their lifetimes, the methods
that notify you of transitions between states, and the effect of those states on the
possibility that the process hosting them might be terminated and the instances
destroyed.
Activity lifecycle
An activity has essentially three states:
void onCreate(Bundle
savedInstanceState)
void onStart()
void onRestart()
void onResume()
void onPause()
void onStop()
void onDestroy()
All of these methods are hooks that you can override to do appropriate work when the
state changes. All activities must implement onCreate() to do the initial setup when the
object is first instantiated. Many will also implement onPause() to commit data changes
and otherwise prepare to stop interacting with the user.
Taken together, these seven methods define the entire lifecycle of an activity. There are
three nested loops that you can monitor by implementing them:
Always followed by
onStart().
Methods that are marked "No" in the Killable column protect the process hosting the
activity from being killed from the moment they are called. Thus an activity is in a killable
state, for example, from the time onPause() returns to the time onResume() is called. It
will not again be killable until onPause() again returns.
As noted in a later section, Processes and lifecycle, an activity that's not technically
"killable" by this definition might still be killed by the system — but that would happen
only in extreme and dire circumstances when there is no other recourse.
To capture that state before the activity is killed, you can implement an
onSaveInstanceState() method for the activity. Android calls this method before making
the activity vulnerable to being destroyed — that is, before onPause() is called. It passes
the method a Bundle object where you can record the dynamic state of the activity as
name-value pairs. When the activity is again started, the Bundle is passed both to
onCreate() and to a method that's called after onStart(), onRestoreInstanceState(),
so that either or both of them can recreate the captured state.
Unlike onPause() and the other methods discussed earlier, onSaveInstanceState() and
onRestoreInstanceState() are not lifecycle methods. They are not always called. For
example, Android calls onSaveInstanceState() before the activity becomes vulnerable
to being destroyed by the system, but does not bother calling it when the instance is
actually being destroyed by a user action (such as pressing the BACK key). In that
case, the user won't expect to return to the activity, so there's no reason to save its
state.
Because onSaveInstanceState() is not always called, you should use it only to record
the transient state of the activity, not to store persistent data. Use onPause() for that
purpose instead.
Coordinating activities
When one activity starts another, they both experience lifecycle transitions. One pauses
and may stop, while the other starts up. On occasion, you may need to coordinate these
activities, one with the other.
The order of lifecycle callbacks is well defined, particularly when the two activities are in
the same process:
Service lifecycle
A service can be used in two ways:
Like an activity, a service has lifecycle methods that you can implement to monitor
changes in its state. But they are fewer than the activity methods — only three — and
they are public, not protected:
void onCreate()
void onStart(Intent intent)
void onDestroy()
By implementing these methods, you can monitor two nested loops of the service's
lifecycle:
If a service permits others to bind to it, there are additional callback methods for it to
implement:
The onBind() callback is passed the Intent object that was passed to bindService and
onUnbind() is handed the intent that was passed to unbindService(). If the service
permits the binding, onBind() returns the communications channel that clients use to
interact with the service. The onUnbind() method can ask for onRebind() to be called if
a new client connects to the service.
The following diagram illustrates the callback methods for a service. Although, it
separates services that are created via startService from those created by
bindService(), keep in mind that any service, no matter how it's started, can potentially
allow clients to bind to it, so any service may receive onBind() and onUnbind() calls.
Broadcast receiver lifecycle
A broadcast receiver has single callback method:
When a broadcast message arrives for the receiver, Android calls its onReceive()
method and passes it the Intent object containing the message. The broadcast receiver
is considered to be active only while it is executing this method. When onReceive()
returns, it is inactive.
A process with an active broadcast receiver is protected from being killed. But a process
with only inactive components can be killed by the system at any time, when the
memory it consumes is needed by other processes.
This presents a problem when the response to a broadcast message is time consuming
and, therefore, something that should be done in a separate thread, away from the main
thread where other components of the user interface run. If onReceive() spawns the
thread and then returns, the entire process, including the new thread, is judged to be
inactive (unless other application components are active in the process), putting it in
jeopardy of being killed. The solution to this problem is for onReceive() to start a service
and let the service do the job, so the system knows that there is still active work being
done in the process.
The next section has more on the vulnerability of processes to being killed.
Because a process running a service is ranked higher than one with background
activities, an activity that initiates a long-running operation might do well to start a
service for that operation, rather than simply spawn a thread — particularly if the
operation will likely outlast the activity. Examples of this are playing music in the
background and uploading a picture taken by the camera to a web site. Using a service
guarantees that the operation will have at least "service process" priority, regardless of
what happens to the activity. As noted in the Broadcast receiver lifecycle section earlier,
this is the same reason that broadcast receivers should employ services rather than
simply put time-consuming operations in a thread.