Android 面試(四):Android Service 你真的能應答自如了嗎?

nanchen2251發表於2019-03-01

本次我們沒有采取分享公眾號推文的方法,我想直接這樣看看效果。

本文首發於微信公眾號「nanchen」,你可以直接在公眾號搜尋「nanchen」或者掃描最下面的二維碼關注我。做不完的開源,寫不完的矯情,南塵一直與你同行。

這是 面試系列 的第三期。本期我們將來探討一下 Android 四大元件的重要組成部分:Service。

往期內容傳遞:
Android 面試(一):說說 Android 的四種啟動模式
Android 面試(二):如何理解 Activity 的生命週期
Android 面試(三):用廣播 BroadcastReceiver 更新 UI 介面真的好嗎?

Service 有多重要?

之前在「蘭柳學」的文章中看到這樣一個場景,挺有意思的,先給大家分享一下,讓我們一起來看看對 Service 的無知到底會有多麻煩。

場景:如果一個應用要從網路上下載一個檔案,並在 Activity 上展示進度條,這個 Activity 要求是可以轉屏的。那麼在轉屏時 Actvitiy 會重啟,如何保證下載的進度條能正確展示進度呢?

不會 Service 的人,一般會想出來這樣的方案。

    1. 在轉屏前將進度快取,轉屏後再讀出來。
    1. 使用 android:configChanges 設定,讓轉屏時 Activity 不銷燬和重建。

針對第 1 種方案,其實細想漏洞百出。首先,轉屏的過程中,我們知道 Activity 的重建算是比較耗時的,可能會有幾百毫秒甚至更久,這時候下載執行緒仍然在工作,進度肯定和儲存時的進度不一致了,如何處理這個問題呢?

第 2 個方案,大家可以自己展開思考,實際的專案中可能會需要額外做一些事情來處理 ContentView 的橫豎佈局的問題。

如果採用 Service,你有什麼好主意呢?不妨在評論區給出。

一定聽過 Service 吧,它有幾種啟動方式?

Service 是一個專門在後臺執行長時間操作的類,它並不與使用者產生 UI 互動。它提供了兩種啟動方式。

  • started
    其它元件呼叫 startService() 啟動一個 Service。一旦啟動,Service 將一直執行在後臺,即使啟動這個 Service 的元件已經被銷燬。通常一個被 start 的 Service 會在後臺執行單獨的操作,也並不需要給啟動它的元件返回結果。只有當 Service 自己呼叫 stopSelf() 或者其它元件呼叫 stopService() 才會終止。
  • bind
    其它元件可以呼叫 bindService() 來繫結一個 Service。這種方式會讓 Service 和啟動它的元件繫結在一起,當啟動它的元件銷燬的時候,Service 也會自動進行 unBind 操作。同一個 Service 可以被多個元件繫結,只有所有繫結它的元件都進行了 unBind 操作,這個 Service 才會被銷燬。

當然,Service 還可以同時在上述兩種方式下執行。這涉及到 Service 的兩個回撥方法的執行: onStartCommand()(通過 start 方式啟動一個 Service 時的回撥方法。)、onBind() (通過 bind 方式啟動一個 Service 回撥的方法)。

無論通過那種方式啟動 Service(start、bind、start & bind),任何元件(甚至其他應用的元件)都可以使用 Service。並通過 Intent 傳遞引數。當然,你也可以將 Service 在 AndroidMenifest.xml 檔案中配置成私有的,不允許其他應用訪問。

android:exported 屬性設為 false,表示不允許其他應用程式啟動本應用的元件,即便是顯式 Intent 也不行(even when using an explicit intent)。這可以防止其他應用程式啟動您的 Service 元件。

Service 的生命週期

一談到元件,我們總是喜歡去研究它的生命週期,而這時候用圖來展示肯定是最好的了。

nanchen.png
nanchen.png

這兩條路徑並不是毫不相干的。當呼叫 startService() 去 start 一個 Service 後,你仍然可以 bind 這個 Service。比如:當播放音樂的時候,需要呼叫 startService() 啟動指定的音樂,當需要獲取該音樂的播放進度的時候,又需要呼叫 bindService(),在這種情況下,除非 Service 被 unbind,此前呼叫 stopService()stopSelf() 都不能停止該 Service。

這些生命週期方法在使用的時候並不需要呼叫各自的父類方法。

兩條生命週期路徑都可以包含兩個巢狀的生命週期:

  • 完整生命週期(entire lifetime):從 onCreate() 被呼叫,到 onDestroy() 返回。和 Activity 類似,一般在 onCreate() 方法中做一些初始化的工作,在 onDestroy() 中做一些資源釋放的工作。如,若 Service 在後臺播放一個音樂,就需要在 onCreate() 方法中開啟一個執行緒啟動音樂,並在 onDestroy() 中結束執行緒。

  • 活動生命週期(activity lifetime):從 onStartCommand()onBind() 回撥開始,由相應的 startService()bindService() 呼叫。start 方式的活動生命週期結束就意味著完整證明週期的結束,而 bind 方式,當 onUnbind() 返回後,Service 的活動生命週期結束。

值得注意的是,無論是 startService() 還是 bindService() 啟動 Service,onCreate()onDestroy() 均會被回撥。

Service 的 onCreate() 可以執行耗時操作嗎?

Service 執行在主執行緒中,它並不是一個新的執行緒,也不是新的程式,所以並不能執行耗時操作。

那如果要在 Service 中執行耗時操作,怎麼做?

我想基本所有人都能想到使用 Thread,事實上我們也經常這麼做。需要在主執行緒執行耗時操作,無非就是開一個執行緒,然後一陣混沌操作。當然,你還可以使用 AysncTaskHandlerThread 來替代 Thread 建立執行緒。

當然沒有問題,那還有其它更有意思的方式嗎?

有,當然有,IntentService 就是一個不錯的選擇。

紛繁複雜的 IntentService

nanchen.png
nanchen.png

IntentService 繼承於 Service,若 Service 不需要同時處理多個請求,那麼使用 IntentService 將是最好選擇。你只需要重寫 onHandleIntent() 方法,該方法接收一個回撥的 Intent 引數,你可以在方法內進行耗時操作,因為它預設開啟了一個子執行緒,操作執行完成後也無需手動呼叫 stopSelf() 方法,onHandleIntent() 將會自動呼叫該方法。

使用 IntentService 的要點如下:

  • 預設在子執行緒中處理回傳到 onStartCommand() 方法中的 Intent;
  • 在重寫的 onHandleIntent() 方法中處理按時間排序的 Intent 佇列,所以不用擔心多執行緒(multi-threading)帶來的問題。
  • 當所有請求處理完成後,自動停止 Service,無需手動呼叫 stopSelf() 方法;
  • 預設實現了 onBind() 方法,並返回 null;
  • 預設實現了 onStartCommand() 方法,並將回傳的 Intent 以序列的形式傳送給 onHandleIntent(),您只需重寫該方法並處理 Intent 即可。

小結

當我們知道了 Service 的用途,心中有一個 Service 相關的概念時,針對實際的場景還是要做具體的分析再決定是否使用 Service。因為 Service 仍然是在主執行緒中呼叫,還是要開執行緒才能處理長時間的工作,Service 和 UI 的互動也讓這個方式變得不那麼簡便。如果你只需要在當前介面去做一些耗時操作,介面退出或改變時,工作也要停止,那麼這時直接使用 Thread(或者 AsyncTask, ThreadHandler)會更合適你。

做不完的開源,寫不完的矯情。歡迎掃描下方二維碼或者公眾號搜尋「nanchen」關注我的微信公眾號,目前多運營 Android ,儘自己所能為你提升。如果你喜歡,為我點贊分享吧~

nanchen
nanchen

相關文章