Service
Service继承了ContextWrapper,实现了componentCallbacks2接口
概述
Service是应用程序的一个组件,用来实现应用程序中不与用户进行交互而且耗时的操作,或者用来支持其他应用程序的一些继承服务或功能。应用中每一个Service都必须相应的在AndroidManifest.xml文件中通过< service >标签进行声明。Services通过调用Context.startService()或Context.bindService()启动。
注:Service和应用程序中的其他对象一样,在主线程中运行,也就是说当Service执行一些耗费CPU资源的(Mp3后台播放)或者阻塞操作(网络操作)时,必须内部单独开一个线程执行相应的操作。详情可以参考进程与线程。IntentService类是Service的一个标准实现,它拥有自己的线程来执行相应的操作。
Service的定义:
Service并不是一个单独的进程,Service对象并不表示它所存在的进程,除非特别的指定,它存在应用程序的进程当中。
Service也不是一个线程,也意味着它所做的任务在主线程中执行(所以要避免ANR)
当Service组件被创建后,系统会将会在主线程对其进行初始化,调用onCreate()方法,和其他相应的回调方法。Service所做的工作主要通过在回调方法中进行实现,比如创建一个辅助线程进行任务操作。
注:Service对象本身很简单,开发者可以根据需求执行前台交互的工作,或者仅仅针对一个Java对象进行相应方法的回调,或者使用AIDL提供一个远程接口。
Service生命周期:
有两种方法来启动Service。
1. 调用Context.startService(),然后系统会创建对象并回调Service.onCreate()方法,接着回调Service.onStartCommand(Intent, int, int)方法,方法中的参数有开发人员传入;然后Service会一直在后台运行至调用Context.stopService()和Service.stopSelf()。Context.startService()不会被嵌套,不论调用多少次Context.startService()方法,当Context.stopService()和Service.stopSelf()被调用时,Service就会停止。Service可以调用Service.stopSelf()方法来确保直到意图被处理完后再停止Service.
在启动Service时,有两种可选的操作模式,取决于Service.onStartCommand()的返回值
- START_STICKY:代表根据需要显示的开始和停止Service
- START_ NOT_STICKY :
- START_ REDELIVER_INTENT:代表Service将一直运行至接收到相应的命令。
- 调用Context.bindService(),与Service打开一个持久连接,当Service不存在时进行创建,然后调用onCreate(),而不调用onStartCommand();与Service创建连接的对象将接收一个Service调用onBind()方法返回的IBinder对象,通过这个对象,可以回调Service内的方法。当连接被创建后,Service将一直处于运行状态,不论IBinder对象是否被引用。通常情况下,返回的IBinder对象是一个实现在aidl的复杂的接口。
Service可以同时被启动和绑定,在这样的情况下,Service将会一直运行只要有一个连接存在,这些连接添加了Context.BIND_ AUTO_CREATE标志。当上述的条件不满足时,Service将调用onDestroy()方法,进行销毁,所有的清理操作必须在onDestroy()返回前完成,比如终止线程,注销Receivers等等。
权限
如果想让其他应用能访问Service,必须在声明它的AndroidManifest.xml文件中进行配置,而想要调用该服务的应用也必须在其AndroidManifest.xml文件中进行权限的声明。
在GINGERBREAD版本中,当调用Context.startService(Intent)时,可以为传入的intent设置参数,赋予相应的权限,权限将保留着Service调用stopSelf(int)的命令或者更高层次的命令。或者Service已经被停止了。授予其他应用接入该Service并不要求权限来进行保护,甚至也不需要将Service声明wei exported。
另外,Service通过权限保护单独的进程间通信的回调。通过调用checkCallingPermission(String)方法在执行相应的回调方法之前。
执行生命周期:
当Service启动或者被绑定时,Android系统试图通过进程托管Service。当处于低内存或者需要杀死现存的进程时,进程托管Service的优先级将提升,处于以下的可能性时:
1. 当Service正在执行onCreate(),onStartCommand(),onDestroy()方法时,托管Service的进程会变成一个前台进程直到确保代码执行完之前不被杀死。
2. 当Service已经启动,托管Service的进程被认为重要性低于任何一个和在屏幕上用户可见的进程,却高于任何一个不可见的进程时。毕竟用户可见的进程很少,这就意味着,只有在内存极低的情况下Service才会被杀死。
3. 当Service被绑定后,托管Service的进程重要性高于绑定它的所有对象要高,换句话说,当绑定该Service的对象对用户可见,也就意味着Service对用户可见。
4. 启动的Service可以使用startForeground(int,Notification)接口,使Service处于前景状态,在这个状态下,系统认为用户会关注这个Service或者说Service将在低内存时不在被杀列表中。
注:当Service在极低内存的情况被杀死后,系统会在内存满足条件的情况下重启Service。一个重要的方法是在onStartCommand()中开启一个异步任务或者开启一个辅助线程执行工作,如果你希望Service被杀死时,任务继续执行。
其他应用的组件运行和Service运行在同一个进程中时,也可以提示托管Service的进程。
本地Service用例:
LocalService
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 the expanded 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 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.setLatestEventInfo(this, getText(R.string.local_service_label),
text, contentIntent);
// Send the notification.
mNM.notify(NOTIFICATION, notification);
}
}
使用LocalService的对象
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();
}
远程信息服务用例:
MessengerService
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);
// 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 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 android:name=".app.MessengerService"
android:process=":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.");
}
}