Service小記

最近剛入職,工作中發現自己的能力是在太差了.遂決定重頭學習,Activity的文章已經寫過了,所以就先從Service開始寫博客了.

先來扯點

Service作為Android四大組件之一,相信學過Android的都知道這貨,它在每一個應用程序中都扮演著重要的角色.
Service是一個可以在后臺執行長時間運行操作而不使用用戶界面的應用組件.Service可由其他應用組件啟動,而且即使用戶切換到其他應用,Service仍將在后臺繼續運行.Service主要用于在后臺處理一些耗時的邏輯,或者去執行某些需要長期運行的任務.必要的時候我們甚至可以在程序退出的情況下,讓Service在后臺繼續保持運行狀態.
Service和Activity很相似,但是區別在于:Service一直在后臺運行,沒有用戶界面,所以不會到前臺,如果Service被啟動起來,就和Activity一樣,具有自己的聲明周期.

注:在開發中,Activity和Service的選擇標準是:如果程序組件在需要在運行時向用戶呈現某種界面,或者程序需要與用戶進行交互,那么使用Activity,否則考慮使用Service.

另外,需要注意的是,Service和Thread不是一個意思,不要被Service的后臺概念所迷惑.實際上Service并不會自動開啟線程,所有的代碼都是默認運行在主線程中的.因此,我們需要在Service的內部手動創建子線程,并在這里執行具體的任務,否則可能造成ANR的問題.

Service的形式

Service基本上分為兩種形式:

  • Started(啟動的)
    • 當應用組件(如 Activity)通過調用 startService() 啟動Service時,Service即處于“啟動”狀態.一旦啟動,Service即可在后臺無限期運行,即使啟動Service的組件已被銷毀也不受影響.通常,一個開啟的Service執行單一操作并且不會給調用者返回結果.例如,它可能通過網絡下載或上傳文件.操作完成后,Service會自行停止運行.
  • Bound(綁定的)
    • 當應用組件通過調用 bindService() 綁定到Service時,Service即處于“綁定”狀態.一個綁定的Service提供客戶端/服務器接口允許組件和Service交互,甚至跨進程操作使用進行間通信(IPC).僅當與另一個應用組件綁定時,綁定服務才會運行.多個組件可以同時綁定到該服務,但全部取消綁定后,該服務即會被銷毀.

Service的使用

幾個方法

想要創建一個service,你必須創建一個Service的子類(或一個它的存在的子類).在你的實現中,你需要重寫一些回調方法來處理Service生命周期的關鍵方面并且對于組件綁定到Service提供一個機制.應重寫的最重要的回調方法包括:

  • onStartCommand()
    • 當其它組件(例如activity)通過調用startService() 來請求Service啟動時系統會調用這個方法.一旦這個方法執行,service就啟動并且可以無限地運行在后臺.如果你實現這個,它主要負責當任務完成后停止service,通過調用stopSelf()stopService().(如果你只想提供綁定,你不需要實現這個方法.)
  • onBind()
    • 當其它組件通過調用bindService()來綁定到Service時系統會調用這個方法.在實現的這個方法中,你必須提供一個客戶端用來和Service交互的接口,通過返回一個IBinder.你必須實現這個方法,但如果你不想允許綁定,你應該返回null.
  • onCreate()
    • 首次創建Service時,系統將調用此方法來執行一次性設置程序(在調用 onStartCommand() 或 onBind() 之前).如果服務已在運行,則不會調用此方法.
  • onDestroy()
    • 當Service不再使用且將被銷毀時,系統將調用此方法.Service應該實現此方法來清理所有資源,如線程、注冊的偵聽器、接收器等.這是Service接收的最后一個調用.

如果希望在Service組件做某些事情,那么只要在onCreate()或onStratCommand()方法中定義相關業務代碼即可.而當服務銷毀時,我們應該在onDestroy()方法中去回收那些不再使用的資源.

使用清單文件聲明服務

如同 Activity(以及其他組件)一樣,您必須在應用的清單文件中聲明所有服務.

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

創建啟動Service

直接用代碼了,比較清楚.

  @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.d(TAG, "onCreate() executed");  
    }  
  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d(TAG, "onStartCommand() executed");  
        return super.onStartCommand(intent, flags, startId);  
    }  
      
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy() executed");  
    }  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        return null;  
    }  

然后在activity_main中添加2個按鈕.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical" >  
  
    <Button  
        android:id="@+id/start_service"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="Start Service" />  
  
    <Button  
        android:id="@+id/stop_service"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content"  
        android:text="Stop Service" />  
  
</LinearLayout>  

接著在MainActivity中添加以下代碼.

public class MainActivity extends Activity implements OnClickListener {  
    private Button startService;  
    private Button stopService;  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        startService = (Button) findViewById(R.id.start_service);  
        stopService = (Button) findViewById(R.id.stop_service);  
        startService.setOnClickListener(this);  
        stopService.setOnClickListener(this);  
    }  
  
    @Override  
    public void onClick(View v) {  
        switch (v.getId()) {  
        case R.id.start_service:  
            Intent startIntent = new Intent(this, MyService.class);  
            startService(startIntent);  
            break;  
        case R.id.stop_service:  
            Intent stopIntent = new Intent(this, MyService.class);  
            stopService(stopIntent);  
            break;  
        default:  
            break;  
        }  
    }  
}  

最后在AndroidManifest.xml中注冊.

    <service android:name="com.example.servicetest.MyService" >  </service>  

這里我們重寫了onCreate()、onStartCommand()、onDestroy()這三個方法.

  • onCreate():在服務創建的時候調用.
  • onStartCommand():在每次服務啟動的時候調用.
  • onDestroy():在服務銷毀的時候調用.
    運行之后,我們做了以下事情.
    當點擊STARTSERVICE按鈕后,控制臺打印的Log如圖:



    當在此點擊STARTSERVICE按鈕后,控制臺打印的Log如圖:



    我們發現此時onCreate()方法沒有執行,onStartCommand()繼續執行了.多點幾次后還是這樣.為什么會這樣呢?這是由于onCreate()方法只會在Service第一次被創建的時候調用,如果當前Service已經被創建過了,不管怎樣調用startService()方法,onCreate()方法都不會再執行.因此你可以再多點擊幾次Start Service按鈕試一次,每次都只會有onStartCommand()方法中的打印日志.
    當點擊STOPSERVICE按鈕后,控制臺打印的Log如圖:

注:在MyService的任何一個位置調用stopSelf()方法就能讓Service自己停下來

Service和Activity通信

接下來,我們讓Service和Activity的關系更緊密一些,來實現在Activity中指揮Service去干活.而這需要借助onBind()方法.
還是看代碼吧.
修改MyService里面的代碼:

...
private MyBinder mBinder = new MyBinder();
...
  @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

     class MyBinder extends Binder {
         public void startDownload(){
             Logger.t(TAG).d("執行startDownload()方法");
             //執行具體下載任務
         }
    }

這里我們新增了一個MyBinder類繼承自Binder類,然后在MyBinder中添加了一個startDownload()方法用于在后臺執行下載任務.
然后再在activity_main中添加2個按鈕

 <Button
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Bind Service" />

    <Button
        android:id="@+id/unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Unbind Service"
        />

修改MainActivity的代碼:

 public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button start, stop;
    private Button bindService;

    private Button unbindService;

    private MyService.MyBinder myBinder;

    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
            myBinder.startDownload();
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        start = (Button) findViewById(R.id.Start_Service);
        stop = (Button) findViewById(R.id.Stop_Service);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);


        start.setOnClickListener(this);
        stop.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.Start_Service:
                Intent startIntent = new Intent(this,MyService.class);
                startService(startIntent);
                break;
            case R.id.Stop_Service:
                Intent stopIntent = new Intent(this,MyService.class);
                stopService(stopIntent);
                break;
            case R.id.bind_service:
                Intent bindIntent = new Intent(this, MyService.class);
                bindService(bindIntent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind_service:
                unbindService(connection);
                break;
        }
    }
}


可以看到,這里我們首先創建了一個ServiceConnection的匿名類,在里面重寫了onServiceConnected()方法和onServiceDisconnected()方法,這兩個方法分別會在Activity與Service建立關聯和解除關聯的時候調用.在onServiceConnected()方法中,我們又通過向下轉型得到了MyBinder的實例,有了這個實例,Activity和Service之間的關系就變得非常緊密了.
當然,現在Activity和Service其實還沒關聯起來了呢,這個功能是在Bind Service按鈕的點擊事件里完成的.可以看到,這里我們仍然是構建出了一個Intent對象,然后調用bindService()方法將Activity和Service進行綁定.bindService()方法接收三個參數,第一個參數就是剛剛構建出的Intent對象,第二個參數是前面創建出的ServiceConnection的實例,第三個參數是一個標志位.這里傳入BIND_AUTO_CREATE表示在Activity和Service建立關聯后自動創建Service,這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行.
然后如何我們想解除Activity和Service之間的關聯怎么辦呢?調用一下unbindService()方法就可以了,這也是Unbind Service按鈕的點擊事件里實現的邏輯.
好的,我們點擊試試.
點擊BIND SERVICE按鈕,輸出如圖:


當再多次點擊則不會再有Log輸出.
點擊UBIND SERVICE按鈕,輸出如圖:

注:任何一個Service在整個應用程序范圍內都是通用的,即MyService不僅可以和MainActivity建立關聯,還可以和任何一個Activity建立關聯,而且在建立關聯時它們都可以獲取到相同的MyBinder實例.

銷毀Service的方法

根據之前的測試,我們知道,點擊STARTSERVICE按鈕啟動Service,再點擊STOPSERVICE按鈕停止Service,這樣MyService就被銷毀了.
如果我們是點擊的Bind Service按鈕呢?由于在綁定Service的時候指定的標志位是BIND_AUTO_CREATE,說明點擊BindService按鈕的時候Service也會被創建,這時應該呢?其實也很簡單,點擊一下UnbindService按鈕,將Activity和Service的關聯解除,從而銷毀Service.
如果我們既點擊了StartService按鈕,又點擊了BindService按鈕,這個時候你會發現,不管你是單獨點擊Stop Service按鈕還是Unbind Service按鈕,Service都不會被銷毀,必要將兩個按鈕都點擊一下,Service才會被銷毀.也就是說,點擊StopService按鈕只會讓Service停止,點擊Unbind Service按鈕只會讓Service和Activity解除關聯,一個Service必須要在既沒有和任何Activity關聯又處理停止狀態的時候才會被銷毀.
我們測試下.

public void onClick(View v) {  
    switch (v.getId()) {  
    case R.id.start_service:  
        Intent startIntent = new Intent(this, MyService.class);  
        startService(startIntent);  
        break;  
    case R.id.stop_service:  
        Log.d("MyService", "click Stop Service button");  
        Intent stopIntent = new Intent(this, MyService.class);  
        stopService(stopIntent);  
        break;  
    case R.id.bind_service:  
        Intent bindIntent = new Intent(this, MyService.class);  
        bindService(bindIntent, connection, BIND_AUTO_CREATE);  
        break;  
    case R.id.unbind_service:  
        Log.d("MyService", "click Unbind Service button");  
        unbindService(connection);  
        break;  
    default:  
        break;  
    }  
}  

重新運行程序,先點擊一下STARTSERVICE按鈕,再點擊一下BindService按鈕,這樣就將Service啟動起來,并和Activity建立了關聯.然后點擊StopService按鈕后Service并不會銷毀,再點擊一下UnbindService按鈕,Service就會銷毀了,打印日志如下所示:


注:根據Android系統的機制,一個服務只要被啟動或者被綁定之后,就會一直處于運行狀態

Service的生命周期

服務的生命周期比 Activity 的生命周期要簡單得多.但是,密切關注如何創建和銷毀服務反而更加重要,因為服務可以在用戶沒有意識到的情況下運行于后臺.
服務生命周期(從創建到銷毀)可以遵循兩條不同的路徑:

  • 啟動服務
    • 該服務在其他組件調用 startService() 時創建,然后無限期運行,且必須通過調用 stopSelf() 來自行停止運行.此外,其他組件也可以通過調用 stopService() 來停止服務.服務停止后,系統會將其銷毀.
  • 綁定服務
    • 該服務在另一個組件(客戶端)調用 bindService() 時創建.然后,客戶端通過 IBinder 接口與服務進行通信.客戶端可以通過調用 unbindService() 關閉連接.多個客戶端可以綁定到相同服務,而且當所有綁定全部取消后,系統即會銷毀該服務. (服務不必自行停止運行.)
      這兩條路徑并非完全獨立.也就是說,您可以綁定到已經使用 startService() 啟動的服務.例如,可以通過使用 Intent(標識要播放的音樂)調用 startService() 來啟動后臺音樂服務.隨后,可能在用戶需要稍加控制播放器或獲取有關當前播放歌曲的信息時,Activity 可以通過調用 bindService() 綁定到服務.在這種情況下,除非所有客戶端均取消綁定,否則 stopService() 或 stopSelf() 不會真正停止服務.

實現生命周期回調

與 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 生命周期回調方法不同,您不需要調用這些回調方法的超類實現.

Service生命周期

服務生命周期左圖顯示了使用 startService() 所創建的服務的生命周期,右圖顯示了使用 bindService() 所創建的服務的生命周期.對于啟動服務,有效生命周期與整個生命周期同時結束(即便是在 onStartCommand() 返回之后,服務仍然處于活動狀態).對于綁定服務,有效生命周期在 onUnbind() 返回時結束.

通過實現這些方法,您可以監控服務生命周期的兩個嵌套循環:

  • 服務的整個生命周期從調用 onCreate() 開始起,到 onDestroy() 返回時結束.與 Activity 類似,服務也在 onCreate() 中完成初始設置,并在 onDestroy() 中釋放所有剩余資源.例如,音樂播放服務可以在 onCreate() 中創建用于播放音樂的線程,然后在 onDestroy() 中停止該線程.
    無論服務是通過 startService() 還是 bindService() 創建,都會為所有服務調用 onCreate() 和 onDestroy() 方法.
  • 服務的有效生命周期從調用 onStartCommand() 或 onBind() 方法開始.每種方法均有 Intent 對象,該對象分別傳遞到 startService() 或 bindService().

注:盡管啟動服務是通過調用 stopSelf() 或 stopService() 來停止,但是該服務并無相應的回調(沒有 onStop() 回調).因此,除非服務綁定到客戶端,否則在服務停止時,系統會將其銷毀—onDestroy() 是接收到的唯一回調.


參考:
Service
Android Service完全解析,關于服務你所需知道的一切(上)
Android Service完全解析,關于服務你所需知道的一切(下)
Android開發指南——Service
《第一行代碼》第9章
《瘋狂Android講義第2版》第10章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容