天天看點

Service服務<二>

上篇:service服務<一>

權限:

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