安卓開發之服務Service

cxmscb發表於2016-07-14

一、Service是什麼?

  1. Service是一個應用元件,它用來在後臺完成一個時間跨度比較大的工作,且沒有關聯任何介面。(生命週期在應用程式程式的主執行緒執行)
  2. 一個Service可以完成的工作:訪問網路(在Service中啟動分執行緒)、播放音樂、檔案IO操作、大資料量的資料庫操作等。如果需要在Service中處理一些網路連線等耗時的操作,那麼應該將這些任務放在分執行緒中處理,避免在主執行緒中阻塞使用者介面。
  3. 特點:Service在後臺執行,不用與使用者進行互動。即使應用退出,服務也不會停止。當應用程式被殺死時(例如一鍵清理),服務便會停止。

二、Service的分類

  1. Local Service (本地服務)
    Service物件與Service的啟動者在同個程式中進行,兩者的通訊是程式內的通訊。
  2. Remote Service (遠端服務)
    Service物件與Service的啟動者不在同個程式中進行,這時存在一個程式間的通訊問題,Android專門為此設計了AIDL來實現程式間的通訊。

三、Service的定義

  1. 定義一個類繼承於Service類,重寫方法。

    public class MusicService extends Service {
    
        public MusicService(){
    
            Log.i("TAG","MusicService");
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            Log.i("TAG","onBind");
            return new Binder();
           //return 到ServiceConnection的onServiceConnected中
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i("TAG","onCreate");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
    
            Log.i("TAG","onStartCommand");
            return super.onStartCommand(intent, flags, startId);
            //返回值不同,Service被殺掉的情況也不同
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.i("TAG","onDestroy");
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
    
            Log.i("TAG","onUnbind");
            return super.onUnbind(intent);
        }
    
    }
    
  2. AndroidManifest.xml中配置Service

    <service android:name="com.cxmscb.cxm.moodmusic.MusicService" />
    <!--本地Service不需要intent-filter-->
    
    <!--可以使用process標籤讓服務執行在另一個程式-->
    <service android:name="com.cxmscb.cxm.moodmusic.MusicService"
        android:process=":remote"
        />
    
  3. 注意:onStartCommand()方法必須返回一個整數,這個整數是一個描述了在系統的殺死事件中,系統應該如何繼續這個服務的值。從onStartCommand()方法中返回的值必須是以下常量:

    START_NOT_STICKY
    如果系統在onStartCommand()方法返回之後殺死這個服務,那麼直到接受到新的Intent物件,這個服務才會被重新建立。
    
    START_STICKY
    如果系統在onStartCommand()返回後殺死了這個服務,系統就會重新建立這個服務並且呼叫onStartCommand()方法,但是它不會重新傳遞最後的Intent物件,系統會用一個null的Intent物件來呼叫onStartCommand()方法。
    在這個情況下,除非有一些被髮送的Intent物件在等待啟動服務。這適用於不執行命令的媒體播放器(或類似的服務),它只是無限期的執行著並等待工作的到來。
    
    START_REDELIVER_INTENT
    如果系統在onStartCommand()方法返回後,系統就會重新建立了這個服務,並且用傳送給這個服務的最後的Intent物件呼叫了onStartCommand()方法。任意等待中的Intent物件會依次被髮送。這適用於那些應該立即恢復正在執行的工作的服務,如下載檔案。
    

四、啟動/停止服務Service

  1. Service分為兩種工作狀態,一種是啟動狀態,主要用於執行後臺計算;另一種是繫結狀態,主要用於其他元件和Service的互動。

  2. Service的這兩種狀態是可以共存的,即Service既可以處於啟動狀態也可以同時處於繫結狀態。

    啟動服務:(不繫結)

    Intent intent = new Intent(this,MusicService.class);
    this.startService(intent);
    //通過intent信使來啟動
    

    停止服務:

    Intent intent = new Intent(this,MusicService.class);
    this.stopService(intent);
    //通過intent信使來停止
    // 也可以在Service中呼叫 stopSelf() 來停止服務
    

    繫結服務:

    ServiceConnection serviceConnection;
    
    // 與Activity繫結服務來啟動服務
    
    Intent intent = new Intent(this,MusicService.class);
    
    if(serviceConnection==null){
    
        serviceConnection = new ServiceConnection() {
    
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //當Activity與服務連線上時回撥。 
            //onBind() --> onServiceConnected()
            //iBinder為Service中的onBind返回的iBinder
        }
    
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            //當Activity與服務斷開後回撥。
        }
    };
        this.bindService(intent,serviceConnection,Context.BIND_AUTO_CREATE);
    

    解綁服務:

    //當Activity銷燬時,要與繫結的服務Sercive解綁,否則會報錯
    
    if(serviceConnection!=null){
    
        Intent intent = new Intent(this,MusicService.class);
        this.unbindService(intent,serviceConnection);
        serviceConnection = null;
    
    }
    

五、Service的生命週期

因啟動服務的方式不同,Service的生命週期也不同。

這裡寫圖片描述

  1. startService
    第一次啟動服務: —>構造方法—>onCreate—>onStartCommand
    再次啟動該服務:—>onStartCommand

    stopService( )
    呼叫:—>onDestory

  2. bindService
    呼叫:—>構造方法—>onCreate—>onBind—->onServiceConneted

    unbindService
    呼叫:(當前Activity與該Service連線)—>onUnbind—>onDestory

六、Activity與Service的資料交流

  1. Activity傳資料給Service:在啟動服務時通過Intent傳遞資料。(注意第一次啟動服務時和再次啟動服務時的方法呼叫區別。)

  2. Service傳即時資料給Activity:Service可以通過特定廣播來傳遞即時資料給接收特定廣播的Activity,傳送廣播時也會帶有Intent物件,可通過Intent攜帶資料。

  3. 當Activity和Service繫結時,可以通過Binder來連結Service和Activity,通過Binder來返回繫結的Service來獲取Service物件內部的資料。

    a。使用Binder擴充套件類物件進行交流:返回Service物件(非跨程式)

    @Override
        public IBinder onBind(Intent intent) {
            Log.i("TAG","onBind");
            return new LocalBinder();
           //return 到ServiceConnection的onServiceConnected中
        }
    
    ------------------------------------------------------------
    // MyService的內部類LocalBinder
    public class LocalBinder  extends Binder{
    
            public MyService getService(){
    
                    return MyService.this;
            }
    
    }
    ---------------------------------------------------------------------- 
    
    MyService service = null;
    
    // 在Activity繫結服務時需要的ServiceConnection中的onServiceConnected方法
    
    @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //當Activity與服務連線上時回撥。 
            //onBind() --> onServiceConnected()
            //iBinder為Service中的onBind返回的iBinder
    
            MyService.LocalBinder  localBinder  = (MyService.LocalBinder)iBinder;
            service = localBinder.getService();
    
    
    
        }       
    

    b. 使用AIDL / Messenger 進行跨程式交流

    使用Messenger進行交流:

    1. 服務端實現一個 Handler,由其處理來自客戶端的訊息Message

      class ServiceHandler extends Handler {
          @Override
          public void handleMessage(Message msg) {
              switch (msg.what) {
                  case 0:
                      Log.i(TAG, "thanks,Service had receiver message from client!");
                      break;
                  default:
                      super.handleMessage(msg);
              }
          }
      }
      
    2. 在服務端將 Handler例項 封裝為 Messenger 物件

      Messenger mMessenger = new Messenger(new ServiceHandler());
      
    3. 獲取Messenger的IBinder例項,在 onBind() 中將其返回客戶端

      @Override
      public IBinder onBind(Intent intent) {
          return mMessenger.getBinder();
      }
      
    4. 客戶端獲取 IBinder 物件後將其 再封裝 Messenger 物件,然後使用Messenger將 Message 物件傳送給服務端

      /**
       * 與服務端互動的Messenger
       */
      Messenger mMessenger = null;    
      
      private ServiceConnection mConnection = new ServiceConnection() {
          public void onServiceConnected(ComponentName className, IBinder service) {
              /**
               * 通過服務端傳遞的IBinder物件,封裝為相應的Messenger
               */
              mMessenger = new Messenger(service);
      
          }
      
          public void onServiceDisconnected(ComponentName className) {
               mMessenger = null;
          }
      };
      
      
      // 使用mMessenger傳送訊息 :
      .... // 建立Messager物件msg
      // 傳送msg物件,由服務端的Handler來處理訊息
      mMessenger..send(msg) ;
      

      (同理可以在客戶端建立客戶端的Handler和Messenger,將Messenger物件通過訊息msg傳遞給服務端,服務端再使用客戶端的Messenger物件來傳送訊息)

    使用AIDL進行交流:

七、前臺服務

將服務置為前臺狀態,可對服務的程式進行提權,即在記憶體不足時,系統也不會考慮將其程式終止。不過將服務置為前臺時,需要在狀態列中顯示一個通知Notification來告知使用者。

  • startForeground(int id, Notification notification)

    把當前服務設定為前臺服務,其中id引數代表唯一標識通知的整型數(不可以為 0),notification是一個狀態列的通知例項。
    
  • stopForeground(boolean removeNotification)

    將處於前臺狀態的服務降權,恢復原來的狀態。方法傳入一個布林值,表示是否刪除狀態列通知,true為刪除。 (該方法並不會停止服務) 
    

八、IntentService

  1. IntentService是Service類的子類,為非同步服務。可以通過startService(Intent)方法傳遞請求給IntentService。
  2. 可以在IntentService實現虛擬函式onHandleIntent,在裡面根據Intent的不同進行不同的處理,按佇列來一一處理。當執行完一個Intent請求物件所對應的工作之後,如果沒有新的Intent請求達到,則自動停止Service;否則執行下一個Intent請求所對應的任務。
  3. 使用場景:需要將任務按佇列來排列處理時,可以用IntentService。
  4. IntentService預設實現了Onbind()方法,返回值為null。

相關文章