Service

所有相關示例代碼託管到 GitHub 上
1.基本概念
服務是一個在後臺長時間執行操作而不需要用戶界面的組件。服務可由其他組件啓動,如 Activity 和 Service,即時切換到其他應用,服務仍可以在後臺運行。此外,其他組件可以綁定到服務來與之交互, 甚至可以進行 IPC 通信。

啓動服務:
由其他組件通過 startService() 啓動服務,之後即可無限期運行,即使啓動服務的組件被銷燬也不會影響它的運行。啓動服務一般執行的是單一的操作,而且不會將執行結果傳遞給調用方。

綁定服務
由其他組件通過 bindService() 綁定到服務,由 IBinder 接口實現客戶端-服務端的交互。綁定了服務的組件銷燬會取消綁定,如果所有綁定了服務的組件取消服務後,那麼綁定服務就會被銷燬。

可以同時實現兩種服務,問題在於是否實現了 onStartCommand() 和 onBind() 方法回調,注意,服務默認運行在其託管進程的主線程上,如需執行耗時的操作,應該開啓新線程,避免 ANR。

2.使用服務還是線程?
服務是一種沒有與用戶交互也可以執行操作的組件。如果需要在主線程外執行工作,而這種工作是用戶與應用交互時才需要的,那麼此時應該創建新線程而不是服務,比如在一個 Activity 界面運行同時播放音樂。

3創建服務
繼承 Service 並實現其必要的回調方法,然後在清單文件聲明服務。

一些重要的回調方法:
onCreate(): startService 或 bindService 後回調的第一個方法,進行一些初始化的操作,比如新線程的創建。只要服務沒被銷燬,只執行一次。

onStartCommand(): 只有通過 startService() 啓動的服務纔會調用到,一旦調用到此方法服務便開始工作,並可無限執行下去,因此如果服務工作完成後應該主動調用 stopSelf() 或由其他組件調用 stopService() 來停止服務的執行。

onBind():只有通過 bindService() 啓動的服務纔會調用到,客戶端綁定服務,該方法需要返回一個 IBinder接口實現類,完成服務和客戶端的交互。

onDestry():服務不在使用將被銷燬前調用,這裏進行資源的清理工作

使用清單文件聲明服務:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

還可以通過添加 android:exported 屬性並將其設置爲 “false”,確保服務僅適用於您的應用。

4啓動服務擴展類
①Service類:適用於所有服務的基類。默認運行於主線程中,若要執行耗時操作,需要開啓新線程來避免 ANR。

public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              Thread.currentThread().interrupt();
          }
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

注意 onStartCommand 返回值:
START_NOT_STICKY: 服務啓動後被系統終止(比如服務執行期間從最近使用應用列表中殺死該應用),除非有掛起的 Intent(即PendingIntent)要傳遞,否則不會重建服務,即執行 onCreate 方法。

START_STICKY:服務啓動後被系統終止,一定時間後會重建服務並執行 onStartCommand()方法,有掛起的 intent 傳遞時就傳遞掛起 intent,如果沒有則會傳遞一個空的 intent到 onStartCommand() 中。這適用於不執行命令,但需要無限期運行並等待執行操作的媒體播放器,或類似服務。

START_REDELIVER_INTENT:服務啓動後被系統終止,一定時間後會重建服務並執行 onStartCommand()方法,並傳遞之前執行的最後一個 intent 。掛起的 intent 依次傳遞。適用於主動執行應該立即恢復操作的服務,比如文件下載。

②IntentService類:Service 的子類。內部封裝了一個線程逐一去處理所有啓動請求。只需實現 onHandleIntent() 回調方法即可,注意此方法使用的是工作線程。如果不要求服務同時處理多個啓動服務請求,這是最好的選擇,大多數服務都不要求同時處理多個請求

public class HelloIntentService extends IntentService {

  public HelloIntentService() {
      super("HelloIntentService");
  }

  @Override
  protected void onHandleIntent(Intent intent) {
      try {
          Thread.sleep(5000);
      } catch (InterruptedException e) {    
          Thread.currentThread().interrupt();
      }
  }
}

只需要一個構造函數和 onHandleIntent()實現即可。如果真的需要重寫其他回調方法,比如 onStartCommand() 等,那麼最後必須調用父類的實現,如

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    return super.onStartCommand(intent,flags,startId);
}

③啓動服務生命週期
首次啓動服務: onCreate() –>onStartCommand()
非首次啓動:直接 onStartCommand()
停止服務:一個 stopSelf() 或者 stopService() 即可停止全部請求

④啓動服務的通信方式
a. 客戶端通過 intent 傳遞數據到服務器

b.客戶端創建一個 廣播接收器,在 onReceive() 方法中通過方法參數 intent 獲取從服務器傳遞過來的數據並進行處理。而服務只需要在執行完操作後把結果封裝到 intent 併發送相應廣播。

5.綁定服務
①生命週期:
客戶端取消綁定前,只執行一次生命週期,即綁定後再次綁定是無效操作。而且只有首次被客戶端綁定時纔會執行 onCreate() 方法,除非被銷燬纔會再次執行。
bindService()–>onCreate()–> onBind()–> onServiceConnection()

②綁定服務的創建
a.繼承 Binder 類

  • 繼承 Binder 類,裏面定義可供客戶端調用的公共方法,服務類實例,以及服務類裏包含的其他實例。
  • 在 onBind() 方法返回 Binder 實例
  • 在 onServiceConnected() 方法對 Binder 進行獲取,根據獲得的 Binder 處理數據。
  • 這裏寫圖片描述

b.使用Messenger類

1.在服務端(1~3步): 創建一個 Handler 變量
2. 根據 Handler 變量構造生成 Messenger 變量
3. 在 onBind() 方法中返回 Messenger.getBinder() ;
4. 在客戶端的 onServiceConnection() 中接收並構造出 new Messenger(IBinder service)
5. 通過 4 中構造的 Messenger 的send() 方法發送 Message 對象。對於 Message 的處理就在 1 中創建的 Handler 的 HandleMessage() 中。

c.使用AIDL

  • 創建一個.aidl文件,會在build/generaed/source/aidl/… 生成對應的 .java 文件。
  • 服務器端創建 1 中生成的 java 文件的內部類 Stub變量,並對實現相應的接口方法,然後在 onBind() 中返回該變量。
  • 把整個main/aidl 文件複製到客戶端中的 main 目錄下。在 onServiceConnected() 方法通過內部類 Stub 的 asInterface(IBinder) 獲得對應的接口實現類,進行方法調用

③Messenger 與 AIDL 的比較
使用 Messenger 比 AIDL 簡單,因爲 Messenger 的底層是 handler 實現,會把每個請求存入到隊列當中,一次處理一個請求。而 AIDL 則會同時發送多個請求,因此在服務端必須對多線程進行處理。

參考資料:
服務相關官方文檔,需翻牆
綁定服務的官方介紹,需翻牆
AIDL 官方文檔,需翻牆
Android中通過Messenger與Service實現進程間雙向通信

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章