Android
Services
1.1 Service
AService
is a component which runs
in the background, without interacting with the user. Every developer
can create new Services
in his
application. Services
support true
multitasking for Android, as they can run in their own process. If
you use threads in Activities
their are
still connected to the life-cycle of Activities
and the Android system may decide to terminate them at any point in
point.
1.2. Service
The Android platform provides pre-definedServices
,
usually exposed via a specific Manager class. Access to these
services can be gained via the method getSystemService()
.
2. Defining Services
2.1. Declaring own Services
You can declare your ownService
to
perform long running operations without user interaction or to supply
functionality to other applications.
A
Service
needs to be declared in the
AndroidManifest.xml
via a <service
android:name="yourclasss"> </service>
and
the implementing class must extend the Service
class or one of its subclasses.
A
Service
will not automatically run
in its own thread. Without the process attribute, they run the main
thread of their hosting process. Therefore you should run performance
intensive tasks in the background.
2.2. Running a Services in its own process
You can also specify that yourService
runs in a separate process via the
android:process=":process_description"
attribute.
This way the service gets its own process and has its own memory. Any long running operation in the
Service
,
e.g. a garbage collection, will not affect the user interface of your
Activity
The colon prefix before the name tells Android that the
Service
is private to its declaring application. If the colon is not used the
Service
would be a global process and
can be used by other components.
<service android:name="WordService" android:process=":my_process" android:icon="@drawable/icon" android:label="@string/service_name" > </service>Running a service in its own process will not block the application in case the service performs long running operations in its main thread. But as the services runs in its own process you need to use some interprocess communication (IPC) to communicate to your service from other parts.
2.3. Intent Services
While the base class for creating aService
is the Service
class you can also
implement IntentService
.
The
IntentService
is used to perform
a certain task in the background. Once done, the instance of
IntentService
terminate itself
automatically. Examples for its usage would be to download a certain
resources from the Internet.
The
IntentService
class offers the
onHandleIntent()
method which will be
asynchronously called by the Android system.
2.4. Starting Services
AnActivity
can start a Service
via the startService()
method and stop
the service via the stopService()
method. If the Activity
want to interact
with the Activity
it can use the
bindService()
method of the service.
This requires an ServiceConnection
object
which allows to connect to the Service
and which return a IBinder object. This IBinder object can be used by
the activity to communicate with the Service
.
Once a
Service
is started the
onCreate()
method is called. Afterwards
the onStartCommand()
method is called
with the Intent data provided by the activity.
startService()
also allows you to
provide a flag which determines the lifecycle behavior of the
services. Service.START_STICKY
is used
for services which are explicit started or stopped. Services started
with Service.START_NOT_STICKY
will end
automatically after the onStartCommand()
method is done. A Service
is started
within the main thread of the application therefore all long running
tasks should be performed in the background.
3. Communicating with Services
There are several way for anActivity
to communicate with an Service and vice versa.
3.1. Binding to local service
If theService
is started in the same
process as the Activity
, the Activity
can directly bind to the service a call of the bindService()
method. This method gets as a parameter a SernviceConnection
.
The onServiceConnected()
method is
called on this object once the Service
is available. The Service
return on its
onBind()
method an object of type
IBinder
which can be used to call to the
service.
See Local Service Example for an example.
3.2. Binding to a service in a different process
To bind to aService
which runs in a
different process you need to use Inter Process Communication (IPC)
as the data needs to be send between different processes. For this
you need to create a AIDL file which looks similiar to an Java
Interface but ends with the .aidl file extension and is only allowed
to extend other AIDL files.
If the Android Development Tools find such a file in your source folder, it will create the necessary stub classes for IPC communication for your. Still using this approach is relatively advanced and will not be covered in this tutorial.
3.3. Handler and Messenger
If the service should be communicating back to theActivity
it can receive an object of type Messenger
via the Intent
data it receives from the
Activity
. If the Messenger
in bound to a Handler
in the Activity
the Service
can send objects of type
Message
to the Activity
.
A
Messenger
is parcable, which means
it can be passed to another process and you can use this object to
send Messages
to the Handler
in the Activity
.
Messenger
provides also the method
getBinder()
which allows to pass a
Messenger
to the Activity
.
The Activity
can therefore send Messages
to the Service
.
Another example would be the usage of . In this case
Activities
can send messages to the Services
and
receive messages as well if they register for them.
4. Broadcast receiver
4.1. Definition
A broadcast receiver is a class which extendsBroadcastReceiver
and which is registered as a receiver in an Android Application via
the AndroidManifest.xml
file(or via
code).
Alternatively to the this static registration, you can also register a
BroadcastReceiver
dynamically
via the Context.registerReceiver()
method.
This class will be able to receive intents. Intents can be generated via the
Context.sendBroadcast()
method.
The class
BroadcastReceiver
defines
the onReceive()
method. Only during this
method your BroadcastReceiver
object
will be valid, afterwards the Android system can recycle the
BroadcastReceiver
. Therefore you cannot
perform any asynchronous operation in the onReceive()
method.
4.2. Sending Broadcast Intents
ThesendBroadcast()
method allows to
send Broadcast Intents
. You cannot
trigger system Broadcasts, the Android system will prevent this.
But you can define intent-filters for your own actions and trigger them via the
sendBroadcast()
method.
The following AndroidManifest.xml file show a
BroadcastReceiver
which is registered to a custom action.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.receiver.own" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name="MyReceiver" > <intent-filter> <action android:name="de.vogella.android.mybroadcast" /> </intent-filter> </receiver> </application> </manifest>You can trigger this event via the
sendBroadcast()
method.
Intent intent = new Intent();
intent.setAction("de.vogella.android.mybroadcast");
sendBroadcast(intent);
4.3. Sticky Broadcast Intents
A normal broadcastIntent
is not
available anymore after is was send and processed by the system. If
you use the sendBroadcast(Intent)
method, the Intent
is sticky, meaning
the Intent
you are sending stays around
after the broadcast is complete.
You can can retrieve that data through the return value of
registerReceiver(BroadcastReceiver,
IntentFilter)
. This works also for a null BroadcastReceiver
.
In all other ways, this behaves the same as
sendBroadcast(Intent)
.
The Android system uses sticky broadcast for certain system information. For example the battery status is send as sticky
Intent
and can get received at any time. The following example demonstrates
that.
// Register for the battery changed event IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); / Intent is sticky so using null as receiver works fine // return value contains the status Intent batteryStatus = this.registerReceiver(null, filter); // Are we charging / charged? int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL; boolean isFull = status == BatteryManager.BATTERY_STATUS_FULL; // How are we charging? int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB; boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
5. Scheduling of Services
5.1. Automatically starting Services via Receivers
To startServices
automatically after
the Android system starts you can register a BroadcastReceiver
to the Android android.intent.action.BOOT_COMPLETED
system event. This requires the
android.permission.RECEIVE_BOOT_COMPLETED
permission.
The following AndroidManifest.xml registers a receiver for the
BOOT_COMPLETED
event.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.ownservice.local" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:icon="@drawable/icon" android:label="@string/app_name" > <activity android:name=".ServiceConsumerActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name="MyScheduleReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <receiver android:name="MyStartServiceReceiver" > </receiver> </application> </manifest>In the
onReceive()
method the
corresponding BroadcastReceiver
would
then start the service.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, WordService.class);
context.startService(service);
}
}
If you application is installed on the SD card, then it is not
available after the android.intent.action.BOOT_COMPLETED
event. Register yourself in this case for the
android.intent.action.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
event.
Also note that as of Android 3.0 the user needs to have started the application at least once before your application can receive
android.intent.action.BOOT_COMPLETED
events.
5.2. Scheduling of Services via AlarmManager
As withActivities
the Android system
may terminate the process of a service at any time to save resources.
For this reason you cannot simple use a TimerTask
in the service to ensure that it is executed on a regular basis.
The following would be incorrect, as Android may terminate your
Service
.
public void onCreate() { super.onCreate(); pollForUpdates(); } // WRONG, don't do this private void pollForUpdates() { timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { if (list.size() >= 6) { list.remove(0); } list.add(fixedList[index++]); if (index >= fixedList.length) { index = 0; } } }, 0, UPDATE_INTERVAL); Log.i(getClass().getSimpleName(), "Timer started."); }For correct scheduling of the
Service
use the AlarmManager
class.
6. Pending Intent
A PendingIntent is a token that you give to another application (e.g. Notification Manager, Alarm Manager or other 3rd party applications), which allows this other application to use the permissions of your application to execute a predefined piece of code.To perform a broadcast via a pending intent so get a PendingIntent via
PendingIntent.getBroadcast()
. To
perform an activity via an pending intent you receive the activity
via PendingIntent.getActivity()
.
7 Broadcast Receiver
We will define a broadcast receiver which listens to telephone state changes. If the phone receives a phone call then our receiver will be notified and log a message.Create a new project
de.vogella.android.receiver.phone
.
We do not need an Activity
. Create the
following AndroidManifest.xml
.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.receiver.phone" android:versionCode="1" android:versionName="1.0" > <application android:icon="@drawable/icon" android:label="@string/app_name" > <receiver android:name="MyPhoneReceiver" > <intent-filter> <action android:name="android.intent.action.PHONE_STATE" > </action> </intent-filter> </receiver> </application> <uses-sdk android:minSdkVersion="9" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" > </uses-permission> </manifest>Create the
MyPhoneReceiver
class.
package de.vogella.android.receiver.phone; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.telephony.TelephonyManager; import android.util.Log; public class MyPhoneReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Bundle extras = intent.getExtras(); if (extras != null) { String state = extras.getString(TelephonyManager.EXTRA_STATE); Log.w("DEBUG", state); if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) { String phoneNumber = extras .getString(TelephonyManager.EXTRA_INCOMING_NUMBER); Log.w("DEBUG", phoneNumber); } } } }If you install your application and receive a call, e.g simulated by the DDMS perspective in Eclipse, then your receiver will be called and lot a message to the console.
8. Tutorial: System Services and BroadcastReceiver
In this chapter we will use the AlertManager and VibratorManager. The VibratorManager will be called by the broadcast receiver which will be called by the AlertManager.Create a new project "de.vogella.android.alarm" with the activity "AlarmActivity". Create the following layout.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <EditText android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Number of seconds" android:inputType="numberDecimal" > </EditText> <Button android:id="@+id/ok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="startAlert" android:text="Start Counter" > </Button> </LinearLayout>Create the following broadcast receiver class. This class will get the Vibrator service.
package de.vogella.android.alarm; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Vibrator; import android.widget.Toast; public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "Don't panik but your time is up!!!!.", Toast.LENGTH_LONG).show(); // Vibrate the mobile phone Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(2000); } }Maintain this class as broadcast receiver in "AndroidManifest.xml" and allow the vibrate authorization.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.alarm" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="9" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".AlarmActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name="MyBroadcastReceiver"></receiver> </application> <uses-permission android:name="android.permission.VIBRATE"></uses-permission> </manifest>Change the code of your Activity "AlarmActivity" to the following. This activity will create an Intent for the Broadcast receiver and get the AlarmManager service.
package de.vogella.android.alarm; import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.Toast; public class AlarmActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void startAlert(View view) { EditText text = (EditText) findViewById(R.id.time); int i = Integer.parseInt(text.getText().toString()); Intent intent = new Intent(this, MyBroadcastReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( this.getApplicationContext(), 234324243, intent, 0); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (i * 1000), pendingIntent); Toast.makeText(this, "Alarm set in " + i + " seconds", Toast.LENGTH_LONG).show(); } }Run your application on the device. Set your time and start the alarm. After the defined number of seconds a Toast should be displayed. Keep in mind that the vibrator alarm does not work on the Android emulator.
9. Tutorial: Using IntentService to download a file
The following will demonstrate how to use theIntentService
class to download a file from the Internet. Once done the
IntentService
will use an instance of
the Messenger
class to inform the
Activity
which started the service about
the location of the downloaded file.
Create a new project called "de.vogella.android.intentservice.download" with a
Activity
called "MainActivity".
Create a service "DownloadService" by creating the following class and the entry in "AndroidManifest.xml". Also add the permission to write to external storage and to access the Internet to the file.
package de.vogella.android.intentservice.download; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import android.app.Activity; import android.app.IntentService; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.Message; import android.os.Messenger; import android.util.Log; public class DownloadService extends IntentService { private int result = Activity.RESULT_CANCELED; public DownloadService() { super("DownloadService"); } // Will be called asynchronously be Android @Override protected void onHandleIntent(Intent intent) { Uri data = intent.getData(); String urlPath = intent.getStringExtra("urlpath"); String fileName = data.getLastPathSegment(); File output = new File(Environment.getExternalStorageDirectory(), fileName); if (output.exists()) { output.delete(); } InputStream stream = null; FileOutputStream fos = null; try { URL url = new URL(urlPath); stream = url.openConnection().getInputStream(); InputStreamReader reader = new InputStreamReader(stream); fos = new FileOutputStream(output.getPath()); int next = -1; while ((next = reader.read()) != -1) { fos.write(next); } // Sucessful finished result = Activity.RESULT_OK; } catch (Exception e) { e.printStackTrace(); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } Bundle extras = intent.getExtras(); if (extras != null) { Messenger messenger = (Messenger) extras.get("MESSENGER"); Message msg = Message.obtain(); msg.arg1 = result; msg.obj = output.getAbsolutePath(); try { messenger.send(msg); } catch (android.os.RemoteException e1) { Log.w(getClass().getName(), "Exception sending message", e1); } } } } <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.intentservice.download" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="DownloadService" > </service> </application> </manifest>Change the "main.xml" layout to the following.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="Button" /> </LinearLayout>Change
MainActivity
to the following.
package de.vogella.android.intentservice.download; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { private Handler handler = new Handler() { public void handleMessage(Message message) { Object path = message.obj; if (message.arg1 == RESULT_OK && path != null) { Toast.makeText(MainActivity.this, "Downloaded" + path.toString(), Toast.LENGTH_LONG) .show(); } else { Toast.makeText(MainActivity.this, "Download failed.", Toast.LENGTH_LONG).show(); } }; }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } public void onClick(View view) { Intent intent = new Intent(this, DownloadService.class); // Create a new Messenger for the communication back Messenger messenger = new Messenger(handler); intent.putExtra("MESSENGER", messenger); intent.setData(Uri.parse("http://www.vogella.de/index.html")); intent.putExtra("urlpath", "http://www.vogella.de/index.html"); startService(intent); } }If you run your example and press the button, the download should be performed by the
Service
and once done
the Activity
should show a Toast with
the file name.
10.Define and consume your own local service
The following chapter will demonstrate how to create and consume a service from an activity. The service will be started at boot and periodically fetch data. The service will used by anactivity
which bind itself to the service. The activity will allow to request
the latest data from the service.
Create a new project called "de.vogella.android.ownservice.local" with a
Activity
called "MainActivity".
Create the
LocalWordService
class.
package de.vogella.android.ownservice.local; import java.util.ArrayList; import java.util.List; import java.util.Random; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; public class LocalWordService extends Service { private final IBinder mBinder = new MyBinder(); private ArrayList<String> list = new ArrayList<String>(); @Override public int onStartCommand(Intent intent, int flags, int startId) { Random random = new Random(); if (random.nextBoolean()) { list.add("Linux"); } if (random.nextBoolean()) { list.add("Android"); } if (random.nextBoolean()) { list.add("iPhone"); } if (random.nextBoolean()) { list.add("Windows7"); } if (list.size() >= 20) { list.remove(0); } return Service.START_NOT_STICKY; } @Override public IBinder onBind(Intent arg0) { return mBinder; } public class MyBinder extends Binder { LocalWordService getService() { return LocalWordService.this; } } public List<String> getWordList() { ArrayList<String> list = new ArrayList<String>(); return list; } }Create the following two classes, which will be registered as
BroadcastReceivers
.
package de.vogella.android.ownservice.local; import java.util.Calendar; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class MyScheduleReceiver extends BroadcastReceiver { // Restart service every 30 seconds private static final long REPEAT_TIME = 1000 * 30; @Override public void onReceive(Context context, Intent intent) { AlarmManager service = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); Intent i = new Intent(context, MyStartServiceReceiver.class); PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); Calendar cal = Calendar.getInstance(); // Start 30 seconds after boot completed cal.add(Calendar.SECOND, 30); // // Fetch every 30 seconds // InexactRepeating allows Android to optimize the energy consumption service.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), REPEAT_TIME, pending); // service.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), // REPEAT_TIME, pending); } } package de.vogella.android.ownservice.local; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class MyStartServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent service = new Intent(context, LocalWordService.class); context.startService(service); } } <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.ownservice.local" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:icon="@drawable/icon" android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".LocalWordService" android:icon="@drawable/icon" android:label="@string/service_name" > </service> <receiver android:name="MyScheduleReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <receiver android:name="MyStartServiceReceiver" > </receiver> </application> </manifest>Change the "main.xml" layout to the following.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="showServiceData" android:text="Button" > </Button> <ListView android:id="@id/android:list" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>Change your
Activity
to the following.
package de.vogella.android.ownservice.local; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Toast; public class MainActivity extends ListActivity { private LocalWordService s; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); wordList = new ArrayList<String>(); adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, wordList); setListAdapter(adapter); doBindService(); } private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder binder) { s = ((LocalWordService.MyBinder) binder).getService(); Toast.makeText(MainActivity.this, "Connected", Toast.LENGTH_SHORT).show(); } public void onServiceDisconnected(ComponentName className) { s = null; } }; private ArrayAdapter<String> adapter; private List<String> wordList; void doBindService() { bindService(new Intent(this, LocalWordService.class), mConnection, Context.BIND_AUTO_CREATE); } public void showServiceData(View view) { if (s != null) { Toast.makeText(this, "Number of elements" + s.getWordList().size(), Toast.LENGTH_SHORT).show(); wordList.clear(); wordList.addAll(s.getWordList()); adapter.notifyDataSetChanged(); } } }
11. Tutorial: Interprocess communication with Messenger
The following chapter will demonstrate how to communicate between anActivity
and an Service
using the Messenger
and Handler
class.
Create a new project called "de.vogella.android.ownservice.messenger" with an
Activity
called "MainActivity".
Creating the following "AndroidManifest.xml".
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="de.vogella.android.ownservice.messenger" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="DownloadServiceMessenger" > </service> </application> </manifest>Create the following class
package de.vogella.android.ownservice.messenger; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import android.app.Activity; import android.app.Service; import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.StrictMode; import android.util.Log; public class DownloadServiceMessenger extends Service { public static final String FILENAME = "fileName"; public static final String URLPATH = "urlPath"; public static final String RESULTPATH = "urlPath"; private int result = Activity.RESULT_CANCELED; // Used to receive messages from the Activity final Messenger inMessenger = new Messenger(new IncomingHandler()); // Use to send message to the Activity private Messenger outMessenger; public DownloadServiceMessenger() { super(); // Don't do this // Network Stuff will run in the main thread StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder() .permitAll().build(); StrictMode.setThreadPolicy(policy); } class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { Log.e("MESSAGE", "Got message"); Bundle data = msg.getData(); String urlPath = data.getString(DownloadServiceMessenger.URLPATH); String fileName = data.getString(DownloadServiceMessenger.FILENAME); String outputPath = download(urlPath, fileName); Message backMsg = Message.obtain(); backMsg.arg1 = result; Bundle bundle = new Bundle(); bundle.putString(RESULTPATH, outputPath); backMsg.setData(bundle); try { outMessenger.send(backMsg); } catch (android.os.RemoteException e1) { Log.w(getClass().getName(), "Exception sending message", e1); } } } private String download(String urlPath, String fileName) { File output = new File(Environment.getExternalStorageDirectory(), fileName); if (output.exists()) { output.delete(); } InputStream stream = null; FileOutputStream fos = null; try { URL url = new URL(urlPath); stream = url.openConnection().getInputStream(); InputStreamReader reader = new InputStreamReader(stream); fos = new FileOutputStream(output.getPath()); int next = -1; while ((next = reader.read()) != -1) { fos.write(next); } // Sucessful finished result = Activity.RESULT_OK; } catch (Exception e) { e.printStackTrace(); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { e.printStackTrace(); } } if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } return output.getAbsolutePath(); } @Override public IBinder onBind(Intent intent) { Bundle extras = intent.getExtras(); // Get messager from the Activity if (extras != null) { outMessenger = (Messenger) extras.get("MESSENGER"); } // Return our messenger to the Activity to get commands return inMessenger.getBinder(); } }Change the "main.xml" layout to the following.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onClick" android:text="Button" /> </LinearLayout>Change
MainActivity
to the following.
package de.vogella.android.ownservice.messenger; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { Messenger messenger = null; private Handler handler = new Handler() { public void handleMessage(Message message) { Bundle data = message.getData(); if (message.arg1 == RESULT_OK && data != null) { String text = data .getString(DownloadServiceMessenger.RESULTPATH); Toast.makeText(MainActivity.this, text, Toast.LENGTH_LONG) .show(); } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } private ServiceConnection conn = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder binder) { messenger = new Messenger(binder); } public void onServiceDisconnected(ComponentName className) { messenger = null; } }; protected void onResume() { super.onResume(); Toast.makeText(this, "OnResume called", Toast.LENGTH_SHORT).show(); Intent intent = null; intent = new Intent(this, DownloadServiceMessenger.class); // Create a new Messenger for the communication back // From the Service to the Activity Messenger messenger = new Messenger(handler); intent.putExtra("MESSENGER", messenger); bindService(intent, conn, Context.BIND_AUTO_CREATE); } @Override protected void onPause() { super.onPause(); unbindService(conn); } public void onClick(View view) { Message msg = Message.obtain(); try { Bundle bundle = new Bundle(); bundle.putString(DownloadServiceMessenger.FILENAME, "index.html"); bundle.putString(DownloadServiceMessenger.URLPATH, "http://www.vogella.de/index.html"); msg.setData(bundle); messenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } }
Services
Service
is an application component that can perform long-running operations
in the background and does not provide a user interface. Another
application component can start a service and it will continue to
run in the background even if the user switches to another
application. Additionally, a component can bind to a service to
interact with it and even perform interprocess communication (IPC).
For example, a service might handle network transactions, play
music, perform file I/O, or interact with a content provider, all
from the background.A service can essentially take two forms:
- Started
-
A service is "started" when an application component
(such as an activity) starts it by calling
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 and does not return a result to the caller. For example, it might download or upload a file over the network. When the operation is done, the service should stop itself. - Bound
-
A service is "bound" when an application component binds
to it by calling
bindService()
. A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another 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.
onStartCommand()
to allow components to start it and onBind()
to allow binding.Regardless of whether your application is started, bound, or both, any application component can use the service (even from a separate application), in the same way that any component can use an activity—by starting it with an
Intent
.
However, you can declare the service as private, in the manifest
file, and block access from other applications. This is discussed
more in the section about Declaring
the service in the manifest.Caution: A service runs in the main thread of its hosting process—the service does not create its own thread and 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 MP3 playback or networking), you should create a new thread within the service to do that work. By using a separate thread, you will reduce the risk of Application Not Responding (ANR) errors and the application's main thread can remain dedicated to user interaction with your activities.
The Basics
Should you use a service or a thread?
A service is simply a component that can run in the background even when the user is not interacting with your application. Thus, you should create a service only if that is what you need.If you need to perform work outside your main thread, but only while the user is interacting with your application, then you should probably instead create a new thread and not a service. For example, if you want to play some music, but only while your activity is running, you might create a thread in
onCreate()
,
start running it in onStart()
,
then stop it in onStop()
.
Also consider using AsyncTask
or HandlerThread
,
instead of the traditional Thread
class. See the Processes
and Threading document for more information about threads.Remember that if you do use a service, it still runs in your application's main thread by default, so you should still create a new thread within the service if it performs intensive or blocking operations.
To create a service, you must create a subclass of
Service
(or one of its existing subclasses). In your implementation, you
need to override some callback methods that handle key aspects of
the service lifecycle and provide a mechanism for components to bind
to the service, if appropriate. The most important callback methods
you should override are:onStartCommand()
-
The system calls this method when another component, such as an
activity, requests that the service be started, by calling
startService()
. Once this method executes, the service is started and can run in the background indefinitely. If you implement this, it is your responsibility to stop the service when its work is done, by callingstopSelf()
orstopService()
. (If you only want to provide binding, you don't need to implement this method.) -
onBind()
-
The system calls this method when another component wants to bind
with the service (such as to perform RPC), by calling
bindService()
. In your implementation of this method, you must provide an interface that clients use to communicate with the service, by returning anIBinder
. You must always implement this method, but if you don't want to allow binding, then you should return null. -
onCreate()
-
The system calls this method when the service is first created, to
perform one-time setup procedures (before it calls either
onStartCommand()
oronBind()
). If the service is already running, this method is not called. -
onDestroy()
- The system calls this method when the service is no longer used and is being destroyed. Your service should implement this to clean up any resources such as threads, registered listeners, receivers, etc. This is the last call the service receives.
startService()
(which results in a call to onStartCommand()
),
then the service remains running until it stops itself with
stopSelf()
or another component stops it by calling stopService()
.If a component calls
bindService()
to create the service (and onStartCommand()
is not called), then the service runs only as long as the
component is bound to it. Once the service is unbound from all
clients, the system destroys it.The Android system will force-stop a service only when memory is low and it must recover system resources for the activity that has user focus. If the service is bound to an activity that has user focus, then it's less likely to be killed, and if the service is declared to run in the foreground (discussed later), then it will almost never be killed. Otherwise, if the service was started and is long-running, then the system will lower its position in the list of background tasks over time and the service will become highly susceptible to killing—if your service is started, then you must design it to gracefully handle restarts by the system. If the system kills your service, it restarts it as soon as resources become available again (though this also depends on the value you return from
onStartCommand()
,
as discussed later). For more information about when the system
might destroy a service, see the Processes
and Threading document.In the following sections, you'll see how you can create each type of service and how to use it from other application components.
Declaring a service in the manifest
Like activities (and other components), you must declare all services in your application's manifest file.To declare your service, add a
<service>
element as a child of the <application>
element. For example:<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>There are other attributes you can include in the
<service>
element to define properties such as permissions required to start
the service and the process in which the service should run. The
android:name
attribute is the only required attribute—it specifies the class
name of the service. Once you publish your application, you should
not change this name, because if you do, you might break some
functionality where explicit intents are used to reference your
service (read the blog post, Things
That Cannot Change).
See the
<service>
element reference for more information about declaring your service
in the manifest.Just like an activity, a service can define intent filters that allow other components to invoke the service using implicit intents. By declaring intent filters, components from any application installed on the user's device can potentially start your service if your service declares an intent filter that matches the intent another application passes to
startService()
.If you plan on using your service only locally (other applications do not use it), then you don't need to (and should not) supply any intent filters. Without any intent filters, you must start the service using an intent that explicitly names the service class. More information about starting a service is discussed below.
Additionally, you can ensure that your service is private to your application only if you include the
android:exported
attribute and set it to "false"
.
This is effective even if your service supplies intent filters.For more information about creating intent filters for your service, see the Intents and Intent Filters document.
Creating a Started Service
Targeting Android 1.6 or lower
If you're building an application for Android 1.6 or lower, you need to implementonStart()
,
instead of onStartCommand()
(in Android 2.0, onStart()
was deprecated in favor of onStartCommand()
).For more information about providing compatibility with versions of Android older than 2.0, see the
onStartCommand()
documentation.A started service is one that another component starts by calling
startService()
,
resulting in a call to the service's onStartCommand()
method.When a service is started, it has a lifecycle that's independent of the component that started it and the service can run in the background indefinitely, even if the component that started it is destroyed. As such, the service should stop itself when its job is done by calling
stopSelf()
,
or another component can stop it by calling stopService()
.An application component such as an activity can start the service by calling
startService()
and passing an Intent
that specifies the service and includes any data for the service to
use. The service receives this Intent
in the onStartCommand()
method.For instance, suppose an activity needs to save some data to an online database. The activity can start a companion service and deliver it the data to save by passing an intent to
startService()
.
The service receives the intent in onStartCommand()
,
connects to the Internet and performs the database transaction. When
the transaction is done, the service stops itself and it is
destroyed.Caution: A services runs in the same process as the application in which it is declared and in the main thread of that application, by default. So, if your service performs intensive or blocking operations while the user interacts with an activity from the same application, the service will slow down activity performance. To avoid impacting application performance, you should start a new thread inside the service.
Traditionally, there are two classes you can extend to create a started service:
Service
- This is the base class for all services. When you extend this class, it's important that you create a new thread in which to do all the service's work, because the service uses your application's main thread, by default, which could slow the performance of any activity your application is running.
-
IntentService
-
This is a subclass of
Service
that uses a worker thread to handle all start 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 each start request so you can do the background work.
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 you implement your service using theIntentService
class.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 you never 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.
onHandleIntent()
to do the work provided by the client. (Though, you also need to
provide a small constructor for the service.)Here's an example implementation of
IntentService
: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) {
}
}
}
}
}
That's all you need: a constructor and an implementation of
onHandleIntent()
.If you decide to also override other callback methods, such as
onCreate()
,
onStartCommand()
,
or onDestroy()
,
be sure to call the super implementation, so that the IntentService
can properly handle the life of the worker thread.For example,
onStartCommand()
must return the default implementation (which is how the intent gets
delivered to onHandleIntent()
):@Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent,flags,startId); }Besides
onHandleIntent()
,
the only method from which you don't need to call the super class is
onBind()
(but you only need to implement that if your service allows
binding).In the next section, you'll see how the same kind of service is implemented when extending the base
Service
class, which is a lot more code, but which might be appropriate if
you need to handle simultaneous start requests.Extending the Service class
As you saw in the previous section, usingIntentService
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
you can extend the Service
class to handle each intent.For comparison, the following example code is an implementation of the
Service
class that performs the exact same work as the example above using
IntentService
.
That is, for each start request, it uses a worker thread to perform
the job and processes only one request at a time.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(); } }As you can see, it's a lot more work than using
IntentService
.However, because you handle each call to
onStartCommand()
yourself, you can perform multiple requests simultaneously. That's
not what this example does, but if that's what you want, then you
can create a new thread for each request and run them right away
(instead of waiting for the previous request to finish).Notice that the
onStartCommand()
method must return an integer. The integer is a value that describes
how the system should continue the service in the event that the
system kills it (as discussed above, the default implementation for
IntentService
handles this for you, though you are able to modify it). The return
value from onStartCommand()
must be one of the following constants:START_NOT_STICKY
-
If the system kills the service after
onStartCommand()
returns, do not recreate the service, unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs. -
START_STICKY
-
If the system kills the service after
onStartCommand()
returns, recreate the service and callonStartCommand()
, but do not redeliver the last intent. Instead, the system callsonStartCommand()
with a null intent, unless there were pending intents to start the service, in which case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands, but running indefinitely and waiting for a job. -
START_REDELIVER_INTENT
-
If the system kills the service after
onStartCommand()
returns, recreate the service and callonStartCommand()
with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as downloading a file.
Starting a Service
You can start a service from an activity or other application component by passing anIntent
(specifying the service to start) to startService()
.
The Android system calls the service's onStartCommand()
method and passes it the Intent
.
(You should never call onStartCommand()
directly.)For example, an activity can start the example service in the previous section (
HelloSevice
) using an
explicit intent with startService()
:Intent intent = new Intent(this, HelloService.class); startService(intent);The
startService()
method returns immediately and the Android system calls the
service's onStartCommand()
method. If the service is not already running, the system first
calls onCreate()
,
then calls onStartCommand()
.If the service does not also provide binding, the intent delivered with
startService()
is the only mode of communication between the application component
and the service. However, if you want the service to send a result
back, then the client that starts the service can create a
PendingIntent
for a broadcast (with getBroadcast()
)
and deliver it to the service in the Intent
that starts the service. The service can then use the broadcast to
deliver a result.Multiple requests to start the service result in multiple corresponding calls to the service's
onStartCommand()
.
However, only one request to stop the service (with stopSelf()
or stopService()
)
is required to stop it.Stopping a service
A started service must manage its own lifecycle. That is, the system does not stop or destroy the service unless it must recover system memory and the service continues to run afteronStartCommand()
returns. So, the service must stop itself by calling stopSelf()
or another component can stop it by calling stopService()
.Once requested to stop with
stopSelf()
or stopService()
,
the system destroys the service as soon as possible.However, if your service handles multiple requests to
onStartCommand()
concurrently, then you shouldn't stop the service when you're done
processing a start request, because you might have since received a
new start request (stopping at the end of the first request would
terminate the second one). To avoid this problem, you can use
stopSelf(int)
to ensure that your request to stop the service is always based on
the most recent start request. That is, when you call stopSelf(int)
,
you pass the ID of the start request (the startId
delivered to onStartCommand()
)
to which your stop request corresponds. Then if the service received
a new start request before you were able to call stopSelf(int)
,
then the ID will not match and the service will not stop.Caution: It's important that your application stops its services when it's done working, to avoid wasting system resources and consuming battery power. If necessary, other components can stop the service by calling
stopService()
.
Even if you enable binding for the service, you must always stop the
service yourself if it ever received a call to onStartCommand()
.For more information about the lifecycle of a service, see the section below about Managing the Lifecycle of a Service.
Creating a Bound Service
A bound service is one that allows application components to bind to it by callingbindService()
in order to create a long-standing connection (and generally does
not allow components to start it by calling
startService()
).You should create a bound service when you want to interact with the service from activities and other components in your application or to expose some of your application's functionality to other applications, through interprocess communication (IPC).
To create a bound service, you must implement the
onBind()
callback method to return an IBinder
that defines the interface for communication with the service. Other
application components can then call bindService()
to retrieve the interface and begin calling methods on the service.
The service lives only to serve the application component that is
bound to it, so when there are no components bound to the service,
the system destroys it (you do not need to stop a bound
service in the way you must when the service is started through
onStartCommand()
).To create a bound service, the first thing you must do is define the interface that specifies how a client can communicate with the service. This interface between the service and a client must be an implementation of
IBinder
and is what your service must return from the onBind()
callback method. Once the client receives the IBinder
,
it can begin interacting with the service through that interface.Multiple clients can bind to the service at once. When a client is done interacting with the service, it calls
unbindService()
to unbind. Once there are no clients bound to the service, the
system destroys the service.There are multiple ways to implement a bound service and the implementation is more complicated than a started service, so the bound service discussion appears in a separate document about Bound Services.
Sending Notifications to the User
Once running, a service can notify the user of events using Toast Notifications or Status Bar Notifications.A toast notification is a message that appears on the surface of the current window for a moment then disappears, while a status bar notification provides an icon in the status bar with a message, which the user can select in order to take an action (such as start an activity).
Usually, a status bar notification is the best technique when some background work has completed (such as a file completed downloading) and the user can now act on it. When the user selects the notification from the expanded view, the notification can start an activity (such as to view the downloaded file).
See the Toast Notifications or Status Bar Notifications developer guides for more information.
Running a Service in the Foreground
A foreground service is a service that's considered to be something the user 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 the foreground, because the user is explicitly aware of its operation. The notification in the status bar might indicate the current song and allow the 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 integer that 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, indicating whether to remove the status
bar notification as well. This method does not stop the
service. However, if you stop the service while it's still running
in the foreground, then the notification is also removed.Note: The methods
startForeground()
and stopForeground()
were introduced in Android 2.0 (API Level 5). In order to run your
service in the foreground on older versions of the platform, you
must use the previous setForeground()
method—see the startForeground()
documentation for information about how to provide backward
compatibility.For more information about notifications, see Creating Status Bar Notifications.
Managing the Lifecycle of a Service
The lifecycle of a service is much simpler than that of an activity. However, it's even more important that you pay close attention to how your service is created and destroyed, because a service can run in the background without the user being aware.The service lifecycle—from when it's created to when it's destroyed—can follow two different paths:
- A started service
The service is created when another component callsstartService()
. The service then runs indefinitely and must stop itself by callingstopSelf()
. Another component can also stop the service by callingstopService()
. When the service is stopped, the system destroys it..
- A bound service
The service is created when another component (a client) callsbindService()
. The client then communicates with the service through anIBinder
interface. The client can close the connection by callingunbindService()
. Multiple clients can bind to the same service and when all of them unbind, the system destroys the service. (The service does not need to stop itself.)
startService()
.
For example, a background music service could be started by calling
startService()
with an Intent
that identifies the music to play. Later, possibly when the user
wants to exercise some control over the player or get information
about the current song, an activity can bind to the service by
calling bindService()
.
In cases like this, stopService()
or stopSelf()
does not actually stop the service until all clients unbind.
Implementing the lifecycle callbacks
Like an activity, a service has lifecycle callback methods that you can implement to monitor changes in the service's state and perform work at the appropriate times. The following skeleton service demonstrates each of the lifecycle methods:Figure 2. The service lifecycle. The diagram on the left shows the lifecycle when the service is created with
startService()
and the diagram on the right shows the lifecycle when the service is
created with bindService()
.public class ExampleService extends Service { int mStartMode; // indicates how to behave if the service is killed IBinder mBinder; // interface for clients that bind boolean mAllowRebind; // indicates whether onRebind should be used @Override public voidNote: Unlike the activity lifecycle callback methods, you are not required to call the superclass implementation of these callback methods.onCreate
() { // The service is being created } @Override public intonStartCommand
(Intent intent, int flags, int startId) { // The service is starting, due to a call tostartService()
return mStartMode; } @Override public IBinderonBind
(Intent intent) { // A client is binding to the service withbindService()
return mBinder; } @Override public booleanonUnbind
(Intent intent) { // All clients have unbound withunbindService()
return mAllowRebind; } @Override public voidonRebind
(Intent intent) { // A client is binding to the service withbindService()
, // after onUnbind() has already been called } @Override public voidonDestroy
() { // The service is no longer used and is being destroyed } }
By implementing these methods, you can monitor two nested loops of the service's lifecycle:
- The entire lifetime of a service happens
between the time
onCreate()
is called and the timeonDestroy()
returns. Like an activity, a service does its initial setup inonCreate()
and releases all remaining resources inonDestroy()
. For example, a music playback service could create the thread where the music will be played inonCreate()
, then stop the thread inonDestroy()
.
TheonCreate()
andonDestroy()
methods are called for all services, whether they're created bystartService()
orbindService()
.
- The active lifetime of a service begins
with a call to either
onStartCommand()
oronBind()
. Each method is handed theIntent
that was passed to eitherstartService()
orbindService()
, respectively.
If the service is started, the active lifetime ends the same time that the entire lifetime ends (the service is still active even afteronStartCommand()
returns). If the service is bound, the active lifetime ends whenonUnbind()
returns.
stopSelf()
or stopService()
,
there is not a respective callback for the service (there's no
onStop()
callback). So, unless the
service is bound to a client, the system destroys it when the
service is stopped—onDestroy()
is the only callback received.Figure 2 illustrates the typical callback methods for a service. Although the figure separates services that are created by
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, a service that was
initially started with onStartCommand()
(by a client calling startService()
)
can still receive a call to onBind()
(when a client calls bindService()
).For more information about creating a service that provides binding, see the Bound Services document, which includes more information about the
onRebind()
callback method in the section about Managing
the Lifecycle of a Bound Service.Bound Services
Quickview
- A bound service allows other components to bind to it, in order to interact with it and perform interprocess communication
- A bound service is destroyed once all clients unbind,
unless the service was also started
In this document
Key classes
Samples
See also
This document shows you how to create a bound service, including how to bind to the service from other application components. However, you should also refer to the Services document for additional information about services in general, such as how to deliver notifications from a service, set the service to run in the foreground, and more.
The Basics
A bound service is an implementation of theService
class that allows other applications to bind to it and interact with
it. To provide binding for a service, you must implement the
onBind()
callback method. This method returns an IBinder
object that defines the programming interface that clients can use
to interact with the service.Binding to a Started Service
As discussed in the Services document, you can create a service that is both started and bound. That is, the service can be started by callingstartService()
,
which allows the service to run indefinitely, and also allow a
client to bind to the service by calling bindService()
.
If you do allow your service to be started and bound, then when the service has been started, the system does not destroy the service when all clients unbind. Instead, you must explicitly stop the service, by calling
stopSelf()
or stopService()
.Although you should usually implement either
onBind()
or onStartCommand()
,
it's sometimes necessary to implement both. For example, a music
player might find it useful to allow its service to run indefinitely
and also provide binding. This way, an activity can start the
service to play some music and the music continues to play even if
the user leaves the application. Then, when the user returns to the
application, the activity can bind to the service to regain control
of playback.Be sure to read the section about Managing the Lifecycle of a Bound Service, for more information about the service lifecycle when adding binding to a started service.
A client can bind to the service by calling
bindService()
.
When it does, it must provide an implementation of
ServiceConnection
,
which monitors the connection with the service. The bindService()
method returns immediately without a value, but when the Android
system creates the connection between the client and service, it
calls onServiceConnected()
on the ServiceConnection
,
to deliver the IBinder
that the client can use to communicate with the service.Multiple clients can connect to the service at once. However, the system calls your service's
onBind()
method to retrieve the IBinder
only when the first client binds. The system then delivers the same
IBinder
to any additional clients that bind, without calling onBind()
again.When the last client unbinds from the service, the system destroys the service (unless the service was also started by
startService()
).When you implement your bound service, the most important part is defining the interface that your
onBind()
callback method returns. There are a few different ways you can
define your service's IBinder
interface and the following section discusses each technique.Creating a Bound Service
When creating a service that provides binding, you must provide anIBinder
that provides the programming interface that clients can use to
interact with the service. There are three ways you can define the
interface:- Extending the Binder class
-
If your service is private to your own application and runs in the
same process as the client (which is common), you should create
your interface by extending the
Binder
class and returning an instance of it fromonBind()
. The client receives theBinder
and can use it to directly access public methods available in either theBinder
implementation or even theService
. - This is the preferred technique when your service is merely a background worker for your own application. The only reason you would not create your interface this way is because your service is used by other applications or across separate processes.
- Using a Messenger
-
If you need your interface to work across different processes, you
can create an interface for the service with a
Messenger
. In this manner, the service defines aHandler
that responds to different types ofMessage
objects. ThisHandler
is the basis for aMessenger
that can then share anIBinder
with the client, allowing the client to send commands to the service usingMessage
objects. Additionally, the client can define aMessenger
of its own so the service can send messages back. -
This is the simplest way to perform interprocess communication
(IPC), because the
Messenger
queues all requests into a single thread so that you don't have to design your service to be thread-safe. - Using AIDL
-
AIDL (Android Interface Definition Language) performs all the work
to decompose objects into primitives that the operating system can
understand and marshall them across processes to perform IPC. The
previous technique, using a
Messenger
, is actually based on AIDL as its underlying structure. As mentioned above, theMessenger
creates a queue of all the client requests in a single thread, so the service receives requests one at a time. If, however, you want your service to handle multiple requests simultaneously, then you can use AIDL directly. In this case, your service must be capable of multi-threading and be built thread-safe. -
To use AIDL directly, you must create an
.aidl
file that defines the programming interface. The Android SDK tools use this file to generate an abstract class that implements the interface and handles IPC, which you can then extend within your service.
Extending the Binder class
If your service is used only by the local application and does not need to work across processes, then you can implement your ownBinder
class that provides your client direct access to public methods in
the service.Note: This works only if the client and service are in the same application and process, which is most common. For example, this would work well for a music application that needs to bind an activity to its own service that's playing music in the background.
Here's how to set it up:
- In your service, create an instance of
Binder
that either:- contains public methods that the client can call
- returns the current
Service
instance, which has public methods the client can call - or, returns an instance of another class hosted by the service with public methods the client can call
- In the client, receive the
Binder
from theonServiceConnected()
callback method and make calls to the bound service using the methods provided.
For example, here's a service that provides clients access to methods in the service through a
Binder
implementation:public class LocalService extends Service { // Binder given to clients private final IBinder mBinder = new LocalBinder(); // Random number generator private final Random mGenerator = new Random(); /** * Class used for the client Binder. 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 this instance of LocalService so clients can call public methods return LocalService.this; } } @Override public IBinder onBind(Intent intent) { return mBinder; } /** method for clients */ public int getRandomNumber() { return mGenerator.nextInt(100); } }The
LocalBinder
provides the
getService()
method for clients to
retrieve the current instance of LocalService
.
This allows clients to call public methods in the service. For
example, clients can call getRandomNumber()
from the service.Here's an activity that binds to
LocalService
and calls getRandomNumber()
when a
button is clicked:public class BindingActivity extends Activity { LocalService mService; boolean mBound = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to LocalService Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } /** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute) */ public void onButtonClick(View v) { if (mBound) { // Call a method from the LocalService. // However, if this call were something that might hang, then this request should // occur in a separate thread to avoid slowing down the activity performance. int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } /** Defines callbacks for service binding, passed to bindService() */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { // We've bound to LocalService, cast the IBinder and get LocalService instance LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; } }; }The above sample shows how the client binds to the service using an implementation of
ServiceConnection
and the onServiceConnected()
callback. The next section provides more information about this
process of binding to the service.Note: The example above doesn't explicitly unbind from the service, but all clients should unbind at an appropriate time (such as when the activity pauses).
For more sample code, see the
LocalService.java
class and the LocalServiceActivities.java
class in ApiDemos.Using a Messenger
Compared to AIDL
When you need to perform IPC, using aMessenger
for your interface is simpler than implementing it with AIDL,
because Messenger
queues all calls to the service, whereas, a pure AIDL interface
sends simultaneous requests to the service, which must then handle
multi-threading.For most applications, the service doesn't need to perform multi-threading, so using a
Messenger
allows the service to handle one call at a time. If it's important
that your service be multi-threaded, then you should use AIDL
to define your interface.If you need your service to communicate with remote processes, then you can use a
Messenger
to provide the interface for your service. This technique allows you
to perform interprocess communication (IPC) without the need to use
AIDL.Here's a summary of how to use a
Messenger
:- The service implements a
Handler
that receives a callback for each call from a client. - The service receives each
Message
in itsHandler
—specifically, in thehandleMessage()
method.
Message
objects) that the service receives in its Handler
.Here's a simple example service that uses a
Messenger
interface:public class MessengerService extends Service { /** Command to the service to display a message */ static final int MSG_SAY_HELLO = 1; /** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } } /** * Target we publish for clients to send messages to IncomingHandler. */ final Messenger mMessenger = new Messenger(new IncomingHandler()); /** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */ @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); return mMessenger.getBinder(); } }Notice that the
handleMessage()
method in the Handler
is where the service receives the incoming Message
and decides what to do, based on the what
member.All that a client needs to do is create a
Messenger
based on the IBinder
returned by the service and send a message using send()
.
For example, here's a simple activity that binds to the service and
delivers the MSG_SAY_HELLO
message to
the service:public class ActivityMessenger extends Activity { /** Messenger for communicating with the service. */ Messenger mService = null; /** Flag indicating whether we have called bind on the service. */ boolean mBound; /** * Class for interacting with the main interface of the service. */ 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 object we can use to // interact with the service. We are communicating with the // service using a Messenger, so here we get a client-side // representation of that from the raw IBinder object. mService = new Messenger(service); mBound = true; } public void onServiceDisconnected(ComponentName className) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. mService = null; mBound = false; } }; public void sayHello(View v) { if (!mBound) return; // Create and send a message to the service, using a supported 'what' value Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onStart() { super.onStart(); // Bind to the service bindService(new Intent(this, MessengerService.class), mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } } }Notice that this example does not show how the service can respond to the client. If you want the service to respond, then you need to also create a
Messenger
in the client. Then when the client receives the
onServiceConnected()
callback, it sends a Message
to the service that includes the client's Messenger
in the replyTo
parameter of the send()
method.You can see an example of how to provide two-way messaging in the
MessengerService.java
(service) and MessengerServiceActivities.java
(client) samples.Binding to a Service
Application components (clients) can bind to a service by callingbindService()
.
The Android system then calls the service's onBind()
method, which returns an IBinder
for interacting with the service.The binding is asynchronous.
bindService()
returns immediately and does not return the IBinder
to the client. To receive the IBinder
,
the client must create an instance of ServiceConnection
and pass it to bindService()
.
The ServiceConnection
includes a callback method that the system calls to deliver the
IBinder
.Note: Only activities, services, and content providers can bind to a service—you cannot bind to a service from a broadcast receiver.
So, to bind to a service from your client, you must:
- Implement
ServiceConnection
.
Your implementation must override two callback methods:
onServiceConnected()
- The system calls this to deliver the
IBinder
returned by the service'sonBind()
method.onServiceDisconnected()
- The Android system calls this when the connection to the service is unexpectedly lost, such as when the service has crashed or has been killed. This is not called when the client unbinds.
- Call
bindService()
, passing theServiceConnection
implementation. - When the system calls your
onServiceConnected()
callback method, you can begin making calls to the service, using the methods defined by the interface.
- To disconnect from the service, call
unbindService()
.
When your client is destroyed, it will unbind from the service, but you should always unbind when you're done interacting with the service or when your activity pauses so that the service can shutdown while its not being used. (Appropriate times to bind and unbind is discussed more below.)
IBinder
to the LocalService
class and request
the LocalService
instance:LocalService mService; private ServiceConnection mConnection = new ServiceConnection() { // Called when the connection with the service is established public void onServiceConnected(ComponentName className, IBinder service) { // Because we have bound to an explicit // service that is running in our own process, we can // cast its IBinder to a concrete class and directly access it. LocalBinder binder = (LocalBinder) service; mService = binder.getService(); mBound = true; } // Called when the connection with the service disconnects unexpectedly public void onServiceDisconnected(ComponentName className) { Log.e(TAG, "onServiceDisconnected"); mBound = false; } };With this
ServiceConnection
,
the client can bind to a service by passing this it to
bindService()
.
For example:Intent intent = new Intent(this, LocalService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
- The first parameter of
bindService()
is anIntent
that explicitly names the service to bind (thought the intent could be implicit). - The second parameter is the
ServiceConnection
object. - The third parameter is a flag indicating options for the
binding. It should usually be
BIND_AUTO_CREATE
in order to create the service if its not already alive. Other possible values areBIND_DEBUG_UNBIND
andBIND_NOT_FOREGROUND
, or0
for none.
Additional notes
Here are some important notes about binding to a service:- You should always trap
DeadObjectException
exceptions, which are thrown when the connection has broken. This is the only exception thrown by remote methods. - Objects are reference counted across processes.
- You should usually pair the binding and unbinding during matching bring-up and tear-down moments of the client's lifecycle. For example:
- If you want your activity to receive responses even while
it is stopped in the background, then you can bind during
onCreate()
and unbind duringonDestroy()
. Beware that this implies that your activity needs to use the service the entire time it's running (even in the background), so if the service is in another process, then you increase the weight of the process and it becomes more likely that the system will kill it.
onResume()
andonPause()
, because these callbacks occur at every lifecycle transition and you should keep the processing that occurs at these transitions to a minimum. Also, if multiple activities in your application bind to the same service and there is a transition between two of those activities, the service may be destroyed and recreated as the current activity unbinds (during pause) before the next one binds (during resume). (This activity transition for how activities coordinate their lifecycles is described in the Activities document.)
RemoteService.java
class in ApiDemos.Managing the Lifecycle of a Bound Service
Figure 1. The lifecycle for a service that is started and also allows binding.
When a service is unbound from all clients, the Android system destroys it (unless it was also started with
onStartCommand()
).
As such, you don't have to manage the lifecycle of your service if
it's purely a bound service—the Android system manages it for you
based on whether it is bound to any clients.However, if you choose to implement the
onStartCommand()
callback method, then you must explicitly stop the service, because
the service is now considered to be started. In this case,
the service runs until the service stops itself with stopSelf()
or another component calls stopService()
,
regardless of whether it is bound to any clients.Additionally, if your service is started and accepts binding, then when the system calls your
onUnbind()
method, you can optionally return true
if you would like to receive a call to onRebind()
the next time a client binds to the service (instead of receiving a
call to onBind()
).
onRebind()
returns void, but the client still receives the IBinder
in its onServiceConnected()
callback. Below, figure 1 illustrates the logic for this kind of
lifecycle.Example
ServicesDemo
package com.example;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class ServicesDemo extends Activity implements OnClickListener {
private static final String TAG = "ServicesDemo";
Button buttonStart, buttonStop;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
buttonStart = (Button) findViewById(R.id.buttonStart);
buttonStop = (Button) findViewById(R.id.buttonStop);
buttonStart.setOnClickListener(this);
buttonStop.setOnClickListener(this);
}
public void onClick(View src) {
switch (src.getId()) {
case R.id.buttonStart:
Log.d(TAG, "onClick: starting srvice");
startService(new Intent(this, MyService.class));
break;
case R.id.buttonStop:
Log.d(TAG, "onClick: stopping srvice");
stopService(new Intent(this, MyService.class));
break;
}
}
}
MyService
package com.example;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class MyService extends Service
{
private static final String TAG =
"MyService";
MediaPlayer player;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Toast.makeText(this, "My Service
Created", Toast.LENGTH_LONG).show();
Log.d(TAG, "onCreate");
player = MediaPlayer.create(this,
R.raw.mai);
player.setLooping(false); // Set
looping
}
@Override
public void onDestroy() {
Toast.makeText(this, "My Service
Stopped", Toast.LENGTH_LONG).show();
Log.d(TAG, "onDestroy");
player.stop();
}
@Override
public void onStart(Intent intent, int
startid) {
Toast.makeText(this, "My Service
Started", Toast.LENGTH_LONG).show();
Log.d(TAG, "onStart");
player.start();
}
}
AndroidManifest.xml
<?xml
version="1.0"
encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example"
android:versionCode="1"
android:versionName="1.0">
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
<activity
android:name=".ServicesDemo"
android:label="@string/app_name">
<intent-filter>
<action
android:name="android.intent.action.MAIN"
/>
<category
android:name="android.intent.category.LAUNCHER"
/>
</intent-filter>
</activity>
<service
android:enabled="true"
android:name=".MyService"
/>
</application>
<uses-sdk
android:minSdkVersion="3"
/>
</manifest>
main.xml
<?xml
version="1.0"
encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Services
Demo" android:gravity="center"
android:textSize="20sp"
android:padding="20dp"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/buttonStart"
android:text="Start"></Button>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop"
android:id="@+id/buttonStop"></Button>
</LinearLayout>
http://www.javabeat.net/articles/252-creating-services-in-android-1.html
No comments:
Post a Comment