android O 對(duì)后臺(tái)服務(wù)的限制

現(xiàn)象


android o版本(8.0)及以上版本,當(dāng)應(yīng)用處于后臺(tái)時(shí)執(zhí)行startService時(shí),會(huì)拋出如下異常:

image

Caused by: java.lang.IllegalStateException: Not allowed to start service ... app is in background uid UidRecord ...

初步理解為由于app處于后臺(tái)時(shí)startServic不被允許

原因分析


從android O版本開始,google為了控制資源使用增加了兩項(xiàng)后臺(tái)限制:

  • 后臺(tái)服務(wù)

  • 廣播

其中對(duì)于后臺(tái)服務(wù)的限制,指的是如果應(yīng)用處于后臺(tái),則不允許直接使用startService。

那什么是后臺(tái)應(yīng)用?后臺(tái)的對(duì)立面是前臺(tái),google對(duì)于前臺(tái)的定義如下:

  • 具有可見的activity(不管該activity已啟動(dòng)還是已暫停)

  • 具有前臺(tái)服務(wù)

  • 有關(guān)聯(lián)的前臺(tái)應(yīng)用(如壁紙,通知偵聽器等)

具體可參考官方鏈接:

https://developer.android.com/about/versions/oreo/background

解決方案


既然我們使用了后臺(tái)服務(wù),必然是一些無(wú)需用戶感知的場(chǎng)景,故考慮替換為前臺(tái)服務(wù)的可能性不大;

從google對(duì)前臺(tái)應(yīng)用的解釋入手,可以通過(guò)制造前臺(tái)場(chǎng)景的方式使自己的應(yīng)用處于前臺(tái);

官方還提及了JobScheduler替代后臺(tái)服務(wù)的方案,可以根據(jù)的業(yè)務(wù)場(chǎng)景選擇是否使用;

startService的方式也不需要完全放棄。為了適配8.0手機(jī),需要先判斷應(yīng)用是否在前臺(tái)再?zèng)Q定是否使用startService;

判斷應(yīng)用是否在前臺(tái)的方式很多,主要原理有兩種:

  • 通過(guò)AM判斷應(yīng)用前臺(tái)activity個(gè)數(shù)

  • 通過(guò)actvitiy生命周期回調(diào)計(jì)數(shù)(Activity回調(diào)、ActivityLifecycleCallbacks)判斷是否有前臺(tái)界面

實(shí)現(xiàn)的方式很多,自行g(shù)oogle

設(shè)置targetSdk < 26也可以實(shí)現(xiàn)該新特性規(guī)避。

源碼分析


startService大致流程如下:

ContextImpl#startServce ->

ContextImpl#startServceCommon ->

AMS#startService ->

ActiveServices#startServiceLocked ->

AMS#checkAllowBackgroundLocked

8.0.0

ContextImpl.java#startServiceCommon

image

紅框中為日志信息的來(lái)源

ActivityServices.java#startServiceLocked

image

另一處Log信息的來(lái)源:app is in background

r.startRequested第一次初始化,默認(rèn)為false;fgRequired為傳入的值false,故可以進(jìn)入后續(xù)邏輯;

通過(guò)AMS#getAppStartModeLocked獲取的allowed為決定值,意思是是否允許后臺(tái)運(yùn)行,返回值有多種類型(從代碼提交中可以看出原先是boolean,現(xiàn)在是int)。

google 在一年前(2016~2017)針對(duì)后臺(tái)應(yīng)用的判斷以及后臺(tái)執(zhí)行限制做了大量的提交,從記錄中可以看到:

image
image

之前提到通過(guò)設(shè)置targetSdk<26的方式就可以,但是這僅僅是從8.0特性的代碼層面分析得知,這種方式并不能阻止7.x系統(tǒng)中對(duì)后臺(tái)服務(wù)的限制。

ActiveServices.java # startServiceLocked

image

7.1.1 AMS 是否允許后臺(tái)服務(wù)啟動(dòng)所關(guān)心的主要還是 當(dāng)前進(jìn)程的優(yōu)先級(jí) 和 是否有后臺(tái)運(yùn)行權(quán)限

image

8.0 AMS 中間的流程較7.x稍微復(fù)雜一些,大致流程如下:

AMS # getAppStartModeLocked ->

AMS # appServicesRestrictedInBackgroundLocked ->

AMS # appRestrictedInBackgroundLocked

image

這里相當(dāng)于是后臺(tái)服務(wù)特權(quán)的檢查,只要滿足三者之一:

  • 有persistent權(quán)限

  • 后臺(tái)運(yùn)行白名單

  • 電源優(yōu)化白名單

就直接可以使用后臺(tái)服務(wù),前兩個(gè)均為系統(tǒng)應(yīng)用才能設(shè)置;

均不滿足則繼續(xù)走后面的判斷流程,重點(diǎn)來(lái)了,8.0中優(yōu)先判斷targetSdk是否在O版本及以上,是則直接返回不允許,否才會(huì)判斷是否有后臺(tái)運(yùn)行權(quán)限!

image

所以,如果需要有后臺(tái)運(yùn)行的邏輯,8.0以下版本優(yōu)先開啟后臺(tái)運(yùn)行權(quán)限,8.0及以上版本優(yōu)先開啟電源優(yōu)化白名單。

隨著Android版本的更新,早先年使用的?;詈诳萍贾饾u都失效了,現(xiàn)在需要著重在產(chǎn)品層面設(shè)計(jì),引導(dǎo)用戶開啟相關(guān)權(quán)限才是迫在眉睫的需求。

總之,Google對(duì)后臺(tái)的限制越來(lái)越嚴(yán)格,不僅限制了各種拉活行為,也限制了搶占后臺(tái)資源的行為,各大app需要提前做好高版本適配。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容