A Service
is an application component that can performlong-running operations in the background and does not provide a user interface. Anotherapplication component can start a service and it will continue to run in the background even if theuser switches to another application.
A service can essentially take two forms:
-
Started
-
A service is "started" when an application component (such as an activity) starts it bycalling
startService()
. Once started, a service can run in the background indefinitely, even if the component that started it is destroyed. Usually,a started service performs a single operation anddoes not return a result to the caller
Bound
bindService()
. A bound service offers a client-serverinterface that allows components to interact with the service, send requests, get results, and evendo so across processes with interprocess communication (IPC).
A bound service runs only as long asanother application component is bound to it.
Multiple components can bind to the service at once,but when all of them unbind,the service is destroyed
Although this documentation generally discusses these two types of services separately, yourservice can work both ways—it can be started (to run indefinitely) and also allow binding.It's simply a matter of whether you implement a couple callback methods: onStartCommand()
to allow components to start it andonBind()
to allow binding.
Caution: A service runs in the main thread of its hosting process—the service does not create its own threadand does not run in a separate process (unless you specify otherwise). This means that, if your service is going to do any CPU intensive work or blocking operations (such as MP3playback or networking),you should create a new thread within the service to do that work. By usinga separate thread, you will reduce the riskof Application Not Responding (ANR) errors and theapplication's main thread can remain dedicated to user interaction with your activities.
public class LocalService extends Service { private NotificationManager mNM; // Unique Identification Number for the Notification. // We use it on Notification start, and to cancel it. private int NOTIFICATION = R.string.local_service_started; /** * Class for clients to access. Because we know this service always * runs in the same process as its clients, we don't need to deal with * IPC. */ public class LocalBinder extends Binder { LocalService getService() { return LocalService.this; } } @Override public void onCreate() { mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); // Display a notification about us starting. We put an icon in the status bar. showNotification(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("LocalService", "Received start id " + startId + ": " + intent); // We want this service to continue running until it is explicitly // stopped, so return sticky. return START_STICKY; } @Override public void onDestroy() { // Cancel the persistent notification. mNM.cancel(NOTIFICATION); // Tell the user we stopped. Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show(); } @Override public IBinder onBind(Intent intent) { return mBinder; } // This is the object that receives interactions from clients. See // RemoteService for a more complete example. private final IBinder mBinder = new LocalBinder(); /** * Show a notification while this service is running. */ private void showNotification() { // In this sample, we'll use the same text for the ticker and the expanded notification CharSequence text = getText(R.string.local_service_started); // Set the icon, scrolling text and timestamp Notification notification = new Notification(R.drawable.stat_sample, text, System.currentTimeMillis()); // The PendingIntent to launch our activity if the user selects this notification PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, LocalServiceActivities.Controller.class), 0); // Set the info for the views that show in the notification panel. notification.setLatestEventInfo(this, getText(R.string.local_service_label), text, contentIntent); // Send the notification. mNM.notify(NOTIFICATION, notification); } }
private LocalService mBoundService; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { // This is called when the connection with the service has been // established, giving us the service object we can use to // interact with the service. Because we have bound to a explicit // service that we know is running in our own process, we can // cast its IBinder to a concrete class and directly access it. mBoundService = ((LocalService.LocalBinder)service).getService(); // Tell the user about this for our demo. Toast.makeText(Binding.this, R.string.local_service_connected, Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. // Because it is running in our same process, we should never // see this happen. mBoundService = null; Toast.makeText(Binding.this, R.string.local_service_disconnected, Toast.LENGTH_SHORT).show(); } }; void doBindService() { // Establish a connection with the service. We use an explicit // class name because we want a specific service implementation that // we know will be running in our own process (and thus won't be // supporting component replacement by other applications). bindService(new Intent(Binding.this, LocalService.class), mConnection, Context.BIND_AUTO_CREATE); mIsBound = true; } void doUnbindService() { if (mIsBound) { // Detach our existing connection. unbindService(mConnection); mIsBound = false; } } @Override protected void onDestroy() { super.onDestroy(); doUnbindService(); }
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
public int onStartCommand(Intent intent, int flags, int startId)
Called by the system every time a client explicitly starts the service by callingstartService(Intent)
, providing the arguments it supplied and a unique integer token representing the start request.Do not call this method directly.
For backwards compatibility, the default implementation calls onStart(Intent, int)
and returns eitherSTART_STICKY
orSTART_STICKY_COMPATIBILITY
.
Traditionally, there are two classes you can extend to create a started service:
- This is the base class for all services. When you extend this class, it's important thatyou create a new thread in which to do all the service's work, because the service uses yourapplication's main thread,by default, which could slow the performance of any activity yourapplication is running.
-
This is a subclass of
Service
that uses a worker thread to handle allstart requests,one at a time. This is the best option if you don't require that your service handle multiple requests simultaneously. All you need to do is implementonHandleIntent()
, which receives the intent for eachstart request so you can do the background work.
Service
IntentService
Extending the IntentService class
Because most started services don't need to handle multiple requests simultaneously(which can actually be a dangerous multi-threading scenario), it's probably best if youimplement your service using theIntentService
class
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* constructor with a name for the worker thread.
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
The IntentService
does the following:
- Creates a default worker thread that executes all intents delivered to
onStartCommand()
separate from your application's main thread. - Creates a work queue that passes one intent at a time to your
onHandleIntent()
implementation, so younever have to worry about multi-threading. - Stops the service after all start requests have been handled, so you never have to call
stopSelf()
. - Provides default implementation of
onBind()
that returns null. - Provides a default implementation of
onStartCommand()
that sends the intent to the work queue and then to youronHandleIntent()
implementation.
All this adds up to the fact that all you need to do is implement onHandleIntent()
to do the work provided by theclient. (Though, you also need to provide a small constructor for the service.)
Extending the Service class
As you saw in the previous section, using IntentService
makes your implementation of a started service very simple. If, however, you require your service to perform multi-threading (instead of processing start requests through a work queue), then youcan extend theService
class to handle each intent.
public class HelloService extends Service { private Looper mServiceLooper; private ServiceHandler mServiceHandler; // Handler that receives messages from the thread private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } // Stop the service using the startId, so that we don't stop // the service in the middle of handling another job stopSelf(msg.arg1); } } @Override public void onCreate() { // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. We also make it // background priority so CPU-intensive work will not disrupt our UI. HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); // Get the HandlerThread's Looper and use it for our Handler mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); // For each start request, send a message to start a job and deliver the // start ID so we know which request we're stopping when we finish the job Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; mServiceHandler.sendMessage(msg); // If we get killed, after returning from here, restart return START_STICKY; } @Override public IBinder onBind(Intent intent) { // We don't provide binding, so return null return null; } @Override public void onDestroy() { Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); } }
However, because you handle each call to onStartCommand()
yourself, you can perform multiple requests simultaneously.
if you want the service to send a result back, thenthe client that starts the service can create aPendingIntent
for a broadcast(withgetBroadcast()
) and deliver it to the servicein theIntent
that starts the service.The service can then use thebroadcast to deliver a result.
the service must stop itself by calling stopSelf()
or anothercomponent can stop it by callingstopService().
Running a Service in the Foreground
A foreground service is a service that's considered to be something theuser is actively aware of and thus not a candidate for the system to kill when low on memory.A foreground service must provide a notification for the status bar, which is placed under the"Ongoing" heading, which means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
For example, a music player that plays music from a service should be set to run in theforeground, because the user is explicitly awareof its operation. The notification in the status bar might indicate the current song and allowthe user to launch an activity to interact with the music player.
To request that your service run in the foreground, call startForeground()
. This method takes two parameters: an integerthat uniquely identifies the notification and the Notification
for the status bar. For example:
Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), System.currentTimeMillis()); Intent notificationIntent = new Intent(this, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(this, getText(R.string.notification_title), getText(R.string.notification_message), pendingIntent); startForeground(ONGOING_NOTIFICATION, notification);
To remove the service from the foreground, call stopForeground()
. This method takes a boolean, indicatingwhether to remove the status bar notification as well. This method does not stop theservice. However, if you stop the service while it's still running in the foreground, then thenotification is also removed
startService also will a additional method,onStart,is implements of onStartCommand.but bind service no it.