Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android官方文檔之Services

Android官方文檔之Services

編輯:關於Android編程

官方原文鏈接

本文將介紹Services的定義、創建、啟動、綁定、前台Service等相關內容。


Service是Android中一個類,它是Android四大組件之一,使用Service可以在後台執行長時間的操作,Service並不與用戶產生UI交互。其他的應用組件可以啟動Service,即便用戶切換了其他應用,啟動的Service仍可在後台運行。一個組件可以與Service綁定並與之交互,甚至是跨進程通信(IPC)。例如,一個Service可以在後台執行網絡請求、播放音樂、執行文件讀寫操作或者與 content provider交互等。


Services兩種啟動形式

Started:其他應用組件(如 Activity)通過調用 startService() 啟動一個Service。一旦啟動,Service將一直運行在後台(run in the background indefinitely)即便啟動Service的組件已被destroy。通常,一個被start的Service會在後台執行單獨的操作,也並不給啟動它的組件返回結果。例如,它可能通過網絡下載或上傳文件。 操作完成後,服務會自行停止運行。 Bound:其他組件調用bindService()方法綁定一個Service。通過綁定方式啟動的Service是一個client-server結構,該Service可以與綁定它的組件進行交互、發送請求、獲取結果,甚至是利用進程間通信 (IPC) 跨進程執行這些操作。一個bound service僅在有組件與其綁定時才會運行(A bound service runs only as long as another application component is bound to it),多個組件可與一個service綁定,service不再與任何組件綁定時,該service會被destroy。

service也可以同時在上述兩種方式下運行。這涉及到Service中兩個回調方法的執行:onStartCommand()(通過start方式啟動一個service時回調的方法)、onBind()(通過bind方式啟動一個service回調的方法)。

無論通過那種方式啟動service(start、bind、start&bind),任何組件(甚至其他應用的組件)都可以使用service。並通過Intent傳遞參數。當然,您也可以將Service在manifest文件中配置成私有的,不允許其他應用訪問。

注意:Service運行在主線程中(A service runs in the main thread of its hosting process),Service並不是一個新的線程,也不是新的進程。也就是說,若您需要在Service中執行較為耗時的操作(如播放音樂、執行網絡請求等),需要在Service中創建一個新的線程。這可以防止ANR的發生,同時主線程可以執行正常的UI操作。

Service基礎知識

為了創建Service,需要繼承Service類。並重寫它的回調方法,這些回調方法反應了Service的生命周期,並提供了綁定Service的機制。最重要的Service的生命周期回調方法如下所示:

onStartCommand():當其他組件調用startService()方法請求啟動Service時,該方法被回調。一旦Service啟動,它會在後台獨立運行。當Service執行完以後,需調用stopSelf() 或 stopService()方法停止Service。(若您只希望bind Service,則無需調用這些方法) onBind():當其他組件調用bindService()方法請求綁定Service時,該方法被回調。該方法返回一個IBinder接口,該接口是Service與綁定的組件進行交互的橋梁。若Service未綁定其他組件,該方法應返回null。 onCreate():當Service第一次創建時,回調該方法。該方法只被回調一次,並在onStartCommand() 或 onBind()方法被回調之前執行。若Service處於運行狀態,該方法不會回調。 onDestroy():當Service被銷毀時回調,在該方法中應清除一些占用的資源,如停止線程、接觸綁定注冊的監聽器或broadcast receiver等。該方法是Service中的最後一個回調。

如果某個組件通過調用startService()啟動了Service(系統會回調onStartCommand()方法),那麼直到在Service中手動調用stopSelf()方法、或在其他組件中手動調用stopService()方法,該Service才會停止。

如果某個組件通過調用bindService()綁定了Service(系統不會回調onStartCommand()方法),只要該組件與Service處於綁定狀態,Service就會一直運行,當Service不再與組件綁定時,該Service將被destroy。

僅當內存過低且必須回收系統資源以供具有用戶焦點的 Activity 使用時,Android 系統才會強制停止服務。如果將服務綁定到具有用戶焦點的 Activity,則它不太可能會終止;如果將服務聲明為在前台運行(稍後討論),則它幾乎永遠不會終止。 否則,當創建了一個長時間在後台運行的Service後,系統會降低該Service在後台任務棧中的級別——這意味著它容易被kill(lower its position in the list of background tasks over time and the service will become highly susceptible to killing),所以在開發Service時,需要使Service變得容易被restart,因為一旦Service被kill,再restart它需要其資源可用時才行(restarts it as soon as resources become available again ),當然這也取決於onStartCommand()方法返回的值,這將在後續介紹。


在manifest文件中注冊Service

如同 Activity(以及其他組件)一樣,您必須在應用的清單文件中聲明所有服務。
要聲明服務,請添加 < service> 元素作為 < application> 元素的子元素。例如:


  ...
  
      
      ...
  

您還可將其他屬性包括在 元素中,以定義一些特性,如啟動服務及其運行所在進程所需的權限。android:name 屬性是唯一必需的屬性,用於指定服務的類名。應用一旦發布,即不應更改此類名,如若不然,可能會存在因依賴顯式 Intent 啟動或綁定服務而破壞代碼的風險。

注意:為了保證應用的安全,請使用顯式Intent啟動或綁定一個Service,請不要在< service>標簽中配置intent-filter。

若不確定該啟動哪個Service,那麼可以在中配置intent-filter,並在Intent中排除該Service(supply intent filters for your services and exclude the component name from the Intent),但必須調用Intent的setPackage()方法,來為啟動的service消除歧義(provides sufficient disambiguation for the target service)。

注:setPackage()方法傳入一個String參數,代表一個包名。該方法表示該Intent對象只能在傳入的這個包名下尋找符合條件的組件,若傳入null,則表示可以在任意包下尋找。

start方式啟動Service

啟動服務由另一個組件通過調用 startService() 啟動,這會導致調用服務的 onStartCommand() 方法。服務啟動之後,其生命周期即獨立於啟動它的組件,並且可以在後台無限期地運行,即使啟動服務的組件已被銷毀也不受影響。 因此,服務應通過調用 stopSelf() 結束工作來自行停止運行,或者由另一個組件通過調用 stopService() 來停止它。
startService()方法中傳入一個Intent參數,用於顯式指定目標Service的名字,並攜帶data以供Service使用,該Intent參數將回傳至onStartCommand()方法中。
比如說,Activity需要向在線數據庫中上傳數據,那麼可以調用startService()啟動一個Service,並將數據傳入Intent的data中,接著,onStartCommand()方法會接收這個Intent並開啟一個線程將數據上傳至網絡,當數據上傳完成後,該Service將停止並被destroy。

注意:默認情況下,服務與服務聲明所在的應用運行於同一進程,而且運行於該應用的主線程中。 因此,如果服務在用戶與來自同一應用的 Activity 進行交互時執行密集型或阻止性操作,則會降低 Activity 性能。 為了避免影響應用性能,您應在服務內啟動新線程。


一般使用如下兩種方式創建一個start Service:

繼承Service類:這是適用於所有服務的基類。擴展此類時,必須在Service中開啟線程來執行耗時操作,因為默認情況下,服務將使用應用的主線程,這會降低應用正在運行的所有 Activity 的性能。 繼承IntentService類:IntentService繼承於Service,如果你不要求服務同時處理多個請求,那麼使用IntentService將是最好選擇。你只需要重寫onHandleIntent()方法,該方法接收一個回傳的Intent參數,你可以在方法內進行耗時操作,因為它默認開啟了一個子線程,操作執行完成後也無需手動調用stopSelf()方法,onHandleIntent()會自動調用該方法。

繼承IntentService類

在大多數情況下,start Service並不會同時處理多個請求(don’t need to handle multiple requests simultaneously),因為處理多線程較為危險(a dangerous multi-threading scenario),所以繼承IntentService類帶創建Service是個不錯選擇。

IntentService 執行以下操作:

創建默認的工作線程,用於在應用的主線程外執行傳遞給 onStartCommand() 的所有 Intent。 創建工作隊列,用於將一個 Intent 逐一傳遞給 onHandleIntent() 實現,這樣您就永遠不必擔心多線程問題。 在處理完所有啟動請求後停止服務,因此您永遠不必調用 stopSelf()。 提供 onBind() 的默認實現(返回 null)。 提供 onStartCommand() 的默認實現,可將 Intent 依次發送到工作隊列和 onHandleIntent() 實現。

綜上所述,您只需實現 onHandleIntent() 來完成客戶端提供的工作即可。(不過,你還需要為服務提供構造函數。)實例如下:

public class HelloIntentService extends IntentService {

  /**
   * A constructor is required, and must call the super IntentService(String)
   * constructor with a name for the worker thread.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // Normally we would do some work here, like download a file.
      // For our sample, we just sleep for 5 seconds.
      long endTime = System.currentTimeMillis() + 5*1000;
      while (System.currentTimeMillis() < endTime) {
          synchronized (this) {
              try {
                  wait(endTime - System.currentTimeMillis());
              } catch (Exception e) {
              }
          }
      }
  }
}

如果您還希望在IntentService的繼承類中重寫其他生命周期方法,如onCreate()、onStartCommand() 或 onDestroy(),那麼請先調用各自的父類方法以保證子線程能夠正常啟動。

比如,要實現onStartCommand()方法,需返回其父類方法(即,如何將 Intent 傳遞給 onHandleIntent()):

@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);
}

除onHandleIntent()外,onBind()方法也無需調用其父類方法(僅當服務允許綁定時,才需要實現該方法)。


Service類

如果您需要在Service中執行多線程而不是處理一個請求隊列(perform multi-threading instead of processing start requests through a work queue),那麼需要繼承Service類,分別處理每個Intent。

為了便於比較,以下提供了 Service 類實現的代碼示例,該類執行的工作與上述使用 IntentService 的示例完全相同。在Service中執行操作時,處理每個請求都需要開啟一個線程,並且同一時刻一個線程只能處理一個請求( for each start request, it uses a worker thread to perform the job and processes only one request at a time)。

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

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service.  Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block.  We also make it
    // background priority so CPU-intensive work will not disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    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();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }

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

因為是由您自己處理對 onStartCommand() 的每個調用,因此可以同時執行多個請求。此示例並未這樣做,但如果您希望如此,則可為每個請求創建一個新線程,然後立即運行這些線程(而不是等待上一個請求完成)。

請注意,onStartCommand() 方法必須返回整型數。整型數是一個值,用於描述系統應該如何在服務終止的情況下繼續運行服務(如上所述,IntentService 的默認實現將為您處理這種情況,不過您可以對其進行修改)。從 onStartCommand() 返回的值必須是以下常量之一:

START_NOT_STICKY:若執行完onStartCommand()方法後,系統就kill了service,不要再重新創建service,除非系統回傳了一個pending intent。這避免了在不必要的時候運行service,您的應用也可以restart任何未完成的操作。 START_STICKY:若系統在onStartCommand()執行並返回後kill了service,那麼service會被recreate並回調onStartCommand()。但是不要重新傳遞最後一個Intent(do not redeliver the last intent)。相反,系統回調onStartCommand()時回傳一個空的Intent,除非有 pending intents傳遞,否則Intent將為null。該模式適合做一些類似播放音樂的操作。 START_REDELIVER_INTENT:若系統在onStartCommand()執行並返回後kill了service,那麼service會被recreate並回調onStartCommand()並將最後一個Intent回傳至該方法。任何 pending intents都會被輪流傳遞。該模式適合做一些類似下載文件的操作。

啟動服務

可以結合使用顯式 Intent 與 startService(),啟動上文中的示例服務 (HelloSevice):

Intent intent = new Intent(this, HelloService.class);
startService(intent);

startService(intent)方法將立即返回,並回調onStartCommand()(請不要手動調用該方法),若該Service未處於運行狀態,系統將首先回調onCreate(),接著再回調onStartCommand()。若您希望Service可以返回結果,那麼需要通過調用getBroadcast 返回的PendingIntent啟動Service(將PendingIntent包裝為Intent),service可使用broadcast 傳遞結果。
多個啟動Service的請求可能導致onStartCommand()多次調用,但只需調用stopSelf() 、 stopService()這兩個方法之一,就可停止該服務。


停止服務

一個啟動的Service必須管理自己的生命周期。系統不會主動stop或destroy一個運行的Service,除非系統內存緊張,否則,執行完onStartCommand()方法後,Service依然運行。停止Service必須手動調用stopSelf()(在Service中)或調用stopService()(在啟動組件中)。
一旦調用了上述兩種方法之一,系統會盡快destroy該Service(as soon as possible)。

如果服務同時處理多個 onStartCommand() 請求,則您不應在處理完一個啟動請求之後停止服務,因為您可能已經收到了新的啟動請求(在第一個請求結束時停止服務會終止第二個請求)。為了避免這一問題,您可以使用 stopSelf(int) 確保服務停止請求始終基於最近的啟動請求。也就說,在調用 stopSelf(int) 時,傳遞與停止請求的 ID 對應的啟動請求的 ID(傳遞給 onStartCommand() 的 startId) 。然後,如果在您能夠調用 stopSelf(int) 之前服務收到了新的啟動請求, ID 就不匹配,服務也就不會停止。

注意:為了避免浪費系統資源和消耗電池電量,應用必須在工作完成之後停止其服務。 如有必要,其他組件可以通過調用 stopService() 來停止服務。即使為服務啟用了綁定,一旦服務收到對 onStartCommand() 的調用,您始終仍須親自停止服務。


創建綁定Service

通過其他組件調用bindService()方法可以綁定一個Service以保持長連接(long-standing connection),這時一般不允許其他組件調用startService()啟動Service。

當其他組件需要與Service交互或者需要跨進程通信時,可以創建一個bound Service。

為創建一個bound Service,必須重寫onBind()回調,該方法返回一個IBinder接口。該接口時組件與Service通信的橋梁。組件調用bindService()與Service綁定,該組件可獲取IBinder接口,一旦獲取該接口,就可以調用Service中的方法。一旦沒有組件與Service綁定,系統將destroy它,您不必手動停止它。

為創建一個bound Service,必須定義一個接口 ,該接口指定組件與Service如何通信。定義的接口在組件與Service之間,且必須實現IBinder接口。這正是onBind()的返回值。一旦組件接收了IBinder,組件與Service便可以開始通信。

多個組件可同時與Service綁定,當組件與Service交互結束後,可調用unbindService()方法解綁。bound Service比start Service要復雜,故我將在後續單獨翻譯。


向用戶發送通知

運行中的Service可以通過Toast Notifications 或 Status Bar Notifications 向用戶發送通知。Toast是一個可以短時間彈出的提醒框。Status Bar是頂部狀態欄中出現的帶有圖標的信息,用戶可以通過下拉狀態欄獲得具體信息並執行某些操作(如啟動Activity)。

通常,Status Bar用於通知某些操作已經完成,如下載文件完成。當用戶下拉狀態欄後,點擊該通知,可獲取詳細內容,如查看該下載的文件。


運行前台Service

前台Service用於動態通知消息,如天氣預報。該Service不易被kill。前台Service必須提供status bar,只有前台Service被destroy後,status bar才能消失。
舉例來說,一個播放音樂的Service必須是前台Service,只有這樣用戶才能確知其運行狀態。為前台Service提供的status bar可以顯示當前音樂的播放狀態,並可以啟動播放音樂的Activity。


調用startForeground()可以啟動前台Service。該方法接收兩個參數,參數一是一個int型變量,用戶指定該通知的唯一性標識,而參數二是一個Notification用於配置status bar,示例如下:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
        System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);

注意:提供給 startForeground() 的整型 ID 不得為 0。

調用stopForeground()來移除(remove)前台Service。該方法需傳入一個boolean型變量,表示是否也一並清除status bar上的notification(indicating whether to remove the status bar notification as well)。該方法並不會停止Service。但是,如果停止正在前台運行的Service,那麼notification 也一並被清除。


管理Service生命周期

從Service的啟動到銷毀,有兩種路徑:

A started service:需手動停止。該服務在其他組件調用 startService() 時創建,然後無限期運行,且必須通過調用 stopSelf() 來自行停止運行。此外,其他組件也可以通過調用 stopService() 來停止服務。服務停止後,系統會將其銷毀。 A bound service:可自動停止。該服務在另一個組件(客戶端)調用 bindService() 時創建。然後,客戶端通過 IBinder 接口與服務進行通信。客戶端可以通過調用 unbindService() 關閉連接。多個客戶端可以綁定到相同服務,而且當所有綁定全部取消後,系統即會銷毀該服務。

這兩條路徑並不是毫不相干的:當調用startService()start一個Service後,您仍可以bind該Service。例如,可以通過使用 Intent(標識要播放的音樂)調用 startService() 來啟動後台音樂服務。隨後,可能在用戶需要稍加控制播放器或獲取有關當前播放歌曲的信息時,Activity 可以通過調用 bindService() 綁定到服務。在這種情況下,除非所有客戶端均取消綁定,否則 stopService() 或 stopSelf() 不會真正停止服務。
Service生命周期


實現Service的生命周期回調

與 Activity 類似,服務也擁有生命周期回調方法,您可以實現這些方法來監控服務狀態的變化並適時執行工作。 以下框架服務展示了每種生命周期方法:

public class ExampleService extends Service {
    int mStartMode;       // indicates how to behave if the service is killed
    IBinder mBinder;      // interface for clients that bind
    boolean mAllowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

注:與 Activity 生命周期回調方法不同,您不需要調用這些回調方法的超類實現。


在兩條生命周期路徑中,都包含了兩個嵌套的生命周期:

完整生命周期( entire lifetime
):從onCreate()被調用到onDestroy()返回。與Activity類似,一般在onCreate()中做一些初始化工作,而在onDestroy()做一些資源釋放工作。如,若Service在後台播放一個音樂,就需要在onCreate()方法中開啟一個線程啟動音樂,並在onDestroy()中結束線程。無論是startService()
還是 bindService()啟動Service,onCreate() 和 onDestroy()均會被回調。 活動生命周期(active lifetime):從onStartCommand() 或
onBind()回調開始。由相應的startService() 或 bindService()調用。 若是Start
Service,那麼Service的活動生命周期結束就意味著其完整生命周期結束 (the active lifetime ends the
same time that the entire lifetime
ends),即便onStartCommand()返回後,Service仍處於活動狀態;若是bound
Service,那麼當onUnbind()返回時,Service的活動生命周期結束。

注意:針對Start Service,由於Service中沒有類似onStop()的回調,所以在調用stopSelf() 或 stopService()後,只有onDestroy()被回調標志著Service已停止。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved