Processes and Threads

Quickview

  • Every service runs in its own lightweight process and all components of the application run in that process, by default
  • Any slow, blocking operations in a service should be done in a new thread, to avoid slowing down other operations

In this document

  1. Processes
    1. Process lifecycle
  2. Threads
    1. Worker threads
    2. Thread-safe methods
  3. Interprocess Communication

When an application component starts and the application does not have any other components running, the Mindroid system starts a new lightweight process for the application with a single thread of execution. By default, all components of the same application run in the same lightweight process and thread (called the "main" thread). If an application component starts and there already exists a lightweight process for that application (because another component from the application exists), then the component is started within that lightweight process and uses the same thread of execution. However you can create additional threads for any lightweight process.

This document discusses how processes and threads work in a Mindroid application.

Lightweight processes

By default, all components of the same application run in the same lightweight process and most applications should not change this. However, if you find that you need to control which process a certain component belongs to, you can do so in the manifest file.

The manifest entry for each type of component element—<service>—supports an mindroid:process attribute that can specify a process in which that component should run. You can set this attribute so that each component runs in its own lightweight process or so that some components share a process while others do not. You can also set mindroid:process so that components of different applications run in the same process.

The <application> element also supports an mindroid:process attribute, to set a default value that applies to all components.

Lightweight process lifecycle

A lightweight process stays alive as long as there is at least one service running in that process.

Threads

When an application is launched, the system creates a thread of execution for the application, called "main." This thread is very important because it is in charge of dispatching events to the appropriate service operations, callbacks etc. It is also the thread in which your application interacts with components.

The system does not create a separate thread for each instance of a component. All components that run in the same lightweight process are instantiated in the main thread, and system calls to each component are dispatched from that thread. Consequently, methods that respond to system callbacks (such as ERROR(onLocationChanged()/mindroid.location.LocationListener#onLocationChanged onLocationChanged()) to report location changes or a lifecycle callback method) always run in the main thread of the process.

When your app performs intensive work in response to some action, this single thread model can yield poor performance unless you implement your application properly. Specifically, if everything is happening in the main thread, performing long operations such as network access or file system access will block the whole application. When the thread is blocked, no events can be dispatched.

  1. Do not block the main thread
  2. Do not access the Mindroid framework from outside the main thread

Worker threads

Because of the single thread model described above, it's vital to the responsiveness of your application's that you do not block the main thread. If you have operations to perform that are not instantaneous, you should make sure to do them in separate threads ("background" or "worker" threads).

Using AsyncTask

AsyncTask allows you to perform asynchronous work on your service. It performs the blocking operations in a worker thread and then publishes the results on the main thread, without requiring you to handle threads and/or handlers yourself.

To use it, you must subclass AsyncTask and implement the doInBackground() callback method, which runs in a pool of background threads. To update your service state, you should implement onPostExecute(), which delivers the result from doInBackground() and runs in the main thread, so you can safely update your state. You can then run the task by calling execute() from the main thread.

For example, you can implement the previous example using AsyncTask this way:

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(Object params) {
        String[] urls = (String[]) params;
        return loadImageFromNetwork(urls[0]);
    }
    
    /** The system calls this to perform work in the main thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Object result) {
        File file = (File) result;
        Log.i(LOG_TAG, "Download of file " + file.getName() + " done");
    }
}

Now the service is safe and the code is simpler, because it separates the work into the part that should be done on a worker thread and the part that should be done on the main thread.

You should read the AsyncTask reference for a full understanding on how to use this class, but here is a quick overview of how it works:

Thread-safe methods

In some situations, the methods you implement might be called from more than one thread, and therefore must be written to be thread-safe.

Interprocess Communication

Mindroid offers a mechanism for interprocess communication (IPC) using remote procedure calls (RPCs), in which a method is called by a service or other application component, but executed remotely (in another lightweight process), with any result returned back to the caller. This entails decomposing a method call and its data to a low level, transmitting it from the local lightweight process to the remote lightweight process, then reassembling and reenacting the call there. Return values are then transmitted in the opposite direction.

To perform IPC, your application must bind to a service, using bindService(). For more information, see the Services developer guide.