一、概述
Service是一个应用程序组件,可在后台执行耗时的操作,没有用户界面。其他的应用程序组件可以启动一个service,即使用户跳转到其他的应用程序,这个service仍可在后台继续运行;另外,其他组件还可以绑定到一个service上,并跟这个service进行通信,甚至可以通过service进行线程间通信(IPC)。
Service有两种存在形式:
Started:
这种service是由其他组件(如activity)通过调用startService()启动的。这种service一旦被启动,就会无限期的运行下去,即使启动它的组件已经被销毁。通常这种service用来执行单一操作,并且不向调用者返回任何结果(例如,通过网络下载或上传文件)。当操作完成后,要记得结束该service。
Bound:
一个应用程序组件可通过调用bindService()绑定到一个service上。Bound service允许其他组件向该service发送请求、从该service获得返回结果,甚至通过该service进行IPC。一个Bound Service只有被其他组件绑定时才能运行。多个组件可以同时绑定到一个service上,当所有绑定组件都解除绑定时,这个service自动被系统销毁。
(一个service可以同时以这两种方式存在,可以被启动而无限期的运行、同时接受绑定。)
注意:一个service运行在其宿主进程的主线程内,除非专门指定,一个service不会创建它自己的线程、也不会运行在一个单独的进程内。这意味着,如果一个service要执行一些阻塞操作或占用大量CPU的工作时,应该在service中另建一个单独的线程,这样可以减少ANR。(如果要执行的工作运行在main thread之外,但此工作只有当用户与应用程序交互时才会执行,这时只需创建一个新线程就可以了,不需要创建service。例如,想在activity运行时播放音乐,应该在activity的onCreate()方法里创建一个新线程,在onStart()里运行它,在onStop()结束它就行了。)
二、基础
要创建一个service,必须继承Service类,并在其中复写必要的Service的生命周期方法,一些重要的方法如下:
onStartCommand()
当其他应用程序组件通过调用startService()启动一个service时,系统会调用这个函数。一旦这个函数开始执行,service就开始在后台无限期的执行;在service中的工作完成以后,程序员要通过调用stopSelf()或stopService()结束当前service。
(如果一个service只是用来被绑定的,没有必要实现这个方法。)
onBind()
当其他组件通过调用bindService()绑定到一个service上时,系统会调用这个函数。这个函数返回一个IBinder,是客户端与service通信的接口。在Service子类中必须实现这个函数,如果不打算让这个service被绑定,可以让它返回null。
onCreate()
当service第一次被创建时调用此方法(在onStartCommand()或onBind()之前被调用)。
onDestroy()
service被销毁时调用。可以在其中执行清理操作,如清理线程、注册的监听器、接收器等。
如果一个service是由startService()启动,这个service会一直运行,直到service通过stopSelf()自行终止、或其他组件调用stopService()将其终止,之后被系统销毁。
如果一个service是由bindService()创建,一旦所有的clients对其解除绑定,这个service就会被系统销毁。
只有当内存极少、必须要回收系统资源以保证当前拥有用户焦点的activity运行时,service才会被系统强行销毁。如果一个service绑定在当前拥有用户焦点的activity上,它是不太可能被杀死的;如果一个service被声明在前台运行(后面有对此详细介绍),也是不太可能被杀死的。然而,如果一个service在后台长时间运行,随着时间推移,系统会逐渐降低它在后台任务中的重要性,使其被杀死的可能性增大;但一旦可以获得系统资源,被系统杀死的service会重新启动(当然还跟onStartCommand()的返回值情况有关)。
像其他组件一样,service必须在manifest文件中声明,格式如下:
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>
/*关于<service>元素可包含的更多属性,参看官方文档中的"Android Manifest"部分*/
像activity一样,service也可定义自己的intent filters。一旦service声明了自己的intent filters,用户设备上的任何应用的组件都有可能启动该service,只要该组件传入到startService()中的intent与service的intent filters匹配。如果一个service只是被所在程序使用,就不应该提供intent filters,只需用intent显示的启动它就行了;或者,为了确保service的私有性,可在manifest中设置android:exported="false" (即使service提供了intent filters,这条属性也会将其屏蔽)。
三、创建Started Service
一个应用程序组件通过向startService()中传递一个Intent来启动Started Service,service中的onStartCommand()方法对该Intent进行接收。
要创建一个Started Service,可以继承以下两个类中的其中一个:
Service:如果继承这个类,要是service中存在可能拖慢activity响应的操作时,需要程序员在该service中创建新线程。
IntentService:这是Service的子类,它会创建一个工作线程处理所有启动请求(一次处理一个请求)。如果一个service不需要同时处理多个请求,就应该继承这个类。继承这个类时,只需实现它的onHandleIntent()方法,这个方法负责接收每个启动请求的intent。
下面介绍如何通过继承这两个类创建自己的Started Service:
继承IntentService类
IntentService做了如下的事情:
*创建一个默认的工作线程,用于处理所有传进来的intents;
*创建一个工作队列,用于存放所有intents,并一次向onHandleIntent()传递一个intent,所以避免了多线程的问题;
*提供了一个onStartCommand()的默认实现,用于把传入的intent放入工作队列,然后传递到onHandleIntent()中;
*处理完所有请求后会自动结束该service,因此不需要程序员再调用stopSelf();
*提供了一个onBind()的默认实现,并返回null。
所以,继承IntentService时,只需实现onHandleIntent()方法并提供一个简单的构造函数即可,如下:
public class HelloIntentService extends IntentService { /**必须的构造函数,必须在其中调用父类的IntentService(String)构造函数,并向其中传入工作线程的名字。 */ 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) { } } } } }
如果需要复写其他的回调方法,一定要记得调用父类IntentService对该方法的实现,以确保IntentService能够正确处理工作线程的声明周期;例如,复写onStartCommand()时,必须返回父类默认的实现,如下:
@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); }
除了onBind()(当service允许绑定时复写此方法)外,所有其他的回调方法都要遵循这个规则。
继承Service类
这种方法多是用在多线程任务中。
示例代码如下(这只是简化示例,并没有实现多线程操作):
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(); } }
注意,onStartCommand()函数必须返回一个整数;当service被系统杀死,该整数指示系统接下来如何处理该service。返回的值必须是下列常量之一:
START_NOT_STICKY
如果在onStartCommand()返回后service被系统杀死,不会重建该service。
START_STICKY
如果在onStartCommand()返回后service被系统杀死,系统会重建service并且调用onStartCommand(),但系统调用onStartCommand()用的是一个空的intent,而不是service被杀死前传入的最后一个intent。
START_REDELIVER_INTENT
如果在onStartCommand()返回后service被系统杀死,系统会重建service,并用service被杀死前传入的最后一个intent再次调用onStartCommand()。
四、启动Started Service
可以在activity或其他组件中通过调用startService()启动service,系统会调用service的onStartCommand()方法(如果该service尚未运行,会先调用其onCreate()方法),例如:
Intent intent = new Intent(this, HelloService.class); startService(intent);
如果一个Started Service没有提供绑定功能,那么startService()就是应用组件与service之间通信的唯一函数。如果此时想让service向其他组件返回信息,可以在调用者组件中调用PendingIntent.getBroadcast()创建一个PendingIntent,并把它放在启动service的intent中传递给service;service就可以用这个broadcast向外返回结果。
通过startService()多次启动service,onStartCommand()会被相应的调用多次;但是,要结束此service,只需调用一次stopSelf()或stopService()就行了。
五、 结束Started Service
一个Started Service必须管理自己的生命周期(系统只有在必须回收内存、并且在onStartCommand()返回后service仍在运行时才强行将其销毁),因此必须通过调用stopSelf()或其他组件调用stopService()结束服务。
然而,如果同时有多个启动请求发送到onStartCommand(),不应该在处理完一个请求后调用stopSelf();因为在调用此函数销毁service之前,可能service又接收到新的启动请求,如果此时service被销毁,新的请求将得不到处理。为了避免这个问题,应该用stopSelf(int startId)代替stopSelf(),因为前者只有在其参数startId跟最后启动该service时生成的ID相等时才会执行;因此,当试图执行stopSelf(int startId)时,如果service已经收到了新的启动请求,startId和新生成的启动ID将不匹配,函数不会执行,service不会被销毁,就可保证新的请求能够得到处理。
注意:为了避免系统资源浪费和消耗电量,当service完成它的工作后,一定要记得将其停止;对于被绑定的service,如果其onStartCommand()曾被调用过,也要记得将其停止。
六、创建Bound Service
详见Android学习--Service之Bound Service
七、 向用户发送Notifications
详见关于"Toast Notifications"和"Status Bar Notifications"的文档。
八、 在前台运行Service
Foreground Service不会再内存不足时被系统杀死。一个foreground service必须向status bar发送一个通知;例如,一个播放音乐的service应该被放在前台运行,在status bar里的通知指示正在播放的歌曲、并可以通过它启动一个activity使用户能跟音乐播放器交互。
让service在前台运行,需调用startForeground()函数,它有两个参数:一个用于标示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);
要将service从前台移除,需调用stopForeground(boolean removeNotification)方法(参数指示是否将notification从status bar中移除);但是这个方法并没有结束此service。
如果当service在前台运行时将其结束,notification也会同时被移除。
九、管理Service的生命周期
Service的生命周期有两种情况:
*Started Service:当其他组件调用startService()时被创建,当调用其stopSelf()或其他组件调用stopService()时停止,之后被系统销毁。
*Bound Service: 当其他组件调用bindService()时被创建,当所有绑定的组件都解除绑定时,系统将其销毁。
这两种情况并不是截然分开的,一个通过startService()启动的service也可以接受绑定;在这种情况下,单有stopService()或stopSelf()并不能终止该service,而是得等到所有组件都解除对该service的绑定。
实现生命周期方法
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的生命周期方法不一样的是,在实现Service的生命周期方法时不需要调用父类对这些方法的实现。
通过实现这些生命周期方法,可以对service的以下两种寿命进行监管:
*entire lifetime:即从onCreate()被调用、到onDestroy()返回之间的时间。
*active lifetime:从onStartCommand()或onBind()开始。如果service是被启动的,就随entire lifetime一起结束;如果service是被绑定的,当onUnbind()返回时结束。
Figure 2. The service lifecycle.