學習筆記|AS入門(十) 組件篇之Service

Service(服務)是Android中實現程序後臺運行的解決方案,它很是適合用於去執行那些不須要和用戶交互並且還要求長期運行的任務。Service主要負責與UI無關的工做,好比耗時操做。本篇學習Service相關知識點有:

  • Service概要
    • 開啓子線程
    • 異步消息處理機制
  • Service生命週期
  • Service的基本用法
    • 普通Service
    • 前臺Service
    • 系統Service
      • 例子:後臺定時任務
    • IntentService
  • Service與Activity的通訊

1.Service概要python

Service的運行不依賴於任何用戶界面,所以即使程序被切換到後臺或者用戶打開了另外一個應用程序,Service仍可以保持正常運行。但當某個應用程序進程被殺掉時,全部依賴於該進程的服務也會中止運行。bash

實際上Service默認並不會運行在子線程中,也不運行在一個獨立的進程中,它一樣執行在主線程中(UI線程)。換句話說,不要在Service裏執行耗時操做,除非你手動打開一個子線程,不然有可能出現主線程被阻塞(ANR)的狀況。首先來學習如何打開一個子線程。異步

(1)開啓子線程ide

經常使用方法是,用Thread類的匿名類的形式而且實現Runnable接口,再調用它的start() 方法,就使得被重寫的run() 方法中的耗時操做運行在子線程當中了。代碼以下:函數

new Thread(new Runnable() {
 @Override
          public void run() {
          //耗時操做的邏輯
          }
}).start();
複製代碼

(2)異步消息處理機制組件化

還要注意一點:Android不容許在子線程中進行UI操做。但有時候,在子線程裏執行一些耗時任務以後須要根據任務的執行結果來更新相應的UI控件,在這裏Android提供了一套異步消息處理機制,它能夠很好地解決在子線程中更新UI的問題。主要用到兩個類:Handler(處理者,主要用於發送和處理消息)和Message(信息,可攜帶少許信息用於在不一樣線程之間交換)。下圖展現瞭如何用它們實現從子線程到主線程的轉換:佈局

能夠看到,只要在須要轉換到主線程進行UI操做的子線程中實例化一個Message對象並攜帶相關數據,再由Handle的sendMessage() 將它發送出去,以後這個數據就會被在主線程中實例化的Handle對象的重寫方法handleMessage() 收到並處理。如今在子線程中更新UI就很容易了。學習

如今來個具體的例子感覺一下,新建佈局,這裏就放一個文本和按鈕:優化

在主活動中按鈕的點擊事件裏開啓一個子線程,但又但願點擊按鈕改變文本內容,此時就用異步消息處理機制,代碼以下:this

效果如圖:

2.Service生命週期

官方文檔提供的Sevice生命週期圖以下:

先來看這幾種回調方法含義:

onCreate():服務第一次被建立時調用

onStartComand():服務啓動時調用

onBind():服務被綁定時調用

onUnBind():服務被解綁時調用

onDestroy():服務中止時調用

從上圖可看到有兩種方法能夠啓動Service,下面分別介紹: 第一種:其餘組件調用Context的startService() 方法能夠啓動一個Service,並回調服務中的onStartCommand()。若是該服務以前還沒建立,那麼回調的順序是onCreate()->onStartCommand()。服務啓動了以後會一直保持運行狀態,直到stopService()stopSelf() 方法被調用,服務中止並回調onDestroy()。另外,不管調用多少次startService()方法,只需調用一次stopService()或stopSelf()方法,服務就會中止了。

第二種:其它組件調用Context的bindService() 能夠綁定一個Service,並回調服務中的onBind()方法。相似地,若是該服務以前還沒建立,那麼回調的順序是onCreate()->onBind()。以後,調用方能夠獲取到onBind()方法裏返回的IBinder對象的實例,從而實現和服務進行通訊。只要調用方和服務之間的鏈接沒有斷開,服務就會一直保持運行狀態,直到調用了unbindService() 方法服務會中止,回調順序onUnBind()->onDestroy()。

注意,這兩種啓動方法並不衝突,當使用startService()啓動Service以後,還可再使用bindService()綁定,只不過須要同時調用 stopService()和 unbindService()方法才能讓服務銷燬掉。

3.Service的基本用法

介紹完Service生命週期和啓動方法以後,下面來具體學習一下如何在Activity中啓動一個Service。

(1)普通Service

第一步:新建類並繼承Service且必須重寫onBind()方法,有選擇的重寫onCreate()、onStartCommand()及onDestroy()方法。 第二步:在配置文件中進行註冊。學到如今會發現,四大組件除了廣播接收器可用動態註冊,定義好組件以後都要完成在配置文件註冊的這一步。 第三步:在活動中利用Intent可實現Service的啓動,代碼以下:

Intent intent = new Intent(this, MyService.class);// MyService是剛剛定義好的Service
 startService(intent);
複製代碼

對應的,中止Service方法:

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

來實戰一下!定義一個MyService,重寫如下四種方法並都打印一行日誌:

在配置文件對MyService進行註冊:

準備主活動佈局,就放兩個按鈕用來開啓和中止Service,而後設置相應的點擊事件:

如今運行程序,順便檢驗一下以前學過的Service在啓動和中止時調用的方法是否是對的,結果如圖:

(2)前臺Service

前臺服務和普通服務最大的區別是,前者會一直有一個正在運行的圖標在系統的狀態欄顯示,下拉狀態欄後能夠看到更加詳細的信息,很是相似於通知的效果。使用前臺服務或者爲了防止服務被回收掉,好比聽歌,或者因爲特殊的需求,好比實時天氣情況。

想要實現一個前臺服務很是簡單,它和以前學過的發送一個通知很是相似,只不過在構建好一個Notification以後,不須要NotificationManager將通知顯示出來,而是調用了startForeground() 方法。

修改MyService的onCreate()方法:

如今從新運行程序,而後點擊START SERVICE的按鈕,一個前臺服務就出現了:

(3)系統Service

除了自定義一個Service,固然還有現有的系統服務,好比以前接觸過的NotificationManage。經過getSyetemService() 方法並傳入一個Name就能夠獲得相應的服務對象了,經常使用的系統服務以下表:

如今再學習一個系統服務AlarmManager,來實現一個後臺定時任務。很是簡單,調用AlarmManager的set() 方法就能夠設置一個定時任務,並提供三個參數(工做類型,定時任務觸發的時間,PendingIntent對象)。下面一一解釋以上三個參數:

1)工做類型:有四個值可選,見下圖:

2)定時任務觸發的時間:以毫秒爲單位,傳入值和第一個參數對應關係是:

3)PendingIntent對象:通常會調用它的getBroadcast() 方法來獲取一個可以執行廣播的PendingIntent。這樣當定時任務被觸發的時候,廣播接收器的onReceive()方法就能夠獲得執行。

接着實戰,修改MyService,將前臺服務代碼都刪掉,重寫onStartCommand()方法,這裏先是獲取到了AlarmManager的實例,而後定義任務的觸發時間爲10秒後,再使用PendingIntent指定處理定時任務的廣播接收器爲MyReceiver,最後調用 set()方法完成設定,代碼如圖:

而後定義一個廣播接收器爲MyReceiver,這裏利用Intent對象去啓動MyService這個服務。這樣作的目的是,一旦啓動MyService,就會在onStartCommand()方法裏設定一個定時任務,10秒後MyReceiver的onReceive()方法將獲得執行,緊接着又啓動MyService,反覆循環。從而一個能長期在後臺進行定時任務的服務就完成了。

MyReceiver也在配置文件中註冊好以後,從新運行,點擊START SERVICE的按鈕,觀察日誌的狀況:

另外,從Android 4.4版本開始,因爲系統在耗電性方面進行了優化使得Alarm任務的觸發時間會變得不許確。若是必定要求Alarm任務的執行時間精確,把AlarmManager的setExact() 方法替代 set()方法就能夠了。

(4)IntentService

爲了能夠簡單地建立一個異步的、會自動中止的服務,Android 專門提供了一個IntentService類。它的使用和普通Service很是像,下面來學習一下:

第一步:新建類並繼承IntentService,在這裏須要提供一個無參的構造函數且必須在其內部調用父類的有參構造函數,而後具體實現 onHandleIntent() 方法,在裏能夠去處理一些耗時操做而不用擔憂 ANR的問題,由於這個方法已是在子線程中運行的了。 第二步:在配置文件中進行註冊。 第三步:在活動中利用Intent實現IntentService的啓動,和Service用的方法是徹底同樣的。

再來實戰,定義一個MyIntentService,準備好無參構造函數,並重寫onHandleIntent()方法,這裏打印了一行日誌,爲了證明這個方法確實已經在子線程,又打印了當前線程的id。另外,根據IntentService的特性,這個服務在運行結束後應該是會自動中止的,因此重寫onDestroy()方法,在這裏也打印了一行日誌,以證明服務是否是中止掉了。

在配置文件對MyIntentService進行註冊:

如今在主活動佈局再準備一個按鈕用來開啓這個IntentService,其點擊事件代碼如圖,在這裏打印了一下主線程的 id,稍後用於和IntentService進行比對:

運行程序,打印的日誌結果,證明了IntentService異步和自動中止:

4.Service與Activity的通訊

最後來學習如何讓Service與Activity進行通訊。這就須要藉助服務的onBind() 方法了。好比但願在MyService裏提供一個下載功能,而後在活動中能夠決定什麼時候開始下載,以及隨時查看下載進度。一塊兒學習一下:

第一步:在MyService裏自定義一個類MyBinder並繼承Binder,在它的內部提供了開始下載以及查看下載進度的方法,爲了模擬一下,這裏就分別打印了一行日誌。

第二步:在MyService的onBind() 方法裏返回剛剛定義好的MyBinder類。

第三步:在活動中實例化一個ServiceConnection類,並重寫它的onServiceConnection()onServiceDisconnection() 方法,這兩個方法分別會在活動與服務成功綁定以及解除綁定的時候調用。在 onServiceConnected()方法中,又經過向下轉型獲得了MyBinder 的實例,有了它活動和服務之間的關係就變得很是緊密了。如今能夠在活動中根據具體的場景來調用MyBinder中的任何非private方法了。這裏簡單調用MyBinder中的兩個模擬方法。

第四步:在活動佈局裏再準備兩個按鈕用於綁定和解綁服務,在它們的點擊事件裏利用Intent對象實現活動和服務的綁定和解綁。方法是:bindService() 實現綁定,它接收三個參數(Intent對象,ServiceConnection對象,標誌位),這裏傳入BIND_AUTO_CREATE 表示在活動和服務進行綁定後自動建立服務。unbindService() 實現解綁,傳入ServiceConnection對象就能夠了。

運行程序,點擊一下Bind Service 按鈕:

能夠看到MyService的兩個模擬方法都獲得了執行,說明確實已經在活動裏成功調用了服務裏提供的方法了。

如今學完四大組件如今能夠總結一下了,Activity提供UI界面管理,Service提供與UI無關的服務,ContentProvider用於存儲和數據共享,Broadcast解決組件和應用的通訊問題,而Intent是將四大組件聯結在一塊兒的粘結劑,但彼此之間幾乎沒有耦合。這種組件化的設計思想使得Android變得很是靈活。

到這裏安卓開發入門整篇已經接近尾聲了,小菜鳥終於打開安卓世界的大門,但安卓的精彩遠不止此,深刻探索之旅也纔剛剛開始。縱觀全篇,整個入門過程當中學習到的基礎知識還只停留在會用的階段,不少要點只是淺談沒有深究,對於基本原理幾乎隻字未提,因此在入門以後,小菜鳥還會繼續深刻學習,不定時分享學習筆記,最後感謝和我一塊兒進步的大家!

相關文章
相關標籤/搜索