android o版本(8.0)及以上版本,當應用處於後臺時執行startService時,會拋出以下異常:java
Caused by: java.lang.IllegalStateException: Not allowed to start service ... app is in background uid UidRecord ...android
初步理解爲因爲app處於後臺時startServic不被容許app
從android O版本開始,google爲了控制資源使用增長了兩項後臺限制:源碼分析
後臺服務優化
廣播ui
其中對於後臺服務的限制,指的是若是應用處於後臺,則不容許直接使用startService。google
那什麼是後臺應用?後臺的對立面是前臺,google對於前臺的定義以下:設計
具備可見的activity(無論該activity已啓動仍是已暫停)3d
具備前臺服務日誌
有關聯的前臺應用(如壁紙,通知偵聽器等)
具體可參考官方連接:
developer.android.com/about/versi…
既然咱們使用了後臺服務,必然是一些無需用戶感知的場景,故考慮替換爲前臺服務的可能性不大;
從google對前臺應用的解釋入手,能夠經過製造前臺場景的方式使本身的應用處於前臺;
官方還說起了JobScheduler替代後臺服務的方案,能夠根據的業務場景選擇是否使用;
startService的方式也不須要徹底放棄。爲了適配8.0手機,須要先判斷應用是否在前臺再決定是否使用startService;
判斷應用是否在前臺的方式不少,主要原理有兩種:
經過AM判斷應用前臺activity個數
經過actvitiy生命週期回調計數(Activity回調、ActivityLifecycleCallbacks)判斷是否有前臺界面
實現的方式不少,自行google
設置targetSdk < 26也能夠實現該新特性規避。
startService大體流程以下:
ContextImpl#startServce ->
ContextImpl#startServceCommon ->
AMS#startService ->
ActiveServices#startServiceLocked ->
AMS#checkAllowBackgroundLocked
8.0.0
ContextImpl.java#startServiceCommon
紅框中爲日誌信息的來源
ActivityServices.java#startServiceLocked
另外一處Log信息的來源:app is in background
r.startRequested第一次初始化,默認爲false;fgRequired爲傳入的值false,故能夠進入後續邏輯;
經過AMS#getAppStartModeLocked獲取的allowed爲決定值,意思是是否容許後臺運行,返回值有多種類型(從代碼提交中能夠看出原先是boolean,如今是int)。
google 在一年前(2016~2017)針對後臺應用的判斷以及後臺執行限制作了大量的提交,從記錄中能夠看到:
以前提到經過設置targetSdk<26的方式就能夠,可是這僅僅是從8.0特性的代碼層面分析得知,這種方式並不能阻止7.x系統中對後臺服務的限制。
ActiveServices.java # startServiceLocked
7.1.1 AMS 是否容許後臺服務啓動所關心的主要仍是 當前進程的優先級 和 是否有後臺運行權限
8.0 AMS 中間的流程較7.x稍微複雜一些,大體流程以下:
AMS # getAppStartModeLocked ->
AMS # appServicesRestrictedInBackgroundLocked ->
AMS # appRestrictedInBackgroundLocked
這裏至關因而後臺服務特權的檢查,只要知足三者之一:
有persistent權限
後臺運行白名單
電源優化白名單
就直接可使用後臺服務,前兩個均爲系統應用才能設置;
均不知足則繼續走後面的判斷流程,重點來了,8.0中優先判斷targetSdk是否在O版本及以上,是則直接返回不容許,否纔會判斷是否有後臺運行權限!
因此,若是須要有後臺運行的邏輯,8.0如下版本優先開啓後臺運行權限,8.0及以上版本優先開啓電源優化白名單。
隨着Android版本的更新,早先年使用的保活黑科技逐漸都失效了,如今須要着重在產品層面設計,引導用戶開啓相關權限纔是迫在眉睫的需求。
總之,Google對後臺的限制愈來愈嚴格,不只限制了各類拉活行爲,也限制了搶佔後臺資源的行爲,各大app須要提早作好高版本適配。