Affiliate links on Android Authority may earn us a commission. Learn more.
Android Concurrency: Performing Background Processing with Services
Your typical Android mobile app is a skilled multi-tasker, capable of performing complex and long-running tasks in the background (such as handling network requests or transferring data) while continuing to respond to user input.
When you’re developing your own Android apps, bear in mind that no matter how complex, lengthy or intensive these “background” tasks may be, when the user taps or swipes onscreen they’ll still expect your user interface to respond.
It may look effortless from the user’s perspective, but creating an Android app that’s capable of multitasking isn’t straightforward, as Android is single-threaded by default and will execute all tasks on this single thread, one task at a time.
While your app is busy performing a long-running task on its single thread, it’ll be unable to process anything else – including user input. Your UI will be completely unresponsive the entire time the UI thread is blocked, and the user may even encounter Android’s Application Not Responding (ANR) error if the thread remains blocked for long enough.
Since an app that locks up every time it encounters a long-running task isn’t exactly a great user experience, it’s crucial that you identify every task that has the potential to block the main thread, and move these tasks onto threads of their own.
In this article I’m going to show you how to create these crucial additional threads, using Android services. A service is a component that’s designed specifically to handle your app’s long-running operations in the background, typically on a separate thread. Once you have multiple threads at your disposal, you’re free to perform whatever long-running, complex or CPU-intensive tasks you want, with zero risk of blocking that all-important main thread.
Although this article focuses on services, it’s important to note that services aren’t a one-size-fits-all solution that’s guaranteed to work for every single Android app. For those situations where services aren’t quite right, Android provides several other concurrency solutions, which I’ll be touching on towards the end of this article.
Understanding threading on Android
We’ve already mentioned Android’s single-threaded model and the implications this has for your application, but since the way Android handles threading underpins everything we’re going to discuss, it’s worth exploring this topic in a bit more detail.
Every time a new Android application component is launched, the Android system spawns a Linux process with a single thread of execution, known as the “main” or “UI” thread.
This is the most important thread in your entire application, as it’s the thread that’s responsible for handling all user interaction, dispatching events to the appropriate UI widgets, and modifying the user interface. It’s also the only thread where you can interact with components from the Android UI toolkit (components from the android.widget and android.view packages), which means that you cannot post the results of a background thread to your UI directly. The UI thread is the only thread that can update your user interface.
Since the UI thread is responsible for processing user interaction, this is the reason why your app’s UI is completely unable to respond to user interaction while the main UI thread is blocked.
Creating a started Service
There’s two types of services you can use in your Android apps: started services and bound services.
A started service is launched by other application components, such as an Activity or Broadcast Receiver, and is typically used to perform a single operation that doesn’t return a result to the starting component. A bound service acts as the server in a client-server interface. Other application components can bind to a bound service, at which point they’ll be able to interact with, and exchange data with this service.
Since they’re typically the most straightforward to implement, let’s kick things off by looking at started services.
To help you see exactly how you’d implement started services in your own Android apps, I’m going to walk you through the process of creating and managing a started service, by building an app that features a fully-functioning started service.
Create a new Android project, and let’s start by building our app’s user interface, which will consist of two buttons: the user starts the service by tapping one button, and stops the service by tapping the other.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context=".MainActivity" >
<Button
android:id="@+id/start"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="startService"
android:text="Start Service"
android:layout_weight="1"/>
<Button
android:id="@+id/stop"
android:layout_marginTop="20dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="stopService"
android:text="Stop Service"
android:layout_weight="1"/>
</LinearLayout>
This service is going to be launched by our MainActivity component, so open your MainActivity.java file. You launch a service by calling the startService() method and passing it an Intent:
public void startService(View view) {
startService(new Intent(this, MyService.class));
}
When you start a service using startService(), that service’s lifecycle is independent from the lifecycle of the Activity, so the service will continue to run in the background even if the user switches to another application, or the component that started the service gets destroyed. The system will only stop a service if it needs to recover system memory.
To ensure your app doesn’t take up system resources unnecessarily, you should stop your service as soon as it’s no longer needed. A service can stop itself by calling stopSelf(), or another component can stop the Service by calling stopService(), which is what we’re doing here:
public void stopService(View view) {
stopService(new Intent(this, MyService.class));
}
}
Once the system has received a stopSelf() or stopSerivce(), it’ll destroy the service as soon as possible.
Now it’s time to create our MyService class, so create a new MyService.java file and add the following import statements:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.HandlerThread;
The next step, is to create a subclass of Service:
public class MyService extends Service {
It’s important to note that a service doesn’t create a new thread by default. Since services are almost always discussed in the context of performing work on separate threads, it’s easy to overlook the fact that a service runs on the main thread unless you specify otherwise. Creating a service is only the first step – you’ll also need to create a thread where this service can run.
Here, I’m keeping things simple and using a HandlerThread to create a new thread.
@Override
public void onCreate() {
HandlerThread thread = new HandlerThread("Thread Name");
//Start the thread//
thread.start();
}
Start the service by implementing the onStartCommand() method, which will be launched by startService():
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
The onStartCommand() method must return an integer that describes how the system should handle restarting the service in the event that it gets killed. I’m using START_NOT_STICKY to instruct the system not to recreate the service unless there’s pending intents that it needs to deliver.
Alternatively, you can set onStartCommand() to return:
- START_STICKY. The system should recreate the service and deliver any pending intents.
- START_REDELIVER_INTENT. The system should recreate the service, then re-deliver the last intent it delivered to this service. When onStartCommand() returns START_REDELIVER_INTENT, the system will only restart the service if it hasn’t finished processing all the intents that were sent to it.
Since we’ve implemented onCreate(), the next step is invoking the onDestroy()method. This is where you’d clean up any resources that are no longer required:
@Override
public void onDestroy() {
}
Although we’re creating a started service and not a bound service, you still need to declare the onBind() method. However, since this is a started service, onBind() can return null:
@Override
public IBinder onBind(Intent intent) {
return null;
}
As I’ve already mentioned, you can’t update UI components directly from any thread other than the main UI thread. If you do need to update the main UI thread with the results of this service, then one potential solution is to use a Handler object.
Declaring your service in the Manifest
You need to declare all of your app’s services in your project’s Manifest, so open the Manifest file and add a <service> element as a child of your <application> element.
There’s a list of attributes you can use to control your service’s behaviour, but as a bare minimum you should include the following:
- android:name. This is the service’s name, which should be a fully qualified class name, such as “com.example.myapplication.myService.” When naming your service, you can replace the package name with a period, for example: android:name=”.MyService”
- android:description. Users can see what services are running on their device, and may choose to stop a service if they’re not sure what this service is doing. To make sure the user doesn’t shut down your service by accident, you should provide a description that explains exactly what work this service is responsible for.
Let’s declare the service we just created:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jessicathornsby.basicservice">
<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>
<service
android:description="@string/servicedesc"
android:name=".MyService">
</service>
</application>
</manifest>
While this is all you need to get your service up and running, there’s a list of additional attributes that can give you more control over your service’s behaviour, including:
- android:exported=[“true” | “false”] Controls whether other applications can interact with your service. If you set android:exported to ‘false,’ then only components that belong to your application, or components from applications that have the same user ID, will be able to interact with this service. You can also use the android:permission attribute to prevent external components from accessing your service.
- android:icon=”drawable.” This is an icon that represents your service, plus all of its intent filters. If you don’t include this attribute in your <service> declaration, then the system will use your application’s icon instead.
- android:label=”string resource.” This is a short text label that’s displayed to your users. If you don’t include this attribute in your Manifest, then the system will use the value of your application’s <label> attribute instead.
- android:permission=”string resource.” This specifies the permission a component must have in order to launch this service or bind to it.
- android:process=”:myprocess.” By default, all of your application’s components will run in the same process. This setup will work for most apps, but if you do need to run your service on its own process, then you can create one by including android:process and specifying your new process’ name.
You can download this project from GitHub.
Creating a bound service
You can also create bound services, which is a service that allows application components (also known as a ‘client’) to bind to it. Once a component is bound to a service, it can interact with that service.
To create a bound service, you need to define an IBinder interface between the service and the client. This interface specifies how the client can communicate with the service.
There’s several ways that you can define an IBinder interface, but if your application is the only component that’s going to use this service, then it’s recommended that you implement this interface by extending the Binder class and using onBind() to return your interface.
import android.os.Binder;
import android.os.IBinder;
...
...
public class MyService extends Service {
private final IBinder myBinder = new LocalBinder();
public class MyBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return myBinder;
}
To receive this IBinder interface, the client must create an instance of ServiceConnection:
ServiceConnection myConnection = new ServiceConnection() {
You’ll then need to override the onServiceConnected() method, which the system will call to deliver the interface.
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
MyBinder binder = (MyBinder) service;
myService = binder.getService();
isBound = true;
}
You’ll also need to override onServiceDisconnected(), which the system calls if the connection to the service is unexpectedly lost, for example if the service crashes or gets killed.
@Override
public void onServiceDisconnected(ComponentName arg0) {
isBound = false;
}
Finally, the client can bind to the service by passing the ServiceConnection to bindService(), for example:
Intent intent = new Intent(this, MyService.class);
bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
Once the client has received the IBinder, it’s ready to start interacting with the service through this interface.
Whenever a bound component has finished interacting with a bound service, you should close the connection by calling unbindService().
A bound service will continue running as long as at least one application component is bound to it. When the last component unbinds from a service, the system will destroy that service. To prevent your app from taking up system resources unnecessarily, you should unbind each component as soon as it’s finished interacting with its service.
The final thing you need to be aware of when working with bound services, is that even though we’ve discussed started services and bound services separately, these two states aren’t mutually exclusive. You can create a started service using onStartCommand, and then bind a component to that service, which gives you a way of creating a bound service that will run indefinitely.
Running a service in the foreground
Sometimes when you create a service, it’ll make sense to run this service in the foreground. Even if the system needs to recover memory, it won’t kill a foreground service, making this a handy way of preventing the system from killing services that your users are actively aware of. For example, if you have a service that’s responsible for playing music, then you may want to move this service into the foreground as chances are your users aren’t going to be too happy if the song they were enjoying comes to a sudden, unexpected stop because the system has killed it.
You can move a service to the foreground, by calling startForeground(). If you do create a foreground service, then you’ll need to provide a notification for that service. This notification should include some useful information about the service and give the user an easy way of accessing the part of your application that’s related to this service. In our music example, you might use the notification to display the name of the artist and song, and tapping the notification could take the user to the Activity where they can pause, stop or skip the current track.
You remove a service from the foreground by calling stopForeground(). Just be aware that this method doesn’t stop the service, so this is something you’ll still need to take care of.
Concurrency alternatives
When you need to perform some work in the background, services aren’t your only option, as Android provides a selection of concurrency solutions, so you can choose the approach that works the best for your particular app.
In this section, I’m going to cover two alternative ways of moving work off the UI thread: IntentService and AsyncTask.
IntentService
An IntentService is a subclass of service that comes with its own worker thread, so you can move tasks off the main thread without having to mess around creating threads manually.
An IntentService also comes with an implementation of onStartCommand and a default implementation of onBind() that returns null, plus it automatically invokes the callbacks of a regular service component, and stops itself automatically once all requests have been handled.
All of this means that IntentService does a lot of the hard work for you, however this convenience does come at a cost, as an IntentService can only handle one request at a time. If you send a request to an IntentService while it’s already processing a task, then this request will have to be patient and wait until the IntentService has finished processing the task at hand.
Assuming that this isn’t a deal breaker, implementing an IntentService is fairly straightforward:
//Extend IntentService//
public class MyIntentService extends IntentService {
// Call the super IntentService(String) constructor with a name
// for the worker thread//
public MyIntentService() {
super("MyIntentService");
}
// Define a method that overrides onHandleIntent, which is a hook method that’ll be called every time the client calls startService//
@Override
protected void onHandleIntent(Intent intent) {
// Perform the task(s) you want to run on this thread//
...
...
...
}
}
Once again, you’ll need to start this service from the relevant application component, by calling startService(). Once the component calls startService(), the IntentService will perform the work you defined in your onHandleIntent() method.
If you do need to update your app’s user interface with the results of your work request, then you have several options, but the recommended approach is to:
- Define a BroadcastReceiver subclass within the application component that sent the work request.
- Implement the onReceive() method, which will receive the incoming intent.
- Use IntentFilter to register this receiver with the filter(s) it needs to catch the result intent.
- Once the IntentService’s work is complete, send a broadcast from your IntentService’s onHandleIntent() method.
With this workflow in place, every time the IntentService finishes processing a request, it’ll send the results to the BroadcastReceiver, which will then update your UI accordingly.
The only thing left to do is declare your IntentService in your project’s Manifest. This follows exactly the same process as defining a service, so add a <service> element to your Manifest and include the attributes of your choice.
AsyncTask
AsyncTask is another concurrency solution that you may want to consider. Like IntentService, AsyncTask provides a ready-made worker thread, but it also includes an onPostExecute() method that runs in the UI thread, which makes AsynTask one of the rare concurrency solutions that can update your app’s UI without requiring any additional setup.
The best way to get to grips with AsynTask is to see it in action, so in this section I’m going to show you how to create a demo app that includes an AsyncTask. This app will consist of an EditText where the user can specify the number of seconds they want the AsyncTask to run. They’ll then be able to launch the AsyncTask with the tap of a button.
Mobile users expect to be kept in the loop, so if it isn’t immediately obvious that your app is performing work in the background, then you should make it obvious! In our demo app, tapping the ‘Start AsyncTask’ button will launch an AsyncTask, however the UI doesn’t actually change until the AsyncTask has finished running. If we don’t provide some indication that work is happening in the background, then the user may assume that nothing is happening at all – maybe the app is frozen or broken, or maybe they should just keep tapping away at that button until something does change?
I’m going to update my UI to display a message that explicitly states “Asynctask is running…” as soon as the AsyncTask launches.
Finally, so that you can verify that the AsyncTask isn’t blocking the main thread, I’ll also be creating an EditText that you can interact with while the AsncTask is running in the background.
Let’s start by creating our user interface:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10dp"
android:layout_marginTop="20dp"
android:text="Run Asynctask for:"/>
<EditText
android:id="@+id/enter_seconds"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:inputType="phone"/>
<TextView
android:text=""
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/message" />
<Button
android:text="Run Async task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"/>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:ems="10"
android:id="@+id/enter_text"
android:hint="Type here while Asynctask is running" />
</LinearLayout>
The next step, is creating the AsyncTask. This requires you to:
- Extend the AsyncTask class.
- Implement the doInBackground() callback method. This method runs in its own thread by default, so any work you perform in this method will happen off the main thread.
- Implement the onPreExecute() method, which will run on the UI thread. You should use this method to perform any tasks you need to complete before AsyncTask starts processing your background work.
- Update your UI with the results of your AsynTask’s background operation, by implementing onPostExecute().
Now you have a high-level overview of how to create and manage an AsyncTask, let’s apply all of this to our MainActivity:
package com.jessicathornsby.async;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private Button button;
private EditText enterSeconds;
private TextView message;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
enterSeconds = (EditText) findViewById(R.id.enter_seconds);
button = (Button) findViewById(R.id.button);
message = (TextView) findViewById(R.id.message);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AsyncTaskRunner runner = new AsyncTaskRunner();
String asyncTaskRuntime = enterSeconds.getText().toString();
runner.execute(asyncTaskRuntime);
}
});
}
//Extend AsyncTask//
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
private String results;
// Implement onPreExecute() and display a Toast so you can see exactly
// when this method is called//
@Override
protected void onPreExecute() {
Toast.makeText(MainActivity.this,
"onPreExecute", Toast.LENGTH_LONG).show();
}
// Implement the doInBackground() callback//
@Override
protected String doInBackground(String... params) {
// Update the UI while AsyncTask is performing work in the background//
publishProgress("Asynctask is running..."); //
// Perform your background work. To keep this example as simple as
// possible, I’m just sending the process to sleep//
try {
int time = Integer.parseInt(params[0])*1000;
Thread.sleep(time);
results = "Asynctask ran for " + params[0] + " seconds";
} catch (InterruptedException e) {
e.printStackTrace();
}
// Return the result of your long-running operation//
return results;
}
// Send progress updates to your app’s UI via onProgressUpdate().
// The method is invoked on the UI thread after a call to publishProgress()//
@Override
protected void onProgressUpdate(String... text) {
message.setText(text[0]);
}
// Update your UI by passing the results from doInBackground to the onPostExecute() method, and display a Toast//
@Override
protected void onPostExecute(String result) {
Toast.makeText(MainActivity.this,
"onPostExecute", Toast.LENGTH_LONG).show();
message.setText(result);
}
}
}
Take this app for a spin by installing it on your device or Android Virtual Device (AVD), entering the number of seconds you want the AsyncTask to run, and then giving the ‘Start AsyncTask’ button a tap.
You can download this project from GitHub.
If you do decide to implement AsyncTasks in your own projects, then just be aware that AsyncTask maintains a reference to a Context even after that Context has been destroyed. To prevent the exceptions and general odd behaviour that can arise from attempting to reference a Context that doesn’t exist anymore, make sure sure you call cancel(true) on your AsyncTask in your Activity or Fragment’s onDestroy() method, and then validate that the task hasn’t been canceled in onPostExecute().
Wrapping Up
Do you have any tips for adding concurrency to your Android applications? Leave them in the comments below!