1. 程式人生 > >安卓服務service全解,生命週期,前臺服務、後臺服務,啟動登出、繫結解綁,註冊

安卓服務service全解,生命週期,前臺服務、後臺服務,啟動登出、繫結解綁,註冊

全棧工程師開發手冊 (作者:欒鵬)

定義服務(服務的生命週期)

這裡寫圖片描述

呼叫context.startService()時依次執行 ->onCreate()- >onStartCommand()->Service running

呼叫context.stopService()時依次執行 ->onDestroy()

呼叫context.bindService()時依次執行->onCreate()->onBind()->Service running

呼叫context.onUnbind()時依次執行 -> onDestroy()

當繫結service和所有客戶端解除繫結之後,Android系統將會銷燬它,(除非它同時被onStartCommand()方法開啟)。

因此,如果你的service是一個純粹的繫結service,那麼你不需要管理它的生命週期。

然而,如果你選擇實現onStartCommand()回撥方法,那麼你必須顯式地停止service,因為service此時被看做是開啟的。

這種情況下,service會一直執行到它自己呼叫 stopSelf()或另一個元件呼叫stopService(),不論它是否和客戶端繫結。

另外,如果你的service被開啟並且接受繫結,那麼當系統呼叫你的 onUnbind()方法時,如果你想要在下次客戶端繫結的時候接受一個onRebind()的呼叫(而不是呼叫 onBind()),你可以選擇在 onUnbind()中返回true。

onRebind()的返回值為void,但是客戶端仍然在它的 onServiceConnected()回撥方法中得到 IBinder 物件。

下圖展示了這種service(被開啟,還允許繫結)的生命週期:
這裡寫圖片描述

程式碼示例:

程式碼中設計服務的生命週期,服務設定為前臺服務和後臺服務。

package com.lp.app.service;


import com.lp.app.Activity1;
import com.lp.app.R;

import android.app.Notification;
import android.app.PendingIntent;
import
android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; //服務的生命週期 public class Service1 extends Service{ public final static String action_name = "com.lp.action.service1"; public final static String key1 = "key1"; public final static String key2 = "key2"; //當服務被建立時,執行oncreat函式 @Override public void onCreate() { Log.v("服務生命週期", "服務第一次被啟動"); pausePlayback(); //將服務放置在後臺。預設服務就是後臺服務。前臺服務是一個通知欄 startPlayback("顯示標題","顯示文字"); //開啟一個通知欄,點選通知,可以將服務移動至前臺 } //onStartCommand為service的重新啟動函式 @Override public int onStartCommand(Intent intent, int flags, int startId) { //startBackgroundTask(intent, startId); //將服務設定到後臺執行 //START_STICKY:如果service程序被kill掉,保留service的狀態為開始狀態,但不保留遞送的intent物件。隨後系統會嘗試重新建立service,重新啟動後引數Intent將為null。 //START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啟該服務。 //START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統會自動重啟該服務,並將原來Intent的值傳入。 //START_STICKY_COMPATIBILITY:START_STICKY的相容版本,但不保證服務被kill後一定能重啟。 //引數flags可以用來確定service的啟動方式。START_FLAG_REDELIVERY表示Intent引數是由系統執行時在通過stopSelf顯式停止service之前終止它而重新傳遞的。 //FLAF_RETRY表示service已經在異常終止後重新啟動,如果service之前被設為START_STICKY,則會傳入這個標誌 Log.v("服務生命週期", "服務被其他視窗通過startService()啟動"); return Service.START_STICKY; } public class MyBinder extends Binder { Service1 getService() { return Service1.this; } } private final IBinder binder = new MyBinder(); @Override public IBinder onBind(Intent intent) { // TODO 自動生成的方法存根 Log.v("服務生命週期", "一個客戶端正在通過bindService()函式繫結到本服務"); return binder; } @Override public void onRebind(Intent intent) { //在onUnbind()函式已經被呼叫過後執行 Log.v("服務生命週期", "一個客戶端正在通過bindService()函式繫結到當前服務"); } @Override public boolean onUnbind(Intent intent) { Log.v("服務生命週期", "所有客戶端已經通過unbindService()函式脫離繫結"); return true; //返回允許繫結 } @Override public void onStart(Intent intent,int startId){ Log.v("服務生命週期", "服務啟動"); super.onStart(intent, startId); } @Override public void onDestroy(){ Log.v("服務生命週期", "服務銷燬"); super.onDestroy(); } //將一個service移動至前臺 //(前臺服務:會在通知一欄顯示 ONGOING 的 Notification。當服務被終止的時候,通知一欄的 Notification 也會消失,這樣對於使用者有一定的通知作用) //前臺服務具有較高的優先順序,能在記憶體不足時,不被殺死 private void startPlayback(String contenttitle, String contenttext) { int NOTIFICATION_ID = 1; //建立一個當單擊通知時將開啟主activity的intent Intent intent = new Intent(this, Activity1.class); PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0); //設定Notification UI引數 Notification notification = new Notification(R.drawable.icon,"啟動app視窗", System.currentTimeMillis()); notification.setLatestEventInfo(this, contenttitle, contenttext, pi); //設定notification為持續顯示 notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT; //將service移到前臺。 startForeground(NOTIFICATION_ID, notification); } //將service移動到後臺 public void pausePlayback() { //移動到後臺並移除Notification stopForeground(true); } }

在manifest檔案中註冊服務

這裡我們hi演示顯式啟動服務和隱式啟動服務。所有這裡處理設定服務的名稱,還設定了觸發條件

<!-- service註冊服務,其中permission表示要想外部應用程式使用這個服務,必須要包含的自定義許可權(只是個名稱) -->
<service 
    android:name="com.lp.app.service.Service1"
    android:permission="com.lp.my_service1_permission">
    <intent-filter>
        <action android:name="com.lp.action.service1"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</service> 

顯式的啟動和終止服務

  Intent serviceIntent=null;
  //顯示啟動一個服務
  private void explicitStart() {
    serviceIntent = new Intent(this, Service1.class);
    startService(serviceIntent);
  }

  //顯式終止一個服務
  private void explicitStop() {
      if(serviceIntent!=null)  
          stopService(serviceIntent);
  }

隱式的啟動和終止服務

隱式啟動,相當於把自定義服務註冊為系統服務,再以啟動系統服務的方式啟動自定義服務。

這種方式的和顯式的啟動和停止服務不同,而是通過intent觸發指定名稱的事件。而這個事件又觸發了註冊在manifest檔案中的service,所以需要在manifest檔案中註冊服務時,設定觸發源

  //隱式的啟動一個Service。相當於把自定義服務註冊為系統服務,再以啟動系統服務的方式啟動自定義服務
  private void implicitStart() {
    Intent intent = new Intent(Service1.action_name);   //在註冊服務是設定了intent-filter,所以啟動這個,可以啟動對應的服務
    intent.putExtra(Service1.key1, "value1"); 
    intent.putExtra(Service1.key2, "value2"); 
    startService(intent);
  }

  //隱式終止一個服務
  private void implicitStop() {
      Intent intent = new Intent(Service1.action_name);
      stopService(intent); 
  }

繫結和解除繫結


 //為Service繫結建立一個service連線
  //service的引用
  private Service1 serviceRef;

  //處理service和 activity之間的連線
  private ServiceConnection mConnection = new ServiceConnection() {
       //當建立連線時呼叫此函式
       public void onServiceConnected(ComponentName className, IBinder service) {
         serviceRef = ((Service1.MyBinder)service).getService();
         Log.v("服務繫結客戶端", "服務繫結建立連線");
       }
       //當service意外斷開時執行
       public void onServiceDisconnected(ComponentName className) {
         serviceRef = null;
         Log.v("服務繫結客戶端", "服務繫結斷開連線");
       }
  };

  Intent bindIntent=null;
  //繫結一個service和activity
  private void bindToService() {
    bindIntent = new Intent(this, Service1.class);
    bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
    //BIND_AUTO_CREATE表示收到繫結請求的時候,如果服務尚未建立,則即刻建立,在系統記憶體不足需要先摧毀優先順序元件來釋放記憶體,且只有駐留該服務的程序成為被摧毀物件時,服務才被摧毀
    //BIND_DEBUG_UNBIND通常用於除錯場景中判斷繫結的服務是否正確,但容易引起記憶體洩漏,因此非除錯目的的時候不建議使用
    //BIND_NOT_FOREGROUND表示系統將阻止駐留該服務的程序具有前臺優先順序,僅在後臺執行,該標誌位位於Froyo中引入
  }

  //解除一個繫結
  private void unbindToService() {
    if (bindIntent!=null) {
        unbindService(mConnection);
    }
  }