权限:
当服务在manifest的<service>标签里声明时,那么这个服务就可以被全局访问执行。通过这样做,其他的应用程序可以在自己的manifest文件中通过声明相应的<use-permission>来开启,停止或绑定到这个服务。
自android2.3起,当使用Context.startService(intent)时,你可以通过设置Intent(意图).FLAG_GRANT_READ_URI_PERMISSION
和/或者
Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这将会授予服务临时的权限到指定Uri的意图。这个权限将会一直保持,直到服务在开启命令或稍后调用了stopSelf(),或者服务已经完全停止了,权限才会解除。这个工作用于授予访问其他应用程序的权限,并不需要请求一个权限来保护服务,即使这个服务没有暴露个其他应用程序。
此外,服务可以保护个人进程间通信的调用权限,通过在执行调用实现前
调用checkCallingPermission(String)方法。
关于更多有关权限和安全的信息可以查看Security and Permissions。
进程生命周期:
只要服务一旦开启或者有客户(client)绑定到它,那么安卓系统将会尝试着使进程保持持有这个服务。当应用运行在低内存或者需要杀死现有的进程时,那么持有服务进程的优先级将在下面几种情况下升高:
如果服务当前已经在它的onCreate(),onStartCommand(),onDestroy方法里执行了代码,那么持有这个服务的进程将会成为前台进程,以确保代码能够执行而没有被杀死;
- 如果服务已经开启,那么持有这个服务的进程将比那些在屏幕上对于用户可见所在的进程要次要,但是比不可见的进程要重要。因为仅有少数进程对于用户是可见的,这意味着服务只有在低内存的情况下才会被杀死。然而,用户并不能直接意识到后台的服务,在这种状态下,服务将被考虑成为一个有效的候选者杀死。特别是,长期运行的服务将增加被杀死的可能性以及如果他们保持足够长的时间,将确保他们能够杀死(或适当的重启)
- 如果一个客户绑定到服务,那么持有服务的进程与最重要的客户相比并不是次要的。也就是,如果其中的一个客户是可见的,那么这个服务也将被认为是可见的。客户(client)的重要性可以通过
BIND_ABOVE_CLIENT
,BIND_ALLOW_OOM_MANAGEMENT
,BIND_WAIVE_PRIORITY
,BIND_IMPORTANT
,和BIND_ADJUST_WITH_ACTIVITY
.来调节影响服务的重要性 - 一个开启的服务可以通过使用startForeground(int,Notification)API使服务处于前台状态,系统将会认为这是用户意识到的一些事情并且不会将服务作为在低内存下杀死的候选者。(但是理论上服务在极低内存的压力下仍会从当前前台应用程序被杀死,但在实际中并不需要考虑它)
注意,这意味着你的服务大多数时间是运行的,当在极大内存压力的情况下,系统将会考虑杀死服务。如果这发生的话,系统稍后会尝试着重启服务。这个结果表明,如果你实现onStartCommand()方法异步的执行工作或者在其他的线程中执行,你可以使用START_FLAG_REDELIVERY让系统重新给你发送一个意图,这样如果你的服务在处理某些事情时被杀死将不会丢失。运行在同一进程的其他应用程序将作为服务(如Activity),当然,增加所有进程的重要性,而不是仅仅服务本身。
本地服务示例:
一个使用服务的例子。首先是服务本身,定义一个类:
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);
}
}
做完上一步,就可以直接在客户端里编写代码来访问正在运行的服务,如下:
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();
}