One of the products we are building at Directi is a x-platform mobile chat client. Developing it has been arduous – we experimented with Phonegap and then Titanium and found both lacking (see this Quora thread for details). So now we are building this as three native apps, targeting iPhone, Android and Blackberry. In this post I am going to cover the Android platform, and would follow up with iOS and Blackberry.
Android Architecture
The following diagram (from http://developer.android.com/guide/basics/what-is-android.html) gives the overall architecture of the Android OS:

The Android OS is basically a Linux 2.6.4 kernel fork. The Linux kernel is used for memory management, process management, security, driver model, shared lib model. Google has added Android specific enhancements: Power management, Kernel debugger, Logger, Low Memory killer, Shared Memory Driver, IPC Driver, etc.
Above the kernel, in the user space, sits the Hardware abstraction layer – this deals with stuff like Radio, Wifi, GPS, Graphics, Audio, Camera, Bluetooth, etc. Basically this defines a set of interfaces that drivers need to implement. These libaries are loaded by the system at runtime on a need basis.
Above the HAL, sit a bunch of libraries – the most important of course is libc – Google has written a port called Bionic libc which is optimized for embedded use – in terms of being small in size (since it is loaded in every process) and fast (since CPU is limited), but also because libc is GPL and Google wanted to protect apps from being GPLed. Besides there is webkit of course and SQLite. Google also provides a Media Framework, OpenGL library, and libraries for UI rendering and audio.
The Android programming environment is based on Java. For this, the OS ships with Dalvik – a custom implementation of the JVM optimized for the embedded environment. Dalivk does not run the Java bytecode, but its own custom bytecode called Dex – Dalvix Executable. It uses runtime memory very efficiently, the bytecode interpreter is highly CPU optimized, and because of this low footprint, the VM can be run in multiple processes – basically each app gets its own Linux process with its own instance of a Dalvik VM. The VM is exposed by a set of core Java APIs which provide the familiar Java library.
All of the above is wrapped in a set of platform services called the Android Application Framework – these services work behind the scenes to provide abstractions like Activities, Packages, Windows, Resources, Content Providers, View System, hardware access, etc.
Startup
Like a typical Linux system, in Android, the bootstrapper loads the Linux kernel and starts the init process, which in turn starts various daemons like Android Debug Bridge (adbd), Radio Interface Layer Daemon (rild), etc. After this, init starts what is called the Zygote process. The purpose of this process is to kick off the rest of the Android system and later help in the instantiation of apps. The Zygote process initializes a Dalvik VM instance and loads a bunch of libraries and starts listening on a socket for requests to spawn more processes (with VM instances). As requests come in, it forks to create new processes with VM instances. Copy-on-write is used to maximize reuse and minimize footprint.

(Android Startup Sequence – source: http://sites.google.com/site/io/anatomy–physiology-of-an-android)
Once Zygote is in place, the init process starts what is called the Runtime process. This basically does two things:
- Start the Service Manager – this is responsible for managing IPC and all services are required to register with it. It acts like a local DNS providing a way to bind to a service given its name.
- Ask Zygote to fork what is called the System Server. This is the first process (besides Zygote) that has a running VM instance. The System server starts services for display (Surface Flinger) and audio (Audio Flinger). These services register with the service manager like all services are expected to. Now other apps can start using display and audio. After this, the System server starts up all the core platform services – Window manager, Telephony manager, Power manager, Activity manager, etc. Each one of these services also registers with the Service Manager, so that apps can use these services.
At this stage, we have the following processes in place:
- Init – the original init process started by the bootstrapper
- Daemons – started by init
- Runtime – also started by init
- Zygote – the original zygote which will continually get forked as new apps get launched
- System Server – the first managed process which contains all the core services and platform components.
After this, the Home Screen or the Idle screen is launched – basically the Activity Manager (which is inside the System Server) sends a message to Zygote to start the “Home” Activity, which causes Zygote to fork into a new process with a Dalvik VM and the Home activity. Now depending on the action carried out by the user, the appropriate app would be launched with the Zygote forlking each time to create a new VM instance inside a new process. So for example, if the user starts the Contacts app, the apt process would be setup:

(Startup processes in Android - source: http://sites.google.com/site/io/anatomy–physiology-of-an-android)
Each new app gets a unique user Id which is opaque to the app, and the system sets permissions for all files in the app so that only the user id assigned to the app can access those files.This makes the system secure since the app only has access to the components that it needs to do its work and nothing else. However, apps may need to communicate with each other and the system services, which are all running in separate processes. This is accomplished thru Inter Process Communication
IPC
With its emphasis on isolating apps and services in process boundaries, it is clear that Android requries a lightweight IPC. The IPC mechanism in Android is called Binder and is based on shared memory. Recall that when a process starts it registers itself with the Service Manager. This happens behind the scenes, and on registeration, each process gets what is called a Context object – a reference to the Service manager. Now lets say app A needs to communicate with service B, and the two are running in two separate processes. To do this, app A asks the Context for the Service B by passing the well known name of the service. The Context returns a reference to the service to A, on which A can call a method – say foo. This method call is intercepted by the Binder driver. The driver marshals the object and passes a reference of it to the receiver – B. Note that this is passing by reference, not passing by value, in which the object is serialized. On the side of the service B, the Binder maintains a thread-pool (transparent to the service). One of the threads in this pool receives the incoming call, locates the actual object in the service B and make the call. The return value is then similarly passed back to the caller. The following diagram (from http://sites.google.com/site/io/anatomy–physiology-of-an-android) illustrates this:

The entire operation is synchronous, and the objects are reference counted so that when they are no longer in use, they can be deleted. Since there is no serialization / de-serialization, there is no overhead and therefore this model can also be used to communicate with services that run in your own process without any penalty.
Applications
An application in Android is a collection of components. There are four types of components:
- Activity: An Activity is a UI component corresponding to one screen with which a user interacts in order to do something.
- Service: A Service is an application component without a UI used to perform long running operations in the background.
- Broadcast Receiver: A broadcast receiver is a component that responds to system-wide broadcasts (for example screen turned off, battery low, etc.)
- Content Provider: A content provider is a component that stores and retrieves data and makes it available to all applications. There are different types of content providers: audio, video, contacts, etc. and you can create your own custom provider also.
As mentioned earlier, each application runs in its own process. By default, all components used by that app also run in the same process, and on the main thread. However, it is possible to make a component of your app run in a separate process thru the manifest file. Thus, an application in Android may span multiple processes.
Application Startup
Android follows a fairly unique model in that there is no single entry point for an application – there is no main() function. Instead, a component in one application can start another application’s component, thus bringing the application to life. This communication across apps happens thru the IPC mechanism descibed above. So while an Activity is owned by an application, it is possible for another application to start it (if the owning application allows it). An example is clicking on a hyperlink in an app opening up the browser. This applies for not just Activities, but other types of components also. In order for this to happen, there are two steps required:
- In case the application is not already running, the Android system would bring the application to life in a new process forked from Zygote.
- The desired component inside the app would need to be activated.
Note that in case the application is already running, the new component would be by default instantiated in the same process.
As mentioned above, IPC happens thru a Context object. So when a component A inside one app needs to activate another component B in a different app, or give it something new to do, it basically uses the Context object to send a message to the other component. In the case of an Activity, Service or a BroadcastReceiver, this takes the form of a what is called an Intent – a passive data structure that defines the operation to be performed for an Activity and a Service, and for a Broadcast Receiver, a defintion of the announcement being broadcast.
Content Providers however are not activated thru Intents. Instead, activation happens on a request from a ContentResolver, which acts as a mediator between the requesting component and the Content Provider.
The Activity Back-Stack
Consider the following scenario:
- You are on the Home Screen. This is Activity 1
- You click on the Mail app icon, and that activates the main activity in the Mail app which comes to the foreground. This is Activity 2
- You now click on Compose and that activates the Compose activity in the Mail app. This is Activity 3
- You decide to cancel out of composing a new message and press the back button. You come back to Activity 2
Here is what happens in the background:
- When one activity starts another activity, it stops and its state is saved. So when Activity 1 starts activity 2, activity 1 is stopped, its state saved and so on
- The system maintains a stack (called a back-stack) with the latest activity on top and the oldest activity at the bottom.
- When the user presses the back-key, Activity 3 is popped and Activity 2 is started from its saved state

(The Activity Back Stack – source: http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html)
This approach allows Android to seamlessly transition from one app to the other in a consistent way.
Tasks
In the scenario described above, assume that when the user was in Compose mail (Activity 3), he decided to call someone and pressed the Home key. This would not unwind the back-stack, but start a new stack. In order to do this, the collection of Activities in the first stack needs to go into the background. This is achieved thru the notion of a Task – a cohesive unit of Acitivities. When a task moves to the background, all activities in the Task are stopped but the back-stack for the task remains intact, so that when the user returns back to the Task, she can resume where she left off. However, to save memory, the back-stack for a background Task is not retained for a very long period and if the user does not go back to the task, the back-stack is cleared except for the root activity.

(Background and Foreground Tasks – source: http://developer.android.com/guide/topics/fundamentals/tasks-and-back-stack.html)
Activity Life Cycle
The lifecycle of an activity is affected by its association with other activities, its task and its back-stack. There are three states in which an activity can exist:
- Resumed / Running: Activity is in the foreground and has user focus. Such an activity is never killed by the system,
- Paused: Activity is visible, but another activity is in the foreground and has user focus. This could happen if the other activity is at the top, but is translucent, or because it does not cover the entire screen. In the paused state, the Activity object is retained in memory, maintains all state and member info and remains attached to the Window manager. However it can be killed in extremely low memory conditions.
- Stopped: The Activity is not visible to the user.While the Activity object is retained in memory, maintains all state and member info, it does not remain attached to the Window manager. The activity can be killed by the system to reclaim memory if required.

(Activity Lifecycle – source: http://developer.android.com/guide/topics/fundamentals/activities.html#Lifecycle)
Saving Activity State
Note that it is entirely possible that the system, in order to reclaim memory, may destroy an Activity, or even the process in which the Activity was running. However, when the user comes back to the activity (thru the back-stack), you still want to resume at the point the user left. To do this, an activity must save its state. This happens thru the Activity.onSaveInstanceState() method.

(Saving and restoring Activity State – source: http://developer.android.com/guide/topics/fundamentals/activities.html#SavingActivityState)
Process Lifecycle
The Android system may need to kill a process in order to reclaim memory. To ensure that this creates minimal impact on the user expereince, Android ranks processes in a priority order:
- Foreground Process: A process that is required for what the user is currently doing. Such a process is killed only as a last resort
- Visible Process: This process is not in the foreground but can affect what the user is seeing on the screen. For example it may host a paused Activity. Such a process is not killed unless doing so is required to keep all foreground processes running
- Service Procress: A process that is running a service and is not one of the two types above. For example the service may be playing music or downloading something. The system would keep such a process running unless there is not enough memory to keep Foreground and Visible processes running.
- Background Process: A process that is holding an activity not currently visible to the user (the activity is stopped). Such a process has no impact on the user experience (if the activity lifecycle is correctly implemented and the activity state is being properly saved and restored) The system can kill such a process any time. Typically there are mulitple background processes running and the system maintains a LRU list which is used to kill such processes.
- Empty Process: An empty process does not hold any active component. The only reason such a process is kept alive in the first place is for caching and to improve startup time. The system often kills such processes to maintain the balance between process caches and underlying kernel caches.
Of course it can so happen that a higher priority process is dependent on a lower priority process. In such a case the ranking of the serving process is increased to the same level as that of the dependent process.
A situation where this ranking has a direct impact is this:
- Your app needs to download something big which may take time and the user is likely to move out of the app’s activity to something else.
- If you spawn a worker thread for this task and the user moves out, the process would become a background process.
- However, if you instead spawn a service, the process would be a service process and be less likely to be killed
Main / UI Thread
When an application is launched and a new process created to host it, the application gets a single thread called the main thread or the UI thread since this thread is responsible for servicing the UI. The way this works is mostly the same for almost any UI implementation – be it desktop operating systems or mobile OS. Basically, the UI thread runs an infinite loop which checks a queue to see if there are any pending UI events. In the case of Android, this concept is formalized in the form of a Looper. The looper loops over a MessageQueue which contains the messages to be dispatched. The actual task of managing the queue is done by a Handler which is responsible for handling (adding, removing, dispatching) messages in the message queue.

(Android Threading – source: http://sites.google.com/site/io/inside-the-android-application-framework)
For the main thread, a Looper is setup by the system. However, you can also associate a Looper with your own thread. Note that the Looper can be associated only with one thread and that association cannot be changed. The way Android ensures this is by putting the Looper on the thread local storage of the thread and not exposing a constructor for the Looper. You get a Looper by calling the static prepare method which first checks the TLS to see if there is already a Looper, and if not creates one. You then call the loop method on it to start pumping messages. Unlike a Looper, mulitple handlers can be associated with a message queue.
So quite obviously, one should not run a blocking operation on the UI thread. If you do, messages would stop getting processed from the Message Queue and your app would become unresponsive. This is what leads to the famous Application Not Responding (ANR) dialog. One solution is decsribed here: create a handler, spawn a worker thread, post results back from the worker thread to the handler, update the UI. But this is a bit cumbersome to do. A simpler solution is to use a AysncTask which takes care of the underlying plumbing.
Service
As mentioned earlier, a Service is a type of component that is used to carry out a background operation, and does not provide a UI. An app component can start a service, and even if the user switches out of the application, the service would keep running. Typical use cases include playing music and downloading a file. Android ensures that a process running a Service is not killed as far as possible (see Process lifecycle above).
Note that a service by default runs on the main UI thread in the same process. This means that if it carries out a blocking operation, your UI would become unresponsive since the thread would not be available to run the Activity. To circumvent this, you should launch a worker thread inside the service.
Recent Comments