Android Services
1.1 Service
A Service is a component which runs in the background, without interacting with
the user. Every developer can create newServices 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.
The
Android platform provides pre-defined Services, usually exposed
via a specific Manager class. Access to these services can be gained via the
method getSystemService().
You
can declare your own Service 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.
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.
You
can also specify that your Service 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 theService would be a global process and can be used by other components.
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 theService 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.
While
the base class for creating a Service 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.
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.
An Activity can start a Service via
the startService() method and
stop the service via the stopService() method. If theActivity want
to interact with the Activity it
can use the bindService() method
of the service. This requires anServiceConnection 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 withService.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.
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 withService.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.
There
are several way for an Activity to communicate with an Service and vice versa.
If
the Service is started in the same process as the Activity, the Activity can directly bind to the service a call of thebindService() 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.
See Local Service Example for an example.
To
bind to a Service 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.
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.
If
the service should be communicating back to the Activity 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 typeMessage 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 theHandler 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.
A Messenger is parcable, which means it can be passed to another process and you can use this object to send Messages to theHandler 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.
A
broadcast receiver is a class which extends BroadcastReceiver 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 theContext.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.
Alternatively to the this static registration, you can also register a BroadcastReceiver dynamically via theContext.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.
The sendBroadcast() 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.
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);
A
normal broadcast Intent is not available anymore after is was send and
processed by the system. If you use thesendBroadcast(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.
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;
To
start Services automatically after the Android system starts you can
register a BroadcastReceiver to
the Androidandroid.intent.action.BOOT_COMPLETED system event. This requires theandroid.permission.RECEIVE_BOOT_COMPLETED permission.
The following AndroidManifest.xml registers a receiver for the BOOT_COMPLETED event.
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 receiveandroid.intent.action.BOOT_COMPLETED events.
Also note that as of Android 3.0 the user needs to have started the application at least once before your application can receiveandroid.intent.action.BOOT_COMPLETED events.
As
with Activities 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.
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.
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().
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().
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 followingAndroidManifest.xml.
Create a new project de.vogella.android.receiver.phone. We do not need an Activity. Create the followingAndroidManifest.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.
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.
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.
The
following will demonstrate how to use the IntentService class to
download a file from the Internet. Once done theIntentService 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.
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.
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 an activity 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.
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();
}
}
}
The
following chapter will demonstrate how to communicate between an Activity and an Service using the Messenger andHandler class.
Create a new project called "de.vogella.android.ownservice.messenger" with an Activity called "MainActivity".
Creating the following "AndroidManifest.xml".
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
A 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:
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.
Although this
documentation generally discusses these two types of services separately, your
service can work both ways—it can be started (to run indefinitely) and also
allow binding. It's simply a matter of whether you implement a couple callback
methods:onStartCommand() to allow components to
start it 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.
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.
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 usingAsyncTask 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:
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 usingAsyncTask 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:
The system calls this method when another
component, such as an activity, requests that the service be started, by
callingstartService(). 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
calling stopSelf() or stopService(). (If you only want to provide
binding, you don't need to implement this method.)
The system calls this method when another
component wants to bind with the service (such as to perform RPC), by callingbindService(). In your implementation of this
method, you must provide an interface that clients use to communicate with the
service, by returning an IBinder. You must always implement this
method, but if you don't want to allow binding, then you should return null.
The system calls this method when the service
is first created, to perform one-time setup procedures (before it calls eitheronStartCommand() or onBind()). If the service is already running,
this method is not called.
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.
If a component starts
the service by calling 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.
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.
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:
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 aboutstarting 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.
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 aboutstarting 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.
TARGETING ANDROID 1.6
OR LOWER
If you're building an
application for Android 1.6 or lower, you need to implement onStart(), 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'sonStartCommand() 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:
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'sonStartCommand() 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:
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.
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 implement onHandleIntent(), which receives the intent
for each start request so you can do the background work.
The following sections
describe how you can implement your service using either one for these classes.
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 the IntentService class.
The IntentService does the following:
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.
All this adds up to
the fact that all you need to do is implement 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:
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 toonHandleIntent()):
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 toonHandleIntent()):
@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.
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.
As you saw in the
previous section, using IntentService makes your implementation
of a started service very simple. If, however, you require your service to
perform multi-threading (instead of processing start requests through a work
queue), then 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.
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:
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:
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.
If the system kills the service after onStartCommand() returns, recreate the
service and call onStartCommand(), but do notredeliver
the last intent. Instead, the system calls onStartCommand() 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.
If the system kills the service after onStartCommand() returns, recreate the
service and call onStartCommand() 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.
For more details about
these return values, see the linked reference documentation for each constant.
You can start a
service from an activity or other application component by passing an Intent (specifying the service to start)
tostartService(). 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 withstartService():
For example, an activity can start the example service in the previous section (HelloSevice) using an explicit intent withstartService():
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.
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.
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
after onStartCommand() 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.
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.
A bound service is one
that allows application components to bind to it by calling bindService() 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.
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.
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.
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.
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:
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 thestartForeground() documentation for information about how to provide backward compatibility.
For more information about notifications, see Creating Status Bar Notifications.
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 thestartForeground() documentation for information about how to provide backward compatibility.
For more information about notifications, see Creating Status Bar Notifications.
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:
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 calls startService(). The service then runs indefinitely and must stop itself by calling stopSelf(). Another component can also stop the service by calling stopService(). When the service is stopped, the system destroys it..
The service is created when another component calls startService(). The service then runs indefinitely and must stop itself by calling stopSelf(). Another component can also stop the service by calling stopService(). When the service is stopped, the system destroys it..
·
A bound service
The service is created when another component (a client) calls bindService(). The client then communicates with the service through an IBinder interface. The client can close the connection by calling unbindService(). 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.)
The service is created when another component (a client) calls bindService(). The client then communicates with the service through an IBinder interface. The client can close the connection by calling unbindService(). 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.)
These two paths are
not entirely separate. That is, you can bind to a service that was already
started with 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.
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
void onCreate() {
// The service is being created
}
@Override
public
int onStartCommand(Intent intent, int flags, int
startId) {
// The service is starting, due to a call to startService()
return mStartMode;
}
@Override
public
IBinder onBind(Intent intent) {
// A client is binding to the service with bindService()
return mBinder;
}
@Override
public
boolean onUnbind(Intent intent) {
// All clients have unbound with unbindService()
return mAllowRebind;
}
@Override
public
void onRebind(Intent intent) {
// A client is binding to the service with bindService(),
// after onUnbind() has already been called
}
@Override
public
void onDestroy() {
// The service is no longer used and is being destroyed
}
}
Note: Unlike the activity lifecycle callback
methods, you are not required to call the superclass
implementation of these callback methods.
By implementing these methods, you can monitor two nested loops of the service's lifecycle:
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 time onDestroy() returns. Like an activity, a
service does its initial setup in onCreate() and releases all remaining
resources in onDestroy(). For example, a music playback
service could create the thread where the music will be played in onCreate(), then stop the thread in onDestroy().
The onCreate() and onDestroy() methods are called for all services, whether they're created by startService() orbindService().
The onCreate() and onDestroy() methods are called for all services, whether they're created by startService() orbindService().
·
The active
lifetime of a service begins with a call to either onStartCommand() or onBind(). Each method is handed the Intent that was passed to either startService() or bindService(), respectively.
If the service is started, the active lifetime ends the same time that the entire lifetime ends (the service is still active even after onStartCommand() returns). If the service is bound, the active lifetime ends when onUnbind() returns.
If the service is started, the active lifetime ends the same time that the entire lifetime ends (the service is still active even after onStartCommand() returns). If the service is bound, the active lifetime ends when onUnbind() returns.
Note: Although a started service is stopped by
a call to either 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 bystartService() 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.
Figure 2 illustrates the typical callback methods for a service. Although the figure separates services that are created bystartService() 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
·
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
·
Service
·
IBinder
SAMPLES
SEE ALSO
·
Services
A bound service is the
server in a client-server interface. A bound service allows components (such as
activities) to bind to the service, send requests, receive responses, and even
perform interprocess communication (IPC). A bound service typically lives only
while it serves another application component and does not run in the
background indefinitely.
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.
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 the Service 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
calling startService(), which allows the service to
run indefinitely, and also allow a client to bind to the service by callingbindService().
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 ofServiceConnection, 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 theServiceConnection, 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 theIBinder only when the first client binds. The system then delivers the same IBinder to any additional clients that bind, without callingonBind() again.
When the last client unbinds from the service, the system destroys the service (unless the service was also started bystartService()).
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.
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 ofServiceConnection, 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 theServiceConnection, 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 theIBinder only when the first client binds. The system then delivers the same IBinder to any additional clients that bind, without callingonBind() again.
When the last client unbinds from the service, the system destroys the service (unless the service was also started bystartService()).
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 an IBinder that provides the programming
interface that clients can use to interact with the service. There are three
ways you can define the interface:
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 from onBind(). The client receives the Binderand can use it to directly access public
methods available in either the Binder implementation or even the Service.
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.
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
a Handler that responds to different types
of Message objects. This Handler is the basis for aMessenger that can then share an IBinder with the client, allowing the
client to send commands to the service using Messageobjects. Additionally, the client can
define a Messenger 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, the Messenger 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.
Note: Most applications should not use
AIDL to create a bound service, because it may require multithreading
capabilities and can result in a more complicated implementation. As such, AIDL
is not suitable for most applications and this document does not discuss how to
use it for your service. If you're certain that you need to use AIDL directly,
see the AIDL document.
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:
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:
1.
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
3.
In the client, receive
the Binder from the onServiceConnected() callback method and
make calls to the bound service using the methods provided.
Note: The reason the service and client must
be in the same application is so the client can cast the returned object and
properly call its APIs. The service and client must also be in the same
process, because this technique does not perform any marshalling across
processes.
For example, here's a service that provides clients access to methods in the service through a Binder implementation:
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:
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 theonServiceConnected() 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.
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 a Messenger for your interface is simpler
than implementing it with AIDL, because Messengerqueues 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:
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.
·
Clients use the IBinder to instantiate the Messenger (that references the service's Handler), which the client uses to send Message objects to the service.
·
The service receives
each Message in its Handler—specifically, in the handleMessage() method.
In this way, there are
no "methods" for the client to call on the service. Instead, the
client delivers "messages" (Message objects) that the service
receives in its Handler.
Here's a simple example service that uses a Messenger interface:
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 usingsend(). For example, here's a simple activity that binds to the service and delivers the MSG_SAY_HELLO message to the service:
All that a client needs to do is create a Messenger based on the IBinder returned by the service and send a message usingsend(). 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) andMessengerServiceActivities.java (client) samples.
You can see an example of how to provide two-way messaging in the MessengerService.java (service) andMessengerServiceActivities.java (client) samples.
BINDING TO A SERVICE
Application components
(clients) can bind to a service by calling bindService(). The Android system then calls
the service'sonBind() 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 theIBinder, the client must create an instance of ServiceConnection and pass it to bindService(). The ServiceConnectionincludes 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:
The binding is asynchronous. bindService() returns immediately and does not return the IBinder to the client. To receive theIBinder, the client must create an instance of ServiceConnection and pass it to bindService(). The ServiceConnectionincludes 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:
1.
Implement ServiceConnection.
Your implementation must override two callback methods:
Your implementation must override two callback methods:
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.
2.
Call bindService(), passing the ServiceConnection implementation.
3.
When the system calls
your onServiceConnected() callback method, you
can begin making calls to the service, using the methods defined by the
interface.
4.
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.)
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.)
For example, the
following snippet connects the client to the service created above by extending the Binder class, so all it must do
is cast the returned 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 an Intent 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 are BIND_DEBUG_UNBIND and BIND_NOT_FOREGROUND, or 0 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 only need to
interact with the service while your activity is visible, you should bind
during onStart() and unbind during onStop().
· If you want your
activity to receive responses even while it is stopped in the background, then
you can bind duringonCreate() and unbind during onDestroy(). 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.
Note: You should usually not bind
and unbind during your activity's onResume() and onPause(), 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 Activitiesdocument.)
For more sample code,
showing how to bind to a service, see the RemoteService.java class in ApiDemos.
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 toonBind()). 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;
}
}
}
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 toonBind()). 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