Class Overview
Service是一个应用程序组件,表示一个长时间运行的操作并且不与应用程序交互或者为其它应用程序提供功能。每一个Service都必须在AndroidManifest.xml文件中声明标签。Service能够通过两种方式启动,Context.startService()
和Context.bindService()
.
注意:Service和其它应用程序组件一样,运行在主进程中。这意味着,如果服务打算做任何CPU密集型(如MP3播放)或阻塞(如网络)业务,它应该开启一个新的线程做这些工作。更多这方面的信息可以在Processes and Threads找到。IntentService类是作为一个标准的实现服务有自己的线程,调度工作去做。
Developer Guides
What is a Service?
关于Service类要先了解它不是什么:
Service不是一个单独的线程,Service对象不意味着它拥有自己的进程,除非特殊说明,它与应用程序的进程相同,作为应用程序的一部分。
Service不是线程,它意味着服务运行在主线程中(应该考虑避免出现程序无响应错误)。
大多数关于服务类的混乱实际上是围绕着它不是什么:
1.服务不是一个单独的过程。服务对象本身并不意味着它正在运行在它自己的进程中;除非另有说明,它在同一个进程中运行,作为应用程序的一部分。
2.服务不是线程。它不是一个意思本身做工作的主要线程(以避免应用程序没有响应错误)。
因此,服务本身很简单,主要提供了2个主要功能:
1.作为应用程序的后台操作(用户不与应用程序直接进行交互)。这时通过Context.startService()开启服务。这就告诉系统它自己安排工作,一直运行知道Service本身或者其它组件停止服务。
2.为其它应用程序提供功能,这是通过
Context.bindService()调用
, 它允许一个长期的连接可以与服务保持交互。
当一个服务组件被创建,不是这些原因,那么实际上系统实例化组件并调用它的oncreate()和主线程的任何其他适当的回调。用适当的行为来实现这些服务,比如创建一个二级线程,在它完成它的工作。
注意,因为服务本身就是这么简单,你可以让你的交互简单或复杂:你想从把它作为一个地方的java对象,你直接调用方法(由当地服务的示例如下所示),提供一个使用AIDL全可进行远程操作的接口。
当一个服务组件实际上是创造的,不是这些原因,那么实际上系统实例化组件和调用它的oncreate()和主线程的任何其他适当的回调。用适当的行为来实现这些服务,比如创建一个二级线程,在它完成它的工作。
注意,因为服务本身就是这么简单,你可以让你对它的简单或复杂的:你可以把它作为一个本地的java对象,直接调用方法(本地Service如下实例所示Local Service Sample),或者提供一个使用AIDL全局远程操作的接口。
Service Lifecycle
让一个服务在系统中运行有两个原因,如果调用Context.startService()系统将会开启服务(创建并调用onCreate()方法)然后回调onStartCommand(Intent, int, int)
方法参数由客户端提供。Service将会一直运行直到调用Context.stopService()
或 stopSelf()
。注意,多次调用Context.startService()不会再次创建(尽管它会多次调用onStartCommand(Intent, int, int)
方法)。因此不管你开启了多少次服务,停止一次Context.stopService()
or stopSelf()
就可以了。然而,服务可以使用他们的 stopSelf(int)方法,确保服务不停止,直到意图已经处理。
启动一个服务,有有两种额外的操作模式,他们可以根据
onStartCommand()的返回值决定运行。START_STICKY
用于显示地根据需求启动和停止服务。而START_NOT_STICKY
or START_REDELIVER_INTENT
被用于仅当进程向他们发送指令时才运行。详细请参照相关文档。
客户端也可以通过Context.bindService()来获得一个持久连接到服务。如果服务尚未运行,同样会创建一个服务(调用onCreate()方法),但是不会调用
onStartCommand()方法。客户端将会收到一个IBinder
对象通过它的onBind(Intent)方法,使客户端回调服务。
服务将持续保持一个连接(不管客户端是否引用服务的IBinder对象)。通常IBinder返回一个复杂的接口,使用aidl编写。
服务可以同时启动并绑定到它,在这种情况下,系统将保持服务运行,只要它被开启,或者有一个或多个连接到它通过Context.BIND_AUTO_CREATE标志。一旦这两个条件都不成立,Service将执行onDestroy()方法并且服务将有效的终止,所有清理(包括停止线程,取消注册接收器)都需在
onDestroy()完成。
Permissions
全局访问服务必须在manifest's 标志中声明。通过这样做,其它应用程序需要在声明权限,才能启动,停止,绑定服务。
在姜饼后的版本中,你可以使用Context.startService(Intent),你也可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION
and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
在Intent中。这将给予服务临时访问权限特定的URIs在Intent中。访问将一直继续,直到Service回调 stopSelf(int)方法,当开始执行命令,或者有更晚的一个。或者直到服务完成。这将给予其它应用程序访问权限
没有要求许可保护的服务,甚至服务不是出口。
此外,一个服务可以保护个人IPC调用权限,通过调用checkCallingPermission(String)方法在执行调用的实现之前。
有关更多信息,请参见Security and Permissions 。
Process Lifecycle
Android系统将试图保持服务运行只要服务已经启动,或者有客户端绑定到它。当内存不足时,需要耍死存在的进程。服务将高于以下情况:
1.如果服务正在执行onCreate()
, onStartCommand()
, or onDestroy()方法中的代码,它持有的进程将会
是一个前台进程,以确保这段代码可以执行没有被杀。
2.如果服务已经启动,那么它持有的进程的优先级低于那些可见的进程。但是比那些不可见的进程优先级高。因为只有几个过程是用户可见的,这意味着服务不会被杀死除非内存很低。然而,由于用户没有直接与后台服务交互,在系统中也有可能会被列为杀死的对象。你应该为此做好准备。特别是,长时间运行的服务将越来越有可能杀死并保证被杀(如果条件适当并重新启动)如果他们保持足够长的时间。
3.如果有客户绑定到服务,然后服务的托管过程会和客户端的进程一样重要。也就是说,如果它的一个客户是用户可见的,那么服务本身被认为是可见的。客户的重要性影响服务的重要性可以通过BIND_ABOVE_CLIENT
, BIND_ALLOW_OOM_MANAGEMENT
,BIND_WAIVE_PRIORITY
, BIND_IMPORTANT
, and BIND_ADJUST_WITH_ACTIVITY调整
。
4.可以通过startForeground(int, Notification)开启一个前台服务。
系统认为它是用户关注的,因此不适合低内存时死亡。(理论上它仍然是可能被杀死在极端的内存压力下从目前的前台应用程序,但实际上这应该不是一个问题。)
注意这意味着大部分时间你的服务正在运行,它可能被系统杀死在内存不够的时候。如果发生这种情况,系统将稍后尝试重新启动服务。重要的是,如果你实现了一个异步或者另外一个前程处理任务。那么你可以使用START_FLAG_REDELIVERY在Intent中调用。当系统被杀的时候处理它。
其它应用程序组件与服务运行在同一进程中(比如Activity)。当然,增加整个进程的重要性比仅仅是服务本身的重要性。
Local Service Sample
最常用的就是服务作为一个次要组件作为应用程序的一部分。与应用程序中的其它组件在同一进程中。一个应用程序的所有组件运行在同一进程中除非显示地声明,这就是一个典型的情况。
在这种情况下。组件是在同一进程中的,可以大大简化它们之间的交互:客户端可以通过IBinder来与service进行交互。
这是服务使用的一个例子,首先是Service类,客户端可以绑定到它:
public class LocalService extends Service {
private NotificationManager mNM;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION = R.string.local_service_started;
/**
* Class for clients to access. 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 LocalService.this;
}
}
@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Display a notification about us starting. We put an icon in the status bar.
showNotification();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(NOTIFICATION);
// Tell the user we stopped.
Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.local_service_started);
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LocalServiceActivities.Controller.class), 0);
// Set the info for the views that show in the notification panel.
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.drawable.stat_sample) // the status icon
.setTicker(text) // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle(getText(R.string.local_service_label)) // the label of the entry
.setContentText(text) // the contents of the entry
.setContentIntent(contentIntent) // The intent to send when the entry is clicked
.build();
// Send the notification.
mNM.notify(NOTIFICATION, notification);
}
}
上述完成后,现在可以写一个客户端代码直接访问服务,比如:
private LocalService mBoundService;
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 service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mBoundService = ((LocalService.LocalBinder)service).getService();
// Tell the user about this for our demo.
Toast.makeText(Binding.this, R.string.local_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mBoundService = null;
Toast.makeText(Binding.this, R.string.local_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(Binding.this,
LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
Remote Messenger Service Sample
如果你想写一个服务可以与客户端进行远程通信(比仅仅是通过使用Context.startService
开启服务),你可通过 Messenger
类代替完整的AIDL文件。
一个通过Messenger实现客户端接口的例子如下,首先是Service类,发布一个Messenger到一个内部的Handler当绑定时。
public class MessengerService extends Service {
/** For showing and hiding our notification. */
NotificationManager mNM;
/** Keeps track of all current registered clients. */
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
/** Holds last value set by a client. */
int mValue = 0;
/**
* Command to the service to register a client, receiving callbacks
* from the service. The Message's replyTo field must be a Messenger of
* the client where callbacks should be sent.
*/
static final int MSG_REGISTER_CLIENT = 1;
/**
* Command to the service to unregister a client, ot stop receiving callbacks
* from the service. The Message's replyTo field must be a Messenger of
* the client as previously given with MSG_REGISTER_CLIENT.
*/
static final int MSG_UNREGISTER_CLIENT = 2;
/**
* Command to service to set a new value. This can be sent to the
* service to supply a new value, and will be sent by the service to
* any registered clients with the new value.
*/
static final int MSG_SET_VALUE = 3;
/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_VALUE:
mValue = msg.arg1;
for (int i=mClients.size()-1; i>=0; i--) {
try {
mClients.get(i).send(Message.obtain(null,
MSG_SET_VALUE, mValue, 0));
} catch (RemoteException e) {
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Display a notification about us starting.
showNotification();
}
@Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(R.string.remote_service_started);
// Tell the user we stopped.
Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show();
}
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.remote_service_started);
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, Controller.class), 0);
// Set the info for the views that show in the notification panel.
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.drawable.stat_sample) // the status icon
.setTicker(text) // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle(getText(R.string.local_service_label)) // the label of the entry
.setContentText(text) // the contents of the entry
.setContentIntent(contentIntent) // The intent to send when the entry is clicked
.build();
// Send the notification.
// We use a string id because it is a unique number. We use it later to cancel.
mNM.notify(R.string.remote_service_started, notification);
}
}
如果我们想让服务运行在远程的进程中(不是一个标准的在同一个apk当中),我们可以通过manifest 标签android:process来声明。
<service android:name=".app.MessengerService"
android:process=":remote" />
注意:"remote"这个名字在这里是任意的,如果你愿意,你可以使用其它的名字来指定附加进程。 ':'前缀副驾驶那个你的包名是一个标准的进程名称。
上述完成以后,客户端可以绑定到服务并且向其发送消息。注意这也要让客户端注册消息并接收。
/** Messenger for communicating with service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mIsBound;
/** Some text view we are using to show state information. */
TextView mCallbackText;
/**
* Handler of incoming messages from service.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_SET_VALUE:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* 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 service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = new Messenger(service);
mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
Message msg = Message.obtain(null,
MessengerService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
// Give it some value as an example.
msg = Message.obtain(null,
MessengerService.MSG_SET_VALUE, this.hashCode(), 0);
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();
}
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;
mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(Binding.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because there is no reason to be able to let other
// applications replace our component.
bindService(new Intent(Binding.this,
MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null,
MessengerService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}