安卓四大組件之--Service

Service是Android系統中的四大組件之一,主要有兩個應用場景:後臺運行和跨進程訪問。Service能夠在後臺執行長時間運行操做而不提供用戶界面,除非系統必須回收內存資源,不然系統不會中止或銷燬服務。服務可由其餘應用組件啓動,並且即便用戶切換到其餘應用,服務仍將在後臺繼續運行。 此外,組件能夠綁定到服務,以與之進行交互,甚至是執行進程間通訊 (IPC),須要注意的是,Service是在主線程裏執行操做的,可能會由於執行耗時操做而致使ANRandroid

1、基礎知識

Service能夠分爲如下三種形式:

  • 啓動

    當應用組件經過調用 startService() 啓動服務時,服務即處於「啓動」狀態。一旦啓動,服務便可在後臺無限期運行,即便啓動服務的組件已被銷燬也不受影響。 已啓動的服務一般是執行單一操做,並且不會將結果返回給調用方。編程

  • 綁定

    當應用組件經過調用 bindService() 綁定到服務時,服務即處於「綁定」狀態。綁定服務提供了一個客戶端-服務器接口,容許組件與服務進行交互、發送請求、獲取結果,甚至是利用進程間通訊 (IPC) 跨進程執行這些操做。多個組件能夠同時綁定服務,服務只會在組件與其綁定時運行,一旦該服務與全部組件之間的綁定所有取消,系統便會銷燬它。安全

  • 啓動且綁定

    服務既能夠是啓動服務,也容許綁定。此時須要同時實現如下回調方法:onStartCommand()和 onBind()。系統不會在全部客戶端都取消綁定時銷燬服務。爲此,必須經過調用 stopSelf() 或 stopService() 顯式中止服務不管應用是處於啓動狀態仍是綁定狀態,或者處於啓動且綁定狀態,任何應用組件都可像使用 Activity 那樣經過調用 Intent 來使用服務(即便此服務來自另外一應用),也能夠經過清單文件將服務聲明爲私有服務,阻止其餘應用訪問要使用服務,必須繼承Service類(或者Service類的現有子類),在子類中重寫某些回調方法,以處理服務生命週期的某些關鍵方面並提供一種機制將組件綁定到服務。服務器

  • onStartCommand()

    當組件經過調用 startService() 請求啓動服務時,系統將調用此方法(若是是綁定服務則不會調用此方法)。一旦執行此方法,服務即會啓動並可在後臺無限期運行。 在指定任務完成後,經過調用 stopSelf() 或 stopService() 來中止服務多線程

  • onBind()

    當一個組件想經過調用 bindService() 與服務綁定時,系統將調用此方法(若是是啓動服務則不會調用此方法)。在此方法的實現中,必須經過返回 IBinder 提供一個回調接口,供客戶端用來與服務進行通訊app

  • onCreate()

    首次建立服務時,系統將調用此方法來執行初始化操做(在調用 onStartCommand() 或 onBind() 以前)。若是在啓動或綁定以前Service已在運行,則不會調用此方法dom

  • onDestroy()

    當服務再也不使用且將被銷燬時,系統將調用此方法,這是服務接收的最後一個調用,在此方法中應清理佔用的資源僅當內存太低必須回收系統資源以供前臺 Activity 使用時,系統纔會強制中止服務。若是將服務綁定到前臺 Activity,則它不太可能會終止,若是將服務聲明爲在前臺運行,則它幾乎永遠不會終止。或者,若是服務已啓動並要長時間運行,則系統會隨着時間的推移下降服務在後臺任務列表中的位置,而服務也將隨之變得很是容易被終止。若是服務是啓動服務,則必須將其設計爲可以妥善處理系統對它的重啓。 若是系統終止服務,那麼一旦資源變得再次可用,系統便會重啓服務(這還取決於 onStartCommand() 的返回值)異步

2、聲明Service

  如同其餘組件同樣,想要使用Service,必須在清單文件中對其進行聲明,聲明方式是添加 < service > 元素做爲 < application > 元素的子元素,例如ide

<application 
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
  <service android:name=".MyService" />
</application>

 

android:name 屬性是惟一必需的屬性,用於指定服務的類名,還可將其餘屬性包括在 < service > 元素中以定義一些特性,爲了確保應用的安全性,最好始終使用顯式 Intent 啓動或綁定 Service,且不要爲服務聲明 Intent 過濾器。 啓動哪一個服務存在必定的不肯定性,而若是對這種不肯定性的考量很是有必要,則可爲服務提供 Intent 過濾器並從 Intent 中排除相應的組件名稱,但隨後必須使用 setPackage() 方法設置 Intent 的軟件包,這樣能夠充分消除目標服務的不肯定性;此外,還能夠經過添加 android:exported 屬性並將其設置爲 "false",確保服務僅適用於本應用。這能夠有效阻止其餘應用啓動本應用內的服務,即使在使用顯式 Intent 時也是如此,Service包含的屬性有函數

 


<service android:description="string resource"
    android:directBootAware=["true" | "false"]
    android:enabled=["true" | "false"]
    android:exported=["true" | "false"]
    android:icon="drawable resource"
    android:isolatedProcess=["true" | "false"]
    android:label="string resource"
    android:name="string"
    android:permission="string"
    android:process="string" >
</service>

 

屬性說明

description:對服務進行描述,屬性值應爲對字符串資源的引用,以便進行本地化

directBootAware:設置是否能夠在用戶解鎖設備以前運行,默認值爲「false」

enabled:設置是否能夠由系統來實例化服務。< application >元素有本身的enabled屬性,適用於包括服務在內的全部應用程序組件。要啓用服務,< application >和< service >屬性必須都爲「true」(默認狀況下都爲true)。若是其中一個是「false」,則服務被禁用。

exported:

    設置其餘應用程序的組件是否能夠調用本服務或與其交互,若是能夠,則爲「true」。當值爲「false」時,只有同一個應用程序或具備相同用戶ID的應用程序的組件能夠啓動該服務或綁定到該服務。該屬性的默認值取決於服務是否包含Intent filters。沒有任何過濾器意味着它只能經過指定其確切的類名來調用,這意味着該服務僅用於應用程序內部使用(由於其餘人不知道類名)。因此在這種狀況下,默認值爲「false」。 另外一方面,若是存在至少一個過濾器,意味着該服務打算供外部使用,所以默認值爲「true」

icon:服務的圖標,屬性值應是對drawable資源的引用。若是未設置,則將使用應用程序圖標

isolatedProcess:設置該服務是否做爲一個單獨的進程運行,若是設置爲true,此服務將在與系統其他部分隔離的特殊進程下運行,而且沒有本身的權限,與它惟一的通訊是經過服務API(綁定和啓動)

label:能夠向用戶顯示的服務的名稱,屬性值應是對字符串資源的引用

name:服務類的徹底限定名

permission:設定組件必須具備的權限,得以啓動服務或綁定服務。若是startService(),bindService()或stopService()的調用者沒有被授予此權限,則該方法將不會工做,而且Intent對象不會傳遞到服務中

process:

    用來運行服務的進程的名稱。一般,應用程序的全部組件都運行在應用程序建立的默認進程中,它與應用程序包名具備相同的名稱。 < application >元素的process屬性能夠爲全部組件設置不一樣的默認值,但組件可使用本身的進程屬性覆蓋默認值,從而容許跨多個進程擴展應用程序.

3、啓動Service

啓動服務由組件經過調用 startService() 啓動,服務啓動以後,其生命週期即獨立於啓動它的組件,而且能夠在後臺無限期地運行,即便啓動服務的組件已被銷燬也不受影響。所以,服務應經過調用 stopSelf() 來自行中止運行,或者由另外一個組件調用 stopService() 來中止

能夠經過擴展兩個類來建立啓動服務:

  • Service

    這是全部服務的父類。擴展此類時,若是要執行耗時操做,必須建立一個用於執行操做的新線程,由於默認狀況下服務將運行於UI線程

  • IntentService

    這是 Service 的子類,它使用工做線程逐一處理全部啓動請求。若是應用不須要同時處理多個請求,這是最好的選擇。IntentService只需實現構造函數與 onHandleIntent() 方法便可,onHandleIntent()方法會接收每一個啓動請求的 Intent

3.一、繼承Service

這裏舉一個音樂播放器的例子,繼承Service類實現自定義Service,提供在後臺播放音樂、暫停音樂、中止音樂的方法.

public class MyService extends Service {

  private final String TAG = "MyService";
  private MediaPlayer mediaPlayer;
  private int startId;
  public enum Control { PLAY, PAUSE, STOP }

  public MyService() { }

  @Override
  public void onCreate() {

    if (mediaPlayer == null) {
       mediaPlayer = MediaPlayer.create(this, R.raw.music);
       mediaPlayer.setLooping(false);
    }

    Log.e(TAG, "onCreate");
    super.onCreate();
  }
  
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {

    this.startId = startId;
    Log.e(TAG, "onStartCommand---startId: " + startId);
    Bundle bundle = intent.getExtras();
  
    if (bundle != null) {
      Control control = (Control) bundle.getSerializable("Key");
      if (control != null) {
        switch (control) {
        case PLAY:
          play();
          break;
        case PAUSE:
          pause();
          break;
        case STOP:
          stop();
          break;
         }
      }
    }
    return super.onStartCommand(intent, flags, startId);
  }

  @Override
  public void onDestroy() {
  
    Log.e(TAG, "onDestroy");
    if (mediaPlayer != null) {
      mediaPlayer.stop();
      mediaPlayer.release();
    }

     super.onDestroy();
  }

  private void play() {
    if (!mediaPlayer.isPlaying()) {
       mediaPlayer.start();
    }
  }

 private void pause() {
    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
       mediaPlayer.pause();
    }
  }
  
  private void stop() {
    if (mediaPlayer != null) {
      mediaPlayer.stop();
    }
   stopSelf(startId);
  }

  @Override
  public IBinder onBind(Intent intent) {
    Log.e(TAG, "onBind");
    throw new UnsupportedOperationException("Not yet implemented");
  }
}



 

在佈局中添加三個按鈕,用於控制音樂播放、暫停與中止

public class MainActivity extends AppCompatActivity { 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
  
  public void playMusic(View view) {
    Intent intent = new Intent(this, MyService.class);
    Bundle bundle = new Bundle();
    bundle.putSerializable("Key", MyService.Control.PLAY);
    intent.putExtras(bundle);
    startService(intent);
  }

  public void pauseMusic(View view) {
    Intent intent = new Intent(this, MyService.class);
    Bundle bundle = new Bundle();
    bundle.putSerializable("Key", MyService.Control.PAUSE);
    intent.putExtras(bundle); startService(intent);
  }

  public void stopMusic(View view) {
    Intent intent = new Intent(this, MyService.class);
    Bundle bundle = new Bundle();
    bundle.putSerializable("Key", MyService.Control.STOP);
    intent.putExtras(bundle); startService(intent); //或者是直接以下調用 //Intent intent = new Intent(this, MyService.class); //stopService(intent);
  }
}

 

在清單文件中聲明Service,爲其添加label標籤,便於在系統中識別Service

<service android:name=".MyService" android:label="@string/app_name" />

 

這裏寫圖片描述

點擊「播放音樂」按鈕後,在後臺將會運行着名爲「Service測試」的服務

 

這裏寫圖片描述:

  1. 經過Log日誌能夠發現,屢次點擊「播放音樂」按鈕,「onCreate()」方法只會在初始時調用一次,「onStartCommand(Intent intent, int flags, int startId)」方法會在每次點擊時都被調用,點擊「中止音樂」按鈕,「onDestroy()」方法會被調用當中,每次回調onStartCommand()方法時。

  2. 參數「startId」的值都是遞增的,startId用於惟一標識每次對Service發起的處理請求。

  3. 若是服務同時處理多個 onStartCommand() 請求,則不該在處理完一個啓動請求以後當即銷燬服務,由於此時可能已經收到了新的啓動請求,在第一個請求結束時中止服務會致使第二個請求被終止。爲了不這一問題,可使用 stopSelf(int) 確保服務中止

   請求始終基於最新一次的啓動請求。也就是說,若是調用 stopSelf(int) 方法的參數值與onStartCommand()接受到的最新的startId值不相符的話,stopSelf()方法就會失效,從而避免終止還沒有處理的請求

  4. 若是服務沒有提供綁定,則使用 startService() 傳遞的 Intent 是應用組件與服務之間惟一的通訊模式。若是但願服務返回結果,則啓動服務的客戶端能夠爲廣播建立一個 PendingIntent (使用 getBroadcast()),並經過啓動服務的 Intent 傳遞給服務。而後,

   服務就可使用廣播傳遞結果

  5. 當中,onStartCommand() 方法必須返回一個整數,用於描述系統應該如何應對服務被殺死的狀況,

    返回值必須是如下常量之一:

  • START_NOT_STICKY

   若是系統在 onStartCommand() 返回後終止服務,則除非有掛起 Intent 要傳遞,不然系統不會重建服務。這是最安全的選項,能夠避免在沒必要要時以及應用可以輕鬆重啓全部未完成的做業時運行服務

  • START_STICKY

   若是系統在 onStartCommand() 返回後終止服務,則會重建服務並調用 onStartCommand(),但不會從新傳遞最後一個 Intent。相反,除非有掛起 Intent 要啓動服務(在這種狀況下,將傳遞這些 Intent ),不然系統會經過空 Intent 調用 onStartCommand()。這適用於不執行命令、

   但無限期運行並等待做業的媒體播放器(或相似服務)

  • START_REDELIVER_INTENT

   若是系統在 onStartCommand() 返回後終止服務,則會重建服務,並經過傳遞給服務的最後一個 Intent 調用 onStartCommand()。任何掛起 Intent 均依次傳遞。這適用於主動執行應該當即恢復的做業(例以下載文件)的服務

 

3.二、IntentService

因爲大多數啓動服務都沒必要同時處理多個請求,所以使用 IntentService 類實現服務也許是最好的選擇

IntentService 執行如下操做:

  • 建立默認的工做線程,用於在應用的主線程外執行傳遞給 onStartCommand() 的全部 Intent
  • 建立工做隊列,用於將 Intent 逐一傳遞給 onHandleIntent() 實現,這樣就沒必要擔憂多線程問題
  • 在處理完全部啓動請求後中止服務,所以沒必要本身調用 stopSelf()方法
  • 提供 onBind() 的默認實現(返回 null)
  • 提供 onStartCommand() 的默認實現,可將 Intent 依次發送到工做隊列和 onHandleIntent()

所以,只需實現構造函數與 onHandleIntent() 方法便可

這裏舉一個關於輸出日誌的例子

public class MyIntentService extends IntentService { 
  private final String TAG = "MyIntentService";
  public MyIntentService() {
    super("MyIntentService");
  }

  @Override
  protected void onHandleIntent(Intent intent) {
    Bundle bundle = intent.getExtras();
    if (bundle != null) {
      for (int i = 0; i < 5; i++) {
        try {
          Thread.sleep(200);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        Log.e(TAG, bundle.getString("key", "默認值"));
      }
    }
  }
}
public class StartIntentServiceActivity extends AppCompatActivity {
  private int i = 1;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_start_intent_service);
  }

  public void startService(View view) {
    Intent intent = new Intent(this, MyIntentService.class);
    Bundle bundle = new Bundle();
    bundle.putString("key", "當前值:" + i++);
    intent.putExtras(bundle);
    startService(intent);
  }
}

 

當中,startService(View view)方法與一個Button綁定,連續快速地屢次點擊Button,驗證IntentService當中的日誌是否依次輸出,仍是交叉着輸出

能夠看到是依次輸出的,即IntentService的工做線程是逐一處理全部啓動請求的

這裏寫圖片描述

此外,查看後臺,能夠看到當先後臺應用程序進程中有兩個服務

這裏寫圖片描述

 

4、綁定Service

  應用組件(客戶端)經過調用 bindService() 綁定到服務,綁定是異步的,系統隨後調用服務的 onBind() 方法,該方法返回用於與服務交互的 IBinder。要接收 IBinder,客戶端必須提供一個 ServiceConnection 實例用於監控與服務的鏈接,並將其傳遞給 bindService()。當 Android 系統建立了客戶端與服務之間的鏈接時,會回調ServiceConnection 對象的onServiceConnected()方法,向客戶端傳遞用來與服務通訊的 IBinder多個客戶端可同時鏈接到一個服務。不過,只有在第一個客戶端綁定時,系統纔會調用服務的 onBind() 方法來檢索 IBinder。系統隨後無需再次調用 onBind(),即可將同一 IBinder 傳遞至其餘綁定的客戶端。當全部客戶端都取消了與服務的綁定後,系統會將服務銷燬(除非 startService() 也啓動了該服務)。

另外,只有 Activity、服務和內容提供者能夠綁定到服務,沒法從廣播接收器綁定到服務

能夠經過如下三種方法定義IBinder接口:

  • 擴展 Binder 類

    若是服務是供本應用專用,而且運行在與客戶端相同的進程中,則應經過擴展 Binder 類並從 onBind() 返回它的一個實例來建立接口。客戶端收到 Binder 後,可利用它直接訪問 Service 中可用的公共方法

  • 使用 Messenger

    如需讓接口跨不一樣的進程工做,則可以使用 Messenger 爲服務建立接口。服務能夠這種方式定義對應於不一樣類型 Message 對象的 Handler。此 Handler 是 Messenger 的基礎,後者隨後可與客戶端分享一個 IBinder,從而讓客戶端能利用 Message 對象向服務發送命令。此外,客戶端還可定義自有 Messenger,以便服務回傳消息。這是執行進程間通訊 (IPC) 的最簡單方法,由於 Messenger 會在單一線程中建立包含全部請求的隊列,這樣就沒必要對服務進行線程安全設計

  • 使用 AIDL

    AIDL(Android 接口定義語言)執行全部將對象分解成原語的工做,操做系統能夠識別這些原語並將它們編組到各進程中,以執行 IPC。 以前採用 Messenger 的方法其實是以 AIDL 做爲其底層結構。 如上所述,Messenger 會在單一線程中建立包含全部客戶端請求的隊列,以便服務一次接收一個請求。 不過,若是想讓服務同時處理多個請求,則可直接使用 AIDL。 在此狀況下,服務必須具有多線程處理能力,並採用線程安全式設計。如需直接使用 AIDL,必須建立一個定義編程接口的 .aidl 文件。Android SDK 工具利用該文件生成一個實現接口並處理 IPC 的抽象類,隨後可在服務內對其進行擴展

 

4.一、綁定服務的具體步驟:

4.1.一、擴展 Binder 類

  若是服務僅供本地應用使用,不須要跨進程工做,則能夠實現自有 Binder 類,讓客戶端經過該類直接訪問服務中的公共方法。此方法只有在客戶端和服務位於同一應用和進程內這一最多見的狀況下方纔有效

如下是具體的設置方法:

  • 在服務中建立一個可知足下列任一要求的 Binder 實例:
    • 包含客戶端可調用的公共方法
    • 返回當前 Service 實例,其中包含客戶端可調用的公共方法
    • 或返回由服務承載的其餘類的實例,其中包含客戶端可調用的公共方法
  • 從 onBind() 回調方法返回此 Binder 實例
  • 在客戶端中,從 onServiceConnected() 回調方法接收 Binder,並使用提供的方法調用綁定服務

4.1.二、實現 ServiceConnection接口

重寫兩個回調方法:

  • onServiceConnected()

系統會調用該方法以傳遞服務的onBind() 方法返回的 IBinder

  • onServiceDisconnected()

Android 系統會在與服務的鏈接意外中斷時,例如當服務崩潰或被終止時調用該方法。當客戶端取消綁定時,系統不會調用該方法

4.1.三、調用 bindService(),傳遞 ServiceConnection 對象

4.1.四、當系統調用了 onServiceConnected() 的回調方法時,就能夠經過IBinder對象操做服務了

4.1.五、要斷開與服務的鏈接需調用 unbindService()方法。若是應用在客戶端仍處於綁定狀態時銷燬客戶端,會致使客戶端取消綁定,更好的作法是在客戶端與服務交互完成後當即取消綁定客戶端,這樣能夠關閉空閒服務

示例代碼:

public class MyBindService extends Service { 

  private IBinder myBinder;
  private Random mGenerator;
  private final String TAG = "MyBindService";

  public class MyBinder extends Binder {
    MyBindService getService() {
      return MyBindService.this;
    }
  }
  
  @Override
  public void onCreate() {
    Log.e(TAG, "onCreate");
    myBinder = new MyBinder();
    mGenerator = new Random();
    super.onCreate();
  }
  
  @Override
  public IBinder onBind(Intent intent) {
    Log.e(TAG, "onBind");
    return myBinder;
  }

  @Override
  public void onDestroy() {
    Log.e(TAG, "onDestroy");
    super.onDestroy();
  }

  @Override
  public boolean onUnbind(Intent intent) {
    Log.e(TAG, "onUnbind");
    return super.onUnbind(intent);
  }

  @Override
  public void onRebind(Intent intent) {
    Log.e(TAG, "onRebind");
    super.onRebind(intent);
  }

  public int getRandomNumber() {
    return mGenerator.nextInt(100);
  }
}
public class BindServiceActivity extends AppCompatActivity {
  private MyBindService mService;
  private boolean mBound = false;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_bind_service);
  }
  private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      MyBindService.MyBinder binder = (MyBindService.MyBinder) service;
      mService = binder.getService(); mBound = true;
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
      mBound = false;
    }
  };
  
  public void bindService(View view) {
    Intent intent = new Intent(this, MyBindService.class);
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
  }

  public void unBindService(View view) {
    if (mBound) {
      unbindService(mConnection); mBound = false;
    }
  }

  public void getData(View view) {
    if (mBound) {
      Toast.makeText(this, "獲取到的隨機數:" + mService.getRandomNumber(), Toast.LENGTH_SHORT).show();
    } else {
      Toast.makeText(this, "服務未綁定", Toast.LENGTH_SHORT).show();
    }
  }   
  @Override
  protected void onDestroy() {
    super.onDestroy();
    if (mBound) {
      unbindService(mConnection);
      mBound = false;
    }
  }
}

 

4.二、綁定服務的生命週期

綁定服務的生命週期在同時啓動服務的狀況下比較特殊,想要終止服務,除了須要取消綁定服務外,還須要服務經過 stopSelf() 自行中止或其餘組件調用 stopService()

其中,若是服務已啓動並接受綁定,則當系統調用了onUnbind() 方法,想要在客戶端下一次綁定到服務時調用 onRebind() 方法的話,則onUnbind() 方法需返回 true。onRebind() 返回空值,但客戶端仍能夠在其 onServiceConnected() 回調中接收到 IBinder對象

這裏寫圖片描述

4.三、綁定時機

  • 若是隻須要在 Activity 可見時與服務交互,則應在 onStart() 期間綁定,在 onStop() 期間取消綁定
  • 若是但願 Activity 在後臺中止運行狀態下仍可接收響應,則可在 onCreate() 期間綁定,在 onDestroy() 期間取消綁定。這意味着 Activity 在其整個運行過程當中(包括後臺運行期間)都須要使用此服務
  • 一般狀況下,切勿在 Activity 的 onResume() 和 onPause() 期間綁定和取消綁定,由於每一次生命週期轉換都會發生這些回調,應該使發生在這些轉換期間的處理保持在最低水平。假設有兩個Activity須要綁定到同一服務,從Activity A跳轉到Activity B,這個過程當中會依次執行A-onPause,B-onCreate,B-onStart,B-onResume,A-onStop。這樣系統會在A-onPause的時候銷燬服務,又在B-onResume的時候重建服務。當Activity B回退到Activity A時,會依次執行B-onPause,A-onRestart,A-onStart,A-onResume,B-onStop,B-onDestroy。此時,系統會在B-onPause時銷燬服務,又在A-onResume時重建服務。這樣就形成了屢次的銷燬與重建,所以須要選定好綁定服務與取消綁定服務的時機

5、在前臺運行Service

前臺服務被認爲是用戶主動意識到的一種服務,所以在內存不足時,系統也不會考慮將其終止。 前臺服務必須在狀態欄提供通知,放在「正在進行」標題下方,這意味着除非服務中止或從前臺移除,不然不能清除通知

要請求讓服務運行於前臺,要調用 startForeground()方法,兩個參數分別是:惟一標識通知的int類型整數和Notification對象

修改MyService當中的play()方法

private void play() { 
  if (!mediaPlayer.isPlaying()) {
    mediaPlayer.start();
    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
    mBuilder.setSmallIcon(R.drawable.bird);
    mBuilder.setContentTitle("這是標題吧~葉應是葉");
    mBuilder.setContentText("http://blog.csdn.net/new_one_object");
    startForeground(1, mBuilder.build());
  }
}

 

點擊播放音樂後,狀態欄就出現了一個通知

這裏寫圖片描述

當中,提供給 startForeground() 的整型參數不得爲 0。要從前臺移除服務,需調用 stopForeground()方法,此方法不會中止服務。 可是,若是前臺服務被中止,則通知也會被移除

6、Service的生命週期

服務生命週期從建立到銷燬能夠遵循兩條不一樣的路徑:

  • 啓動服務

該服務在其餘組件調用 startService() 時建立,而後無限期運行,必須經過調用 stopSelf() 來自行中止運行或經過其餘組件調用 stopService() 來中止服務。服務中止後,系統會將其銷燬

  • 綁定服務

服務在另外一個組件(客戶端)調用 bindService() 時建立。而後,客戶端經過 IBinder 接口與服務進行通訊。客戶端能夠經過調用 unbindService() 關閉鏈接。多個客戶端能夠綁定到相同服務,並且當全部綁定所有取消後,系統即會銷燬該服務(服務沒必要自行中止運行)

這兩條路徑並不是徹底獨立。也就是說,能夠綁定到已經使用 startService() 啓動的服務。例如,能夠經過使用 Intent(標識要播放的音樂)調用 startService() 來啓動後臺音樂服務。隨後,可能在用戶須要稍加控制播放器或獲取有關當前播放歌曲的信息時,Activity 能夠經過調用 bindService() 綁定到服務。在這種狀況下,除非全部客戶端均取消綁定,不然 stopService() 或 stopSelf() 不會實際中止服務

相關文章
相關標籤/搜索