Android四大組件之Service

前言

Hi,你們好,上一期咱們講了如何使用BroadcastReceiver,這一期咱們講解Android四大組件之Service相關知識。天天一篇技術乾貨,天天咱們一塊兒進步。java

耐心專一不只僅是美德,更是一筆財富。android

1.簡介與定義

簡介

Service是一個能夠在後臺執行長時間運行操做而不提供用戶界面的應用組件。Service可由其餘應用組件啓動,並且即便用戶切換到其餘應用,Service仍將在後臺繼續運行。 此外,組件能夠綁定到Service,以與之進行交互,甚至是執行進程間通訊 (IPC)。 例如,Service能夠處理網絡事務、播放音樂,執行文件 I/O 或與內容提供程序交互,而全部這一切都可在後臺進行。網絡

定義

Service是一個專門在後臺處理長時間任務的Android組件。ide

1.Service不是一個單獨的進程;函數

2.Service也不是一個單獨的線程;this

3.Service是一個單獨的Android組件,Service運行在主線程上,若是想在Service中處理很佔時間的操做時,必須在Service中開線程,以下降Activity沒有響應的風險;spa

4.Service不提供用戶界面;操作系統

它有兩種啓動方式:startServicebindService線程

2.用途

Service有三個常見用途。代理

1.功能調度:Service接收指定的廣播信息,從而進一步分析和處理事件,最後修改數據、更新界面或者進行其餘相關的操做,調度整個應用使其保持正確的狀態。

2.功能提供:Service並不會接收任何的廣播,只接收指定的廣播提供狀態數據,這時須要綁定Service,綁定Service時要管理好Service,通常在Activity的onStop函數裏進行解綁unBindService操做。

3.遠程調用:定義AIDL服務,跨進程調用Service,先定義一個遠程調用接口,而後爲該接口提供一個IBinder實現類,客戶端獲取了遠程的Service的IBinder對象的代理後,經過該IBinder對象去回調遠程Service的屬性或方法。

3.應用場景

若是某個程序組件須要在運行時向用戶呈現界面,或者程序須要與用戶交互,就須要用Activity,不然就應該考慮使用Service。

4.Service與Activity對比

類似點:

1.都是單獨的Android組件;

2.都擁有獨立的生命週期;

3.都是Context的派生類,因此能夠調用Context類定義的如getResources()getContentResolver()等方法;

4.都擁有本身生命週期回調方法;

不一樣點:

1.Activity運行於前臺有圖形用戶界面,負責與用戶交互;Service一般位於後臺運行,不須要與用戶交互,也沒有圖形用戶界面。

5.Service的生命週期

隨着應用程序啓動Service方式不一樣,Service的生命週期也略有差別,以下圖:

若是應用程序經過startService()方法來啓動Service,Service的生命週期如上圖左半部分所示。

經過調用startService() 方法啓動Service: 當其餘組件調用startService()方法時,Service被建立,而且無限期運行,其自身必須調用stopSelf()方法或者其餘組件調用stopService() 方法來中止Service,當Service中止時,系統將其銷燬。

若是應用程序經過bindService()方法來啓動Service,Service的生命週期如上圖右半部分所示。

經過bindService() 方法啓動Service: 當其餘組件調用bindService()方法時,Service被建立。接着客戶端經過IBinder接口與Service通訊。客戶端經過unbindService() 方法關閉鏈接。多個客戶端能綁定到同一個Service,而且當他們都解除綁定時,系統將銷燬Service(Service不須要被中止)

特別說明:當Activity調用bindService()綁定一個已經過startService()啓動的Service時,系統只是把Service內部的IBinder對象傳給Activity,並不會把該Service生命週期徹底綁定到該Activity,於是當Activity調用unBindService()方法取消與該Service的綁定時,也只是切斷該Activity與Service之間的關聯,並不能中止該Service組件。要中止該Service組件,還需調用stopService()方法。

Service的生命週期裏,經常使用的有:

4個手動調用的方法

手動調用方法 做用
startService() 啓動服務
stopService() 關閉服務
bindService() 綁定服務
unbindService() 解綁服務

5個自動調用的方法

內部自動調用的方法 做用
onCreate() 建立服務
onStartCommand() 開始服務
onDestroy() 銷燬服務
onBind() 綁定服務
onUnbind() 解綁服務

6.Service的使用

當咱們開始使用Service的時候固然是啓動一個Service了,啓動Service的方法和啓動Activity很相似,都須要藉助Intent來實現,下面咱們就經過一個具體的例子來看一下。

public class MyService extends Service {  
  
    public static final String TAG = "MyService";  
  
    @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 IBinder onBind(Intent intent) {  
        return null;  
    }
      
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        Log.d(TAG, "onDestroy() executed");  
    }  
  
}  
複製代碼

要建立一個這樣的Service,你須要讓該類繼承Service類,而後重寫如下方法:

  • onCreate() 1.若是service沒被建立過,調用startService()後會執行onCreate()和onStartCommand()方法; 2.若是service已處於運行中,調用startService()不會執行onCreate()方法,只執行onStartCommand()方法。 也就是說,onCreate()只會在第一次建立service時候調用,屢次執行startService()不會重複調用onCreate(),此方法適合完成一些初始化工做。
  • onStartCommand() 若是屢次執行了Context的startService()方法,那麼Service的onStartCommand()方法也會相應的屢次調用。onStartCommand()方法很重要,咱們在該方法中根據傳入的Intent參數進行實際的操做,好比會在此處建立一個線程用於下載數據或播放音樂等。
  • onBind() Service中的onBind()方法是抽象方法,Service類自己就是抽象類,因此onBind()方法是必須重寫的,即便咱們用不到。
  • onDestroy() 在銷燬的時候會執行Service的該方法。

這幾個方法都是回調方法,且在主線程中執行,由Android操做系統在合適的時機調用。

注意:每一個Service必須在manifest中 經過來聲明。

<service android:name="com.demo.service.MyService" > 
  ... 
</service>
複製代碼

如今咱們經過繼承Service的方式定義了咱們本身的MyService類,而且在manifest中聲明瞭咱們的MyService,接下來咱們應該啓動咱們本身的服務。

啓動Service

第一種方式:咱們是經過一個Intent對象,並調用startService()方法來啓動MyService

Intent startIntent = new Intent(this, MyService.class);  
startService(startIntent); 
複製代碼

注意:假如咱們是經過點擊Button執行上面的代碼,那麼第一次點擊的時候回執行其中的onCreate()onStartCommand()方法,可是當咱們第二次點擊的時候就只會執行onStartCommand()方法。

爲何會這樣呢? 這是因爲onCreate()方法只會在Service第一次被建立的時候調用,若是當前Service已經被建立過了(第一次點擊建立了MyService),無論怎樣調用startService()方法,onCreate()方法都不會再執行。

第二種方式:經過bindService啓動Service

bindService啓動服務特色: 1.bindService啓動的服務和調用者之間是典型的client-server模式。調用者是clientservice則是server端。service只有一個,但綁定到service上面的client能夠有一個或不少個。這裏所提到的client指的是組件,好比某個Activity

2.client能夠經過IBinder接口獲取Service實例,從而實如今client端直接調用Service中的方法以實現靈活交互,這在經過startService()方法啓動中是沒法實現的。

3.bindService啓動服務的生命週期與其綁定的client息息相關。當client銷燬時,client會自動與Service解除綁定(client會有ServiceConnectionLeaked異常,但程序不會崩潰)。固然,client也能夠明確調用ContextunbindService()方法與Service解除綁定。當沒有任何clientService綁定時,Service會自行銷燬。

啓動了以後,當咱們想中止服務的時候該怎麼作呢?

中止Service

第一種方式:咱們也是經過一個Intent對象,並調用stopService()方法來中止MyService

Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); 
複製代碼

第二種方式:調用unbindService(conn)方法來中止MyService

unbindService(ServiceConnection conn)
複製代碼
Service和Activity通訊

在上面咱們高高興興的啓動了Service了,可是細心的你可能發現了,貌似咱們僅僅只是啓動了而已,ActivityService並無多少"交流",下面咱們就讓ActivityService交流一下。

public class MyService extends Service {

    public static final String TAG = "MyService";

    private MyBinder mBinder = new MyBinder();

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

    class MyBinder extends Binder {

        public void startDownload() {
            Log.d("TAG", "startDownload() executed");
            // 執行具體的下載任務
        }

    }

}
複製代碼

接下來咱們在MainActivity中經過Button來綁定Service和解除綁定

public class MainActivity extends Activity implements OnClickListener {
        
    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);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        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;
        default:
            break;
        }
    }

}
複製代碼

能夠看到,這裏咱們首先建立了一個ServiceConnection的匿名類,在裏面重寫了onServiceConnected()方法和onServiceDisconnected()方法,這兩個方法分別會在ActivityService創建關聯和解除關聯的時候調用。在onServiceConnected()方法中,咱們又經過 向下轉型 獲得了MyBinder的實例,有了這個實例,ActivityService之間的關係就變得很是緊密了。如今咱們能夠在Activity中根據具體的場景來調用MyBinder中的任何public方法,即實現了Activity指揮Service幹什麼Service就去幹什麼的功能。

固然,如今ActivityService其實還沒關聯起來了呢,這個功能是在Bind Service按鈕的點擊事件裏完成的。能夠看到,這裏咱們仍然是構建出了一個Intent對象,而後調用bindService()方法將ActivityService進行綁定。bindService()方法接收三個參數,第一個參數就是剛剛構建出的Intent對象,第二個參數是前面建立出的ServiceConnection的實例,第三個參數是一個標誌位,這裏傳入BIND_AUTO_CREATE表示在ActivityService創建關聯後自動建立Service,這會使得MyService中的onCreate()方法獲得執行,但onStartCommand()方法不會執行(只有當咱們經過 startService()方法請求啓動服務時,調用此方法)。

解除Activity和Service之間的關聯,調用

unbindService(connection);  
複製代碼
關於銷燬Service說明
  • MyService的內部經過stopSelf()方法來銷燬的;

  • 一個Service必需要在既沒有和任何Activity關聯又處理中止狀態的時候纔會被銷燬;

  • 在Service的onDestroy()方法裏去清理掉那些再也不使用的資源,防止在Service被銷燬後還會有一些再也不使用的對象仍佔用着內存;

7.IntentService

IntentService是Service的子類,在介紹IntentService以前,先來了解使用Service時須要注意的兩個問題

  • Service 不會專門啓動一個線程執行耗時操做,全部的操做都是在主線程中進行的,以致於容易出現ANR,因此須要手動開啓一個子線程;
  • Service 不會自動中止,須要調用stopSelf()方法 或者 是stopService() 方法中止;

使用IntentService不會出現這兩個問題,由於IntentService在開啓Service時,會自動開啓一個新的線程來執行它,另外,當Service運行結束後,會自動中止。

8.如何保證服務不會被殺死

第一種方式,返回 START_STICKYSTART_REDELIVER_INTENT

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

/** * 返回 START_STICKY 或 START_REDELIVER_INTENT * @param intent * @param flags * @param startId * @return */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //return super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

複製代碼

第二種方式,提升service的優先權

<service  
    android:name="com.demo.UploadService"  
    android:enabled="true" >  
    <intent-filter android:priority="1000" >  
        <action android:name="com.demo.MyService" />  
    </intent-filter>  
</service>

複製代碼

結語

Service做爲Android的四大組件之一,而且項目開發過程當中一些場景下常常被使用到,小夥伴們趕忙上手實操,把它靈活的運用到項目中,結合上兩期的Activity和BroadcastReceiver實現有趣的交互吧。
PS:若是還有未看懂的小夥伴,歡迎加入咱們的QQ技術交流羣:892271582,裏面有各類大神回答小夥伴們遇到的問題哦~

相關文章
相關標籤/搜索