原文地址 http://developer.android.com/guide/components/services.html
(按照个人理解进行翻译,方便以后查找资料,个人水平有限,如有错误,还请谅解)
翻译:
Services
Service是能够在后台执行耗时操作并且不提供用户界面的应用程序组件。其他应用程序组件可以启动service,甚至当用户使用其他软件时,service将继续在后台运行。此外,一个组件可以绑定到service,与其交互甚至执行进程间通信(IPC)。例如,一个service可能处理网络传输、播放音乐、执行文件I/O操作、或与content provider交互,所有都在后台运行。
一个service本质上可以分为两类:
Started
“started”--service是,当一个应用程序组件(例如一个activity)调用startService()启动它。一旦启动后,甚至启动它的组件销毁了,service也可以在后台一直运行。通常,一个“started”--service执行单一操作并且不返回任何东西给调用者。例如,service可能上传或下载一个文件。当操作完成,service应该自己停止自己。
Bound
“bound”--service是,当一个应用程序组件调用bindService()绑定到它身上。一个“bound”--service提供一个客户-服务器接口,通过这个接口,调用者可以与service进行交互,发送请求,返回结果,甚至是进程间通信。一个“bound”--service生命周期与绑定到它身上的应用程序组件相同。多个组件可以同时绑定到一个service上,但是只有它们全部解绑时,service才会销毁。
尽管这篇文档分开讨论这两种类型的service,你的service可以同时以两种方式工作----它可以被started(后台一直运行)并且允许绑定。这仅仅是你是否实现两个回调方法的问题:onStartCommand()允许组件启动service(“started”),onBind()允许组件绑定到service(“bound”)。
不管你的service(原文为application,这里我认为service更为妥当)是“started”,“bound”,或两者,就像任何组件可以使用Intent启动一个activity一样,任何应用程序组件可以使用Intent启动一个service(甚至是其他应用程序)。无论如何,你可以在manifest文件中声明service为当前应用程序私有以阻止其他应用程序的访问。这些将在Declaring the service in the manifest中进行更详细的讨论。
注意:一个service在它宿主进程(即当前应用程序的进程)的主线程中运行--标明service不会创建它自己的线程,不会运行在单独的进程中(除非我们指定)。这意味着,如果我们的service执行任何CPU密集型任务或阻塞操作(例如播放MP3或网络操作),我们应该创建一个新线程给service做这些工作。通过使用单独的线程,我们将减少ANR(应用程序无响应)错误的发生,然后应用程序主线程可以继续执行activity中用户交互的工作。
The Basics
为了创建一个service,我们必须创建一个Service(或一个已经存在的Service子类)的子类。在我们的子类中,我们需要覆盖一些处理service生命周期的关键的回调方法,并且如果需要,提供组件绑定到service的机制。最重要回调方法如下:
onStartCommand()
当其他组件例如activity,通过调用startService()请求启动service时,系统调用这个方法。一旦这个方法执行,service将被启动并能够一直在后台运行。如果我们实现这个方法,当它工作完成,我们负责停止service,通过调用stopSelf()或stopService()。(如果我们只提供绑定工作方式,我们不需要实现这个方法。)
onBind()
当其他组件想要通过调用bindService()绑定到service上(例如执行RPC(远程过程调用)),系统会调用这个方法。在我们实现这个方法时,我们必须通过返回一个IBinder,它提供一个客户与服务器(调用者与service)通信的接口。我们必须实现这个方法,但是如果我们不想考虑绑定工作方式,应该返回null。
onCreate()
当service第一次创建时,系统调用这个方法,执行 一次性处理程序(在系统调用onStartCommand()或onBind()之前)。如果指定service已经运行,这个方法不再被调用。
onDestroy()
当指定service不再被使用并且被销毁时,系统调用这个方法。我们的service应该实现这个方法去释放任何申请的资源,例如线程,注册的监听器,接收器等等。这是service接收的最后一个调用。
如果一个组件通过startService()启动service(导致调用onStartCommand()),那么这个service将持续运行直到它自己调用stopSelf()或其他组件调用stopService()来停止service。
如果一个组件通过bindService()来创建service(onStartCommand()不会被调用),那么这个service的生命周期将和绑定到它身上的组件相同。一旦这个service被所有客户解绑,系统会销毁它。
只有当内存很低并且具有用户焦点的activity必须重新获得系统资源时,Android系统才会强制停止一个service。如果这个service被绑定于具有用户焦点的activity上,那么它不太可能被销毁,如果它被声明在前台运行(后面讨论),那么它几乎不会被销毁。否则,如果这个service已经启动并且一直在后台运行,随着时间的推移,系统会降低它在后台任务列表中的位置(即降低优先级),它将非常容易被销毁----如果我们的service已经被销毁(原文是started,这里我理解应该为killed),我们必须设计使它稳定的被系统重启。如果系统销毁了我们的service,当资源再次可用时,它将尽快重启service(然而就像后面讨论的一样,这也需要依据onStartCommand()的返回值)。关于系统可能会销毁service时间的更多信息,请参考Processes and Threading文档。
在接下来的几部分,我们将学习如何创建每种类型的service,以及如何在其他应用程序组件中使用它们。
Declaring a service in the manifest
就像activity(和其他组件),我们必须在应用程序的manifest文件中声明所有的services。
为了声明我们的service,添加一个<service>节点作为<application>的子节点。例如:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
这里我们可以在<service>中包含其他属性,例如启动service需要的权限,service运行的进程。android:name是唯一必须的属性——它指定这个service的类名。一旦我们发布我们的应用程序,我们最好不要改变这个name,因为如果我们这么做,我们可能会破坏一些使用显示intent引用service的功能(可以读一读Things That Cannot Change这篇博客)。
关于在manifest文件中声明我们的service的更多信息,请查看<service>元素reference(这里就不译为“参考”了,感觉有点别扭,reference大家都懂得)。
就像activity,service可以定义intent filters用以允许其他组件使用隐式intent调用它。通过声明intent filters,安装在用户设备上的任何应用程序的组件都可能启动这个service,如果这个service声明的一个intent filter与其他应用程序传递给startService()的intent匹配。
如果我们打算只局部使用我们的service(其他应用程序不使用它),那么我们不需要(并且不应该)提供任何intent filters。除了任何intent filters,我们必须在intent中显示指定这个service的类名用以启动service。更多关于启动一个service的信息在下面讨论。
此外,只要我们包含android:exported属性并设置为“false”,就可以确保service为我们的应用程序私有。这点针对service提供intent filters同样有效。
更多关于为我们的service创建intent filters的信息,请查看Intents and Intent Filter文档。
Creating a Started Service
一个started service是其他组件通过调用startService()来启动它,导致调用service的onStartCommand()方法。
当一个service是started,它的生命周期独立于启动它的组件,并且可以一直在后台运行,甚至当启动它的组件被销毁时。同样地,这个service应该在完成工作时通过调用stopSelf()停止自己,或者其他组件可以通过调用stopService()停止它。
一个应用程序组件例如activity可以通过调用startService(),并传递一个指定这个service和包含其他任何service需要使用的数据的Intent,来启动它。这个service在onStartCommand()方法中接收这个Intent。
例如,假设一个activity需要保存一些数据给在线服务器。这个activity可以启动一个service并传递需保存的数据给startService()的参数intent。这个service在onStartCommand()中接收这个intent,连接Internet并进行数据传输。当传输完成,这个service停止它自己并被销毁。
注意:默认情况下,一个service运行在声明它的应用程序的进程的主线程中。因此,当用户与相同应用程序中的activity交互时,如果我们的service执行一些密集型的或阻塞的操作,这个service将降低activity的性能。为了避免影响应用程序的性能,我们应该在service中启动一个新的线程。
一般而言,我们可以继承以下两个类来创建一个started service:
Service
这是所有services的基类。当我们继承它时,创建一个新的线程执行所有service工作非常重要,因为默认情况下这个service使用我们的应用程序的主线程,这会降低应用程序里运行的activity的性能。
IntentService
这是Service的子类,它一次一个地使用一个工作线程处理所有启动请求。如果我们不需要在service中同时处理多个请求,这是最佳选择。我们所需做的就是实现onHandleIntent()方法执行后台工作,它接收每个启动请求传递过来的intent。
接下来的部分描述如何使用这些类实现我们的service。
Extending the IntentService class
因为大多数started services不需要同时处理多个请求(在多线程场景下非常危险),使用IntentService类实现我们的service可能是最好的。
IntentService主要做以下工作:
* 创建一个默认的独立于应用程序主线程的工作线程,执行所有传递到onStartCommand()中的intents。
* 在我们的onHandleIntent()实现里,传递一个intent用于创建一个工作队列,因此我们不必担心多线程的问题。
* 所有启动请求被处理后停止这个service,所以我们不必调用stopSelf()。
* 提供返回null的onBind()默认实现。
* 提供一个onStartCommand()的默认实现,它发送intent给工作队列,接下来给onHandleIntent()。
所有上面添加的都表明一个事实,我们需要做的就是实现onHandleIntent()执行客户提供的工作。(尽管如此,我们还需要提供一个service的构造函数。)
这里有一个实现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) {
}
}
}
}
}
这就是我们需要的:一个构造函数和一个onHandleIntent()的实现。
如果我们决定覆盖其他回调方法,例如onCreate(),onStartCommand(),或onDestroy(),一定要调用父类的实现,以至于IntentService可以管理工作线程的生命周期。
例如,onStartCommand()必须返回默认实现(这是intent如何传递给onHandleIntent()):
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
除了onHandleIntent()之外,onBind()是唯一一个我们不需要调用父类的方法(仅当service允许绑定时,我们才需实现)。
在下一个部分,我们将看到当继承Service基类时这种service是如何实现的,虽然代码增多,但当我们需要同时处理多个启动请求时可能很合适。
Extending the Service class
如同我们之前看到的一样,使用IntentService使我们实现一个started service变得非常简单。但是,如果我们需要我们的service执行多线程(而不是在一个工作队列处理启动请求),那么我们可以继承Service类来处理每一个intent。
相比之下,接下来的示例代码是一个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后如何重启它(就像上面讨论的一样,尽管我们可以修改它,默认的IntentService实现已经为我们做了这点)。onStartCommand()的返回值必须是以下常量之一:
START_NOT_STICKY
除非有pending intents(即还未处理的intent),否则当onStartCommand()返回后,如果系统销毁这个service,系统不会重新创建这个service。这是最安全的选择,以避免当不必要时或我们的应用程序可以简单的重启来完成未做完的工作,我们的service还在运行。
START_STICKY
如果系统在onStartCommand()返回后销毁这个service,那么会重新创建这个service并调用onStartCommand(),但是不会重新发送最后的intent。除非还有pending intents启动这个service,在这种情况下,这些intents会被传递,否则,系统传递一个空intent调用onstartCommand()。这对不执行命令但一直运行等待工作的多媒体播放器(或类似服务)而言很合适。
START_REDELIVER_INTENT
如果系统在onStartCommand()返回后销毁这个service,那么会重新创建这个service并且发送最后的intent给这个service调用onStartCommand()。任何pending intents会被轮流发送。这对于正积极的执行工作并应该立即恢复的服务(例如下载一个文件)而言很合适。
更多关于这些返回值的详述,请查看每个常量的reference文档。
Starting a Service
我们可以从一个activity或其它应用程序组件,通过传递一个Intent(指定启动的service)给startService(),来启动一个service。Android系统调用这个service的onStartCommand()方法,并将intent传递给它。(我们不应该直接调用onStartCommand()。)
例如,一个activity可以传递一个显示的intent给startService(),来启动前面部分的示例服务(HelloService):
Intent intent = new Intent(this, HelloService.class);
startService(intent);
这个startService()方法会立即返回,然后Android系统调用service的onStartCommand()方法。如果这个service还未运行,系统会先调用onCreate(),然后调用onStartCommand()。
如果这个service也不提供绑定,通过startService()传递过去的intent,是应用程序组件和service之间唯一通信方式。然而,如果我们想要这个service返回一个结果,那么启动service的客户端(各种组件)可以创建一个PendingIntent给一个broadcast(使用getBroadcast()获取),并将它递送给启动service的Intent。这个service接下来可以使用这个broadcast发送一个结果。
多个启动service的请求,导致同时多个onStartCommand()的调用。然而,只需一个请求来停止这个service(使用stopSelf()或stopService())。
Stopping a service
一个started service必须自己管理它的生命周期。就是,系统不会停止或销毁这个service,除非它必须恢复系统内存并且在onStartCommand()返回后继续运行。因此,这个service必须通过调stopSelf()停止自己,或者其它组件通过调用stopService()也能停止它。
一旦使用stopSelf()或stopService()请求停止,系统会尽快销毁这个service。
无论如何,如果我们的service在onStartCommand()中同时处理多个请求,那么我们不应该在完成一个start请求时停止这个service,因为我们可能已经接收到一个新的start请求(在第一个请求完成是停止将会终止第二个请求)。为了避免这个问题,我们可以使用stopSelf(int)来确保我们的停止请求,总是基于最近的start请求。那就是,当我们调用stopSelf(int)时,参数与start请求ID(传递给onStartCommand()的startId)一致。接下来,在我们可能会调用stopSelf(int)之前,如果这个service接收一个新的start请求,那么这个ID不匹配,service就不会停止。
注意:为了避免浪费系统资源和消耗电量,在service完成工作后停止它是非常重要的。如果必要,其他组件可以通过调用stopService()停止这个service。甚至当这个service使用绑定方式时,如果曾经有过一次onStartCommand()调用,我们必须总是自己停止这个service。
关于一个service的生命周期的更多信息,将会在下面Managing the Lifecycle of a Service部分看到。
Creating a Bound Service
一个bound service是,为了创建一个长时间-稳定的连接,允许应用程序组件通过调用bindService()绑定到它身上(并且一般情况下,不允许组件通过调用startService()启动它)。
当我们的应用程序里activity或其他组件需要与service交互时,或通过IPC(进程间通信)开放一些我们应用程序里的功能给其他应用程序使用时,我们应该创建一个bound service。
为了创建一个bound service,我们必须实现onBind()回调方法,并返回一个定义了和service通信的接口的IBinder。接下来,其他应用程序组件可以调用bindService()获得这个接口并且开始调用service里面的方法。这个service只存活于绑定到它身上的组件,因此当没有组件绑定到它身上时,系统会销毁它(我们不需要用停止started service的方式,停止一个bound service)。
为了创建一个bound service,首先,我们必须做的事是,定义客户端与service通信的接口。这个service与客户端的接口,必须是一个IBinder的实现,并且是我们service里面onBind()回调方法的返回值。一旦客户端收到这个IBinder,它可以通过这个接口开始与service交互。
多个客户端可以同时绑定到这个service上。当一个客户端完成与service的交互,它调用unbindService()完成解绑定。一旦没有客户端绑定到service上,系统会销毁这个service。
有多种方式实现一个bound service,并且这些实现比一个started service更复杂,因此关于bound service的讨论出现在单独的文档Bound Service中。
Sending Notifications to the User
一旦开始运行,一个service可以使用Toast Notifications或者Status Bar Notifications通知用户相关事件的发生。
一个toast notification是一个消息,它在当前窗口的界面上显示一段时间然后消失,而一个status bar notification在状态栏中提供一个消息的图标,用户可以选择它以执行一个动作(例如启动一个activity)。
通常而言,当一些后台工作完成(例如完成一个文件的下载),status bar notification是最好的技术,此时用户可以做一些事情。当用户从下拉视图中选择这个通知消息时,这个通知消息可以启动一个activity(例如查看已下载的文档)。
请查看Toast Notifications或Status Bar Notifications开发中向导获取更多信息。
Running a Service in the Foreground
一个前台service是用户自己知道的一些东西,因此当内存很少时,不在系统销毁队列中。一个前台service必须提供一个状态栏的通知信息,位于“Ongoing”heading(不知怎么翻译)下面,意味着这个通知信息不会消失,除非这个service从前台停止或移除了。
例如,一个从service播放音乐的音乐播放器,应该被置于前台运行,因为用户很清楚的知道它的工作。在状态栏中的通知信息可能指出当前播放的歌曲,并且允许用户启动一个activity与音乐播放器交互。
调用startForeground()来请求我们的service在前台运行。这个方法有两个参数:一个整数,它能够唯一识别这个通知信息,另一个是状态栏上的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。这个方法有一个boolean参数,标明是否同时从状态栏上移除通知信息。这个方法不会停止service。然而,当它还在前台运行时,我们停止这个service,那么通知信息也会被移除。
注解:这里的startForeground()和stopForeground()方法是在Android 2.0(API Level 5)时被引入的。在更早版本的平台上,为了让我们的service在前台运行,我们必须使用之前版本的setForeground()方法——查看startForeground()文档获取关于如何提供向后兼容的信息。
想要更多关于notifications的信息,请查看Creating Status Bar Notifications
Managing the Lifecycle of a Service
一个service的生命周期比一个activity简单多了。无论如何,我们了解service是如何被创建和销毁是非常重要的,因为一个service可以在用户不知道的情况下在后台运行。
service的生命周期——从创建到销毁——可以沿着以下两条路线:
* 一个started service
这个service是被其他组件调用startService()创建的。这个service之后一直运行,并且必须被它自己调用stopSelf()停止。其他组件也能通过调用stopService()停止它。当这个service被停止了,系统会销毁它。
* 一个bound service
这个service是当其他组件(客户端)调用bindService()时创建的。接下来客户端可以通过一个IBinder接口与service通信。客户端可以通过调用unbindService()关闭这个连接。多个客户端可以绑定到同一个service上,并当所有客户端解绑定时,系统销毁这个service。(这种service不需要停止它自己。)
这两条路线不是完全分开的。就是说,我们可以绑定到一个已经使用startService()启动的service上。例如,一个后台音乐服务,有可能通过传递一个指定播放音乐的Intent,给startService()调用启动。之后,有可能当用户想要获取一些播放器的控制权或得到当前歌曲的信息时,一个activity可以通过调用bindService()绑定到service上。在这种情况下,直到所有客户端解绑定,stopService()和stopSelf()才会实际上停止这个service。
Implementing the lifecycle callbacks
就像activity,我们可以实现一些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 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
}
}
注意:不像activity生命周期回调方法,我们不需要在这些回调方法中调用父类的实现。
图2. service的生命周期。这张图的左边表示使用startService()创建的service的生命周期,右边表示使用bindService()创建的service的生命周期。
通过实现这些方法,我们可以监视service生命周期的两个嵌套循环:
* 一个service的整个生命期,从onCreate()调用到onDestroy()返回。就像activity,一个service在它的onCreate()中做初始化工作,并在onDestroy()中释放所有获取的资源。例如,一个音乐播放器service可能在onCreate()里创建一个新线程播放音乐,随后在onDestroy()中停止这个线程。
onCreate()和onDestroy()方法可被所有service调用,无论它是被startService()还是被bindService()调用创建的。
* 一个service的活动期开始于onStartCommand()或onBind()调用。每个方法单独处理传递给startService()或bindService()的Intent。如果这个service已经启动,活动期与整个生命期同时结束(这个service在onStartCommand()返回后仍然是活动的)。如果这个service是bound类型,它的活动期结束于onUnbind()返回。
注意:一个started service被stopSelf()或stopService()调用停止,没有其他回调来停止这个servie(没有onStop()回调)。因此,除非这个service被绑定到一个客户端,否则当service被停止时——onDestroy是唯一接受的回调,系统会销毁它。
图2表明一个service的典型回调方法。尽管图中将使用startService()和bindService()创建的service分开了,牢记任何一个service,无论它是怎么被创建的,都有可能允许客户端绑定到它身上。因此,一个使用onStartCommand()(通过客户端调用startService())初始启动的service,仍然能接收onBind()调用(当客户端调用bindService())。
想要更多关于创建一个bound service的信息,请查看Bound Services文档,在Managing the Lifecycle of a Bound Service部分包含更多关于onRebind()回调方法的信息。