權限:
當服務在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();
}