1. 程式人生 > >Android四大元件--Service詳解

Android四大元件--Service詳解

Service 作為 Android 四大元件之一,可以說是一個合格的 Android 應用的基石。它主要用於在後臺處理一些耗時的邏輯,或者去執行某些需要長期執行的任務。必要的時候我們甚至可以在程式退出的情況下,依然讓 Service 在後臺保持執行狀態。

1、Service介紹

Service 是一個可以在後臺執行長時間執行操作而不提供使用者介面的應用元件。服務可由其他應用元件啟動,而且即使使用者切換到其他應用,服務仍將在後臺繼續執行。 此外,元件可以繫結到服務,以與之進行互動,甚至是執行程序間通訊 (IPC)。 例如,服務可以處理網路事務、播放音樂,執行檔案 I/O 或與內容提供程式互動,而所有這一切均可在後臺進行。

服務基本上分為兩種形式:

  • 啟動

    當應用元件(如 Activity)通過呼叫 startService() 啟動服務時,服務即處於“啟動”狀態。一旦啟動,服務即可在後臺無限期執行,即使啟動服務的元件已被銷燬也不受影響。 已啟動的服務通常是執行單一操作,而且不會將結果返回給呼叫方。例如,它可能通過網路下載或上傳檔案。 操作完成後,服務會自行停止執行。

  • 繫結

    當應用元件通過呼叫 bindService() 繫結到服務時,服務即處於“繫結”狀態。繫結服務提供了一個客戶端-伺服器介面,允許元件與服務進行互動、傳送請求、獲取結果,甚至是利用程序間通訊 (IPC) 跨程序執行這些操作。 僅當與另一個應用元件繫結時,繫結服務才會執行。 多個元件可以同時繫結到該服務,但全部取消繫結後,該服務即會被銷燬。

對於 Service 的介紹,官方有詳細的介紹

2、基本用法

關於 Service 最基本的用法當然是在程式中啟動一個 Service 啦,我們就來介紹下這個流程。

首先寫一個繼承 Service 的類:

public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("TAG", "onCreate()");
    }

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

Service 也有 onCreate(),具體的在下面介紹,Service 第一次被建立就會呼叫 onCreate()。

Service 的啟動與 Activity 的方式相似,也是使用 Intent:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }
}

每一個 Service 我們若想使用都必須在 AndroidManifest.xml 中註冊,在 application 標籤下:

<service android:name=".MyService"/>

這樣 MyService 就可以啟動了。

要想停止 Service,呼叫一下 stopService():

stopService(intent); 

在 AndroidManifest.xml 中,service 元素的幾個屬性:

  • android:name:服務類名,唯一必需的屬性。

  • android:label:服務的名字,如果此項不設定,那麼預設顯示的服務名則為類名。

  • android:icon:服務的圖示。

  • android:permission:申明此服務的許可權,這意味著只有提供了該許可權的應用才能控制或連線此服務。

  • android:process:表示該服務是否執行在另外一個程序,如果設定了此項,那麼將會在包名後面加上這段字串表示另一程序的名字。

  • android:enabled:表示是否能被系統例項化,為true表示可以,為false表示不可以,預設為true。

  • android:exported:表示該服務是否能夠被其他應用程式所控制或連線,其預設值是由 Service 中有無 intent-filter 決定的,如果有 intent-filter,預設值為 true,否則為 false。為 false 的情況下,即使有 intent-filter 匹配,也無法開啟。

3、啟動和繫結

上面說過 Service 分為兩種方式,啟動和繫結,這兩種都可以實現服務的啟動,這也使得 Service 的啟動到銷燬出現兩種路徑:

先來介紹下最重要的幾種回撥方法:

  • onCreate()

    首次建立服務時,系統將呼叫此方法來執行一次性設定程式(在呼叫 onStartCommand() 或 onBind() 之前)。如果服務已在執行,則不會呼叫此方法。

  • onStartCommand()

    當另一個元件(如 Activity)通過呼叫 startService() 請求啟動服務時,系統將呼叫此方法。一旦執行此方法,服務即會啟動並可在後臺無限期執行。 如果您實現此方法,則在服務工作完成後,需要由您通過呼叫 stopSelf() 或 stopService() 來停止服務,服務使用 stopSelf() 自行停止執行,其他元件通過呼叫 stopService() 停止。(如果您只想提供繫結,則無需實現此方法。)

  • onBind()

    當另一個元件想通過呼叫 bindService() 與服務繫結(例如執行 RPC)時,系統將呼叫此方法。在此方法的實現中,您必須通過返回 IBinder 提供一個介面,供客戶端用來與服務進行通訊。請務必實現此方法,但如果您並不希望允許繫結,則應返回 null。

  • onDestroy()

    當服務不再使用且將被銷燬時,系統將呼叫此方法。服務應該實現此方法來清理所有資源,如執行緒、註冊的偵聽器、接收器等。 這是服務接收的最後一個呼叫。

onStartCommand() 的返回值這裡特別說明,它有三種可選值,如下:

  • START_STICKY

    當 Service 因記憶體不足而被系統 kill 後,一段時間後記憶體再次空閒時,系統將會嘗試重新建立此 Service,一旦建立成功後將回調 onStartCommand 方法,但其中的 Intent 將是 null,除非有掛起的 Intent,如 pendingintent,這個狀態下比較適用於不執行命令、但無限期執行並等待作業的媒體播放器或類似服務。

  • START_NOT_STICKY

    當 Service 因記憶體不足而被系統 kill 後,即使系統記憶體再次空閒時,系統也不會嘗試重新建立此 Service。除非程式中再次呼叫 startService() 啟動此 Service,這是最安全的選項,可以避免在不必要時以及應用能夠輕鬆重啟所有未完成的作業時執行服務。

  • START_REDELIVER_INTENT

    當 Service 因記憶體不足而被系統 kill 後,則會重建服務,並通過傳遞給服務的最後一個 Intent 呼叫 onStartCommand(),任何掛起 Intent 均依次傳遞。與 START_STICKY 不同的是,其中的傳遞的 Intent 將是非空,是最後一次呼叫 startService() 中的 intent。這個值適用於主動執行應該立即恢復的作業(例如下載檔案)的服務。

4、繫結服務

繫結服務是客戶端-伺服器介面中的伺服器。繫結服務可讓元件(例如 Activity)繫結到服務、傳送請求、接收響應,甚至執行程序間通訊 (IPC)。 繫結服務通常只在為其他應用元件服務時處於活動狀態,不會無限期在後臺執行。

前面介紹了用 startService() 來啟動 Service,現在來介紹下用 bindService() 該怎麼做。

因為要繫結服務,就得為元件提供一個介面 Binder,所以我們首先要在 Service 中寫一個 Binder 類,然後在 onBind() 中返回 Binder 物件:

public class MyService extends Service {

    private MyBinder mBinder = new MyBinder();

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

    class MyBinder extends Binder {
        public void download() {
            Log.d("TAG", "download");
        }
    }
}

我們在 Binder 類裡寫自己的邏輯,必須實現 onBind() 回撥方法以返回 IBinder,用於定義與服務通訊的介面。

public class MainActivity extends AppCompatActivity {

    private MyService.MyBinder mBinder;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinder = (MyService.MyBinder) service;
            mBinder.download();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent, connection, BIND_AUTO_CREATE);
    }
}

應用元件(客戶端)可通過呼叫 bindService() 繫結到服務。Android 系統隨後呼叫服務的 onBind() 方法,返回用於與服務互動的 IBinder。

繫結是非同步的。bindService() 會立即返回,“不會”使 IBinder 返回客戶端。要接收 IBinder,客戶端必須建立一個 ServiceConnection 例項,並將其傳遞給 bindService()。

ServiceConnection 包括兩個回撥方法:

  • onServiceConnected()
    系統會呼叫該方法以傳遞服務的 onBind() 方法返回的 IBinder。

  • onServiceDisconnected()
    Android 系統會在與服務的連線意外中斷時(例如當服務崩潰或被終止時)呼叫該方法。當客戶端取消繫結時,系統“不會”呼叫該方法。

當系統呼叫我們的 onServiceConnected() 回撥方法時,就可以使用介面定義的方法開始呼叫服務。

bindService() 方法第三個引數是一個指示繫結選項的標誌。它通常應該是 BIND_AUTO_CREATE,以便建立尚未啟用的服務,在Activity和Service建立關聯後自動建立Service,這會使得MyService中的onCreate()方法得到執行,但onStartCommand()方法不會執行。其他可能的值為 BIND_DEBUG_UNBIND 和 BIND_NOT_FOREGROUND,或 0(表示無)

要斷開與服務的連線,使用 unbindService() 方法:

unbindService(connection);

如果既使用了 startService() 又使用了 bindService(),單獨使用 stopService() 或者 unbindService() 都不能銷燬 Service,不會觸發 onDestroy()。因為一個Service必須要在既沒有和任何Activity關聯又處理停止狀態的時候才會被銷燬,所以這兩個方法必須都被呼叫,順序不定。

如果你只是想要啟動一個後臺服務長期進行某項任務,那麼使用 startService() 便可以了。如果你還想要與正在執行的 Service 取得聯絡,那麼有兩種方法:一種是使用 broadcast,另一種是使用 bindService()。前者的缺點是如果交流較為頻繁,容易造成效能上的問題,而後者則沒有這些問題。

注意 Service 也是執行在主執行緒裡的,我們要做耗時操作就要在 Service 中建立子執行緒。

還有個遠端 Service,這個可以用到 AIDL 實現,可以看看我的部落格Android–AIDL基礎介紹

結束語:本文僅用來學習記錄,參考查閱。