Service是一个执行长时间后台运行操作,并且不提供界面的程序组件。其他组件可以启动一个service,这个service会运行在后台,即使用户切换到了其他程序,service也不会停止。一个组件可以和service绑定来进行交互,甚至是进行进程间的通信(IPC)。例如,一个service可以处理网络连接,播放音乐,执行文件I/O,或者和一个content provider交互,这些都在后台运行。
你需要继承Service类来创建一个service,在你的实现中,你需要重写一些重要的回调函数:
其他组件使用startService()传递一个intent来启动一个service,而service中的onStartCommand()方法接收intent并启动service。当service启动后,它有自己独立的生命周期。
一个Bound Service允许程序组件调用bindService()来绑定,从而创建一个长时间的连接。
service可以通过Toast Notification或者Status Bar Notification来通知用户。
前台service必须在status bar中提供一个通知,放在“运行中”的标题下面,除非service停止或者转入后台,不然这个通知不会消失。
service的生命周期比activity简单,不过也需要注意,因为一个service可能在你不知道的情况下运行在后台。
一个service基本可以采取两种方式:
Started
Bound当一个组件调用startService()来启动一个service时,service就是"Started"的,一旦被启动,service就会一直运行在后台,通常,一个启动的service执行一个单元的操作,并且没有返回值。例如,从网络下载一个文件。当操作完成,service就自动关闭了。
当然,一个service可以在被启动的同时也被绑定。只要你简单的实现一些回调就可以实现:onStartCommand()让组件启动它,onBind()让组件绑定它。当一个组件使用bindService()来绑定一个service时,service就是"bound"的。一个绑定的service提供一个客户端-服务器的接口来让组件和它进行交互,发送请求,取得结果,进程间的交互。多个组件可以同时绑定一个service,当所有的组件都解除绑定时,service被销毁。
和activity一样,任何组件都可以使用service。当然你也可以在清单文件中定义service为私有的。
警告:一个service运行在它的宿主进程的主线程中 - service不创建它自己的线程,不运行在单独的进程中(除非你指定)。意思就是说,如果你的service执行任何高CPU负载工作或者阻塞操作(例如播放MP3或者联网),你就应该创建一个新的线程来让service工作。使用单独的线程,你将减少程序无响应的风险,程序主线程也可以专注于用户交互。
基础
你需要继承Service类来创建一个service,在你的实现中,你需要重写一些重要的回调函数:
onStartCommand()
onBind()当其他组件使用startService()来启动一个service时调用。一旦这个方法执行,service就被启动并一直运行在后台,当service完成工作时,要调用stopSelf()或者stopService()来停止service。如果service仅仅提供绑定,那么这个方法不需要实现。
onCreate()当其他组件使用bindService()绑定一个service时执行。这个方法的实现需要提供一个客户端和服务器的接口,通过返回IBinder来实现通信。如果你不允许绑定的话,这个方法需要返回null。
onDestroy()第一次创建时调用,只会执行一次,在上面的两个方法前执行。
如果一个service是通过startService()启动的,那么这个service会一直运行,直到它调用stopSelf()或者其他组件调用stopService()来停止它。被销毁或者长时间不使用时调用这个方法。这个方法中主要是释放资源,例如关闭线程,注册的监听,接受者等等,是service最后调用的方法。
如果是通过bindService()创建的service,一旦service被所有的客户端解除绑定时,系统就会销毁它。
系统会在急需内存资源时强制停止一个service,如果一个service绑定在一个正取得用户焦点的activity上,那么基本不会被销毁,如果一个service被声明为前台运行,那么它也不会被销毁。一个service随着启动时间的增加,系统会降低它的在后台任务的位置,从而有很大几率被销毁,一旦你的service被销毁,你必须很好的设计重启操作。当系统资源充足时,它会被马上重启。
下面的章节介绍每种启动service的方式和他们是怎么被其他组件使用的。
在清单文件中声明一个service
你必须在清单文件中声明所有的service:
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>
android:name是唯一必须声明的属性。一旦发布了程序,这个名称就不应该更换。
和activity一样,service也可以定义intent过滤器来让其他组件隐式的调用service。
如果你想让你的service是私有的,你需要声明android:exported属性为false。
创建一个Started Service
其他组件使用startService()传递一个intent来启动一个service,而service中的onStartCommand()方法接收intent并启动service。当service启动后,它有自己独立的生命周期。
例如,假设一个activity要保存数据到网络数据库,activity可以通过intent传递数据启动一个service,service接收intent,连接网络,更新数据库,当更新完成后,service自动停止和被销毁。
下面有两个你可以继承的service类:
Service
IntentService所有service的基类。当你继承这个类时,比较重要的一点是你应该创建一个新的线程来执行service的工作,因为默认service运行在程序主线程中,会降低程序的性能。
扩展IntentService类service类的子类,使用一个工作线程来处理所有的启动请求,一次一个。如果你不需要同时处理多个请求,那么这个类是很好的选择。你只需要实现onHandleIntent()方法来接收intent,然后执行后台工作。
因为大多数started service不需要同时处理多个请求,所以实现IntentService可能是做好的选择了。
它做下面这些事:
- 创建一个独立于主线程的工作线程,执行传递给onStartCommand()的所有intent。
- 创建一个工作队列,一次只传递一个intent给你的onHandleIntent(),所以你不用担心多线程。
- 处理完所有的请求后停止service,你不需要调用stopSelf()。
- 提供默认的返回null的onBind()的实现。
- 提供默认的onStartCommand()的实现,它发生intent到工作队列,然后工作队列发送到onHandleIntent()中。
你只需在onHandleIntent()中实现客户端提供的工作(当然,简单的构造函数还是需要的):
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) {
}
}
}
}
}
如果你想重写onCreate,onStartCommand,或者onDestroy方法,你需要调用父类的实现,以便IntentService能很好的管理工作线程的生命周期。例如:
@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); }
除了onHandleIntent(),唯一你不用调用父类实现的是onBind()(如果你想你的service允许绑定,那么你需要实现这个方法,默认返回null)。
扩展Service类
如果你想service能实现多线程的工作,那么你需要继承Service类。
为了做比较,下面的代码实现了上面的IntentService一样的工作,对于每个启动请求,它使用一个工作线程来处理,一次只处理一个。
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(); } }
如你所见,比IntentService代码多了很多。
因为我们自己在onStartCommand中处理请求,所以你可以执行多线程,不过上面的例子没有实现,你可以自己为每个请求创建一个新的线程,然后运行它们,而不是等待上一个请求完成。
需要注意的是onStartCommand方法需要返回一个整型,这个整型值描述了当系统杀掉这个service后,系统会这么继续这个service。返回值是下面这些常量:
START_NOT_STICKY
START_STICKY如果系统在onStartCommand方法返回后杀掉service,那么不好重新创建这个service,除非有新的intent被传递过来。这是最安全的做法,避免service被重复创建,让程序可以重启未完成的工作。
如果系统在onStartCommand方法返回后杀掉service,那么重新创建service,并调用onStartCommand方法,但是不重新传递最近的intent。而是使用null的intent来调用onStartCommand方法。除非有没有处理的intent来启动service,这种情况适合媒体播放,它不执行命令,但是运行在后台等待一个工作的来临。
START_REDELIVER_INTENT
启动一个service如果系统在onStartCommand方法返回后杀掉service,使用最后传递的intent调用onStartCommand()来重新创建service。这种情况适合哪种需要立即回复工作的service,比如下载文件。
下面的例子使用显式的intent启动一个service:
Intent intent = new Intent(this, HelloService.class); startService(intent);
startService()方法会马上返回,然后系统调用service的onStartCommand()方法。如果service不是已经在运行,那么就会调用onCreate()方法。
如果service不提供绑定,那么startService()是传递intent给service是唯一的一个通信方式。不过,如果你想service返回一个结果,启动service的客户端可以创建一个PendingIntent来启动service,那么service就可以使用广播来传递一个结果。
多个启动请求会导致onStartCommand被多次调用,不过停止请求只需要一个。
停止一个service
一个started的service必须自己管理生命周期,需要自己调用stopSelf或者其他组件调用stopService来停止。调用后,系统会尽快的销毁这个service。
在多请求的情况下,我们可以使用stopSelf(int)来确保停止的是对应的ID的请求。
警告:在程序中停止service并释放资源非常重要。当你启动绑定service时,如果有onStartCommand的调用,你也需要自己停止service。
创建一个Bound Service
一个Bound Service允许程序组件调用bindService()来绑定,从而创建一个长时间的连接。
Bound service让你可以在组件间进行通信,而且通过进程通信,你的程序的部分功能会被展现给其他程序。
为了创建一个bound service,你必须实现onBind()方法,返回一个IBinder,这个IBinder定义了通信接口。其他程序组件通过调用bindService()来检索这个接口,然后在service上执行函数。当没有组件绑定到这个bound service时,会被系统销毁。
发送通知给用户
service可以通过Toast Notification或者Status Bar Notification来通知用户。
toast notification是出现在当前窗口的一个提示信息,会在一小段时间后消失,当status bar notification提供一个图标和信息在status bar时,用户可以点击图标来执行某个动作。
通常一个status bar notification是好的选择,当后台工作完成后,用户可以直接点击执行下面的工作。
在前台运行一个Service
前台service必须在status bar中提供一个通知,放在“运行中”的标题下面,除非service停止或者转入后台,不然这个通知不会消失。
例如,一个音乐播放器需要被设置为运行在前台,因为用户需要操作它。status bar可以显示当前播放歌曲,运行用户点击启动一个activity来与播放器交互。
调用startForeground()来让service运行在前台,这个方法有两个参数:整形的notification的唯一标示符和一个notification。例如:
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);
调用stopForeground()从前台移除service。需要一个布尔型参数来指定是否同时移除status bar的通知。这个方法不会停止service,不过,如果你停止运行在前台的service,那么通知也会消失。
提示:上面的方法在Android2.0才被引进,如果你想在更低版本中使用,你需要使用setForeground()方法。
管理service的生命周期
service的生命周期比activity简单,不过也需要注意,因为一个service可能在你不知道的情况下运行在后台。
它的生命周期包含下面两个重要的路径:
- started service
其他组件调用startService()时被创建。然后这个service会一直运行,直到自己调用stopSelf(),或者其他组件调用stopService。当service已经停止,系统消耗它。
- bound service
其他组件(客户端)调用bindService()时创建,客户端通过IBinder接口与service通信,调用unbindService()关闭连接。多个客户端可以绑定相同的service,当所有的客户端解除绑定后,系统自动消耗service。
这两个路径不是独立的,你可以绑定一个使用startService()启动的service。例如,一个背景音乐service可以被startService()启动,过了一会,你可能想执行一些控制或者取得一些当前歌曲信息,那么一个activity就可以绑定这个service,在这种情况下,stopService和stopSelf不能停止这个service,直到所有的客户端解除绑定。
实现生命周期回调
和activity一样,你可以实现回调函数来改变service的状态,执行合适的工作,下面是一个基本的例子:
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 voidonCreate
() { // The service is being created } @Override public intonStartCommand
(Intent intent, int flags, int startId) { // The service is starting, due to a call tostartService()
return mStartMode; } @Override public IBinderonBind
(Intent intent) { // A client is binding to the service withbindService()
return mBinder; } @Override public booleanonUnbind
(Intent intent) { // All clients have unbound withunbindService()
return mAllowRebind; } @Override public voidonRebind
(Intent intent) { // A client is binding to the service withbindService()
, // after onUnbind() has already been called } @Override public voidonDestroy
() { // The service is no longer used and is being destroyed } }
和activity不太相同的是,每个实现都不需要调用父类的实现。
![](https://img-my.csdn.net/uploads/201209/06/1346935998_5752.png)
实现这些函数,你可以监控service生命周期的两个内嵌循环:
- 整个生命时间是从onCreate()调用到onDestroy()返回。在onCreate中初始化,在onDestroy中释放资源。例如,一个音乐播放器service可以在onCreate()中创建线程来播放音乐,在onDestroy中停止线程。
- 活动生命时间是从onStartCommand()或者onBind()被调用开始。如果service是被started的,那么活动时间和整个生命时间的停止时一样的。如果是service是bound的,活动时间在onUnbind返回时结束。
提示:虽然started service通过调用stopSelf()或者stopService()来停止,但是没有一个特别的回调函数,所以,除非service被绑定到了客户端,不然当service停止后系统就会销毁它,onDestroy()是唯一回调函数。
不管service是怎么启动的,都允许客户端绑定它。