翻译一下Service.java的API文档----哎哟,我去,公司终于可以发文章了
简介
service是应用相关组件,主要用于应用要做一个长时间运行的动作,或者不需要跟用户交互或为其他应用提供功能。每一个service,都必须在AndroidManafest.xml中定义,
一个service,通过Context.startService()或Context.bindService()被启动。
service在main thread中运行,这意味着service,可以做CPU密集型操作(MP3后台播放)或阻塞操作(比如网络操作),service可以自己创建线程去完成它的任务。
更新信息,可以在 ApplicationFundamentals:Processes and Threads中找到。比如IntentService.java是一个标准的利用它自己的线程去实现其功能的service实现类
主题
1. 什么是service
2. service生命周期
3. permissions
4. Process生命周期
5. 本地 service 范例
6. 远程信息service范例
一、什么是service
大多数关于service的疑问都围绕着它不是什么
1. 服务不是一个单独的进程。服务对象本身并不意味着它在自己的进程中运行。除非有其他说明,它同在一个进程中运行,作为应用程序的一部分。
2. 服务不是线程。它不可以脱离主线程进行工作的(以避免应用程序无响应错误)
其实服务本身很简单,它只提供了两个主要功能:
1. 应用要在后台做什么事情(甚至用户并没有跟应用直接交互)。这就调用Context.startService(),这要求系统安排工作,直到运行到service或者人为的停止
2. 将其一部分功能应用到其他应用上。这就调用Context.bindService()。这允许长时间保持连接为了服务与它交互。
当service组件被创建之后,不管是通过bindService或者startService创建的,系统都将会实例化service,并调用onCreate,以及其他适当的回调。这取决于service实现了什么样的功能行为,比如创建一个在service内部创建了二级线程
注意:service本身是简单,你可以用local java object来对待它,或者通过aidl远程调用这种复杂的方式。
二、service生命周期
service的运行有两种方式。Context.startService()或者Context.bindService().
1.Context.startService:系统将会取回service(这可能会创建它同时调用onCreate()方法),然后调用onStartCommand(Intent,int, int)方法(参数由客户端提供)。service会一直运动到Context.stopService()或stopSErlf()被调用。
注意:多次调用Context.startService不会启动多个service,但是这会多次调用onStartCommand(),一旦Context.stop()或stopSelf()被调用,就会立即停止service。
stopSelf()会保证onStartCommand中的方法被执行完毕,才会停止service。
在启动service后,service主要有两种操作模式,这取决于onStartCommand的返回值。
START_STICKY:显式开启关闭service
START_NOT_STICKY|START_REDELIVER_INTENT:用于仅在接受到的指令时才运行。
2.Context.bindService:这会获得一个长久的连接服务。如果service没在运行(那会调用onCreate),但不会调用onStartCommand。service会通过onBind(Intent)函数返回一个IBinder给客户端,这允许客户端做一些连接创建后的回调。只要连接被建立,ervice会保持运行(不管客户端还是否保留服务的IBinder)。通常IBinder会被写在一个复杂的interface AIDL中。
service可以同时被started和有多个跟它相连的连接。在这种情况下,只要它被started有一个或多个用Context.BIND_AUTO_CREATE标签的连接,service会一直运行。一旦不存在连接或被stop,系统最终会调用onDestroy(),这样service就正式有效终止。所有的清理动作(stopping threads, unregisteringreceivers)应该在onDestroy之前被处理。
Permissions
全局访问一个service,需要在manifest中定义<service>标签。其他应用需要添加相应的<uses-permission>。
在android2.3版本中,若使用Context.startService(),需要同时设置intent的flag成Intent.FLAG_GRANT_READ_URI_PERMISSION and/orIntent.FLAG_GRANT_WRITE_URI_PERMISSION。
这会给让service有暂时访问指定URI的权限。权限会一直保持到service调用stopSelf,或者一直到service完全被系统停止。
这种方式可以给予对其他apps中未添加permission保护的service的访问权限(even when the service is not exported at all)。
此外,通过调用checkcallingpermission(string),service可以限制个别IPC 对它的调用权限。
进程优先级
android系统会一直保持 持有service的进程运行,只要service一直被启动或者有客户端接连上。当内存不足需要杀进程时,持有service的进程的有以下几种可能。
1. 如果service正在执行onCreate,onStartCommand,onDestory函数,这进程会被确保执行完毕
2. service已被启动,但优先级比正与用户交互的可见进程低,比不可见的进程高。那么,这个进程就不应该被killed,除非达到extreme low memory的情况
3. 如果客户端绑定上来service,那么持有service的进程就跟最重要的进程一样重要,因为只要有一个客户端是可见的,service就应该被视为可见的
4. 当系统考虑将service是活跃的同时不是可杀的候选进程,那么已启动的service可以用startForeground函数将service改成前显得状态。(在极端的情况下,这样也仍有可能被杀掉)
注意:在内存压力比较大的时候,即便service大多数时间在运行的,也会被杀掉。如果发生这种情况,系统会稍后尝试重新启动service。如果你正在onStartCommand()中安排任务,那么你就可能需要添加START_FLAG_REDELIVERY让系统在重新启动service再次派发这个intent。这样确保service在执行这个intent指令时被杀掉后还能重新执行。
其他的应用组件(比如Activity)也会增加整个进程的重要性。
本地Service范例
service其中一种比较常用的典型方法就是应用程序的辅助组件。APK的所有组件都运行在同一个进程中,除非另有明确规定。
采用这种方式,会大大简化应用程序组件之间的交互复杂性:app的组件可以通过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);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_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 theexpanded notification
CharSequence text = getText(R.string.local_service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample,text,
System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects thisnotification
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.setLatestEventInfo(this,getText(R.string.local_service_label),
text, contentIntent);
// Send the notification.
mNM.notify(NOTIFICATION, notification);
}
}
第一步完成后,客户端代码可以直接访问运行中的service
private LocalService mBoundService;
private ServiceConnection mConnection = newServiceConnection() {
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 sameprocess, 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 范例
如果你需要写一个service与远程客户端通信(不仅仅是context.startservice发送命令的使用),也可以用Message类代替AIDL
以下就是使用Messager作为clients接口的范例。首先是service本身在绑定时发布一个messager到内部handler。(额外加,其实这里的arraylist只是保存ibinder)
public class MessengerService extendsService {
/** 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'sreplyTo 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 receivingcallbacks
* from the service. The Message'sreplyTo 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 isdead. Remove it from the list;
// we are goingthrough the list from back to front
// so this is safeto 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 theexpanded notification
CharSequence text = getText(R.string.remote_service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample,text,
System.currentTimeMillis());
// The PendingIntent to launch ouractivity 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.setLatestEventInfo(this,getText(R.string.remote_service_label),
text, contentIntent);
// 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);
}
}
如果我们想要让这个service运行在远程进程中(而不是apk本身),我们就需要在manifest中标记。android:process
<serviceandroid:name=".app.MessengerService"
android:process=":remote" />
这里的remote的名字是任意的。如果你想要其他进程,需要使用其他名字。: 前缀附加你package的标准进程名字。
这样,客户端就可以绑定上service,同时允许收发信息。
/** Messenger for communicating withservice. */
Messenger mService = null;
/** Flag indicating whether we have calledbind on the service. */
boolean mIsBound;
/** Some text view we are using to showstate 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 = newMessenger(new IncomingHandler());
/**
*Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = newServiceConnection() {
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 weneed to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
总结:
Context.startService和Context.bindService的侧重点是不一样的。
区别:
1. 侧重点不一样
Context.startService(): 在后台执行的任务上
Context.bindService(): 将一部分功能扩展到其他应用上。
2. 生命周期不同
onCreate--->onStartCommand--->stopService/stopSelf
多次startService--->多次onStartCommand--->1个stopService/stopSelf
onCreate--->onBind--->onUnBind--->onDestroy
只要被started或者有多个绑定,就会一直运行,直到被stop或者没有连接
共同点:都是后台长时间运行
全局访问的service,要在manifest中定义,其他应用要访问需要有相应的permission。
service可以通过checkCallingPermission来限制个别IPC对它的访问。
service的在执行onCreate onStartCommand onDestroy时不会被终止。
service的优先级取决了绑定它的clients
可以通过startForeground来保持service的不被杀掉,但是在内存压力很大的时候依旧会被杀,可以通过FLAG可以保证service在系统内存不那么紧张的时候,会恢复。