天天看點

Android 8.0 背景應用無法啟動服務源碼分析

都知道在Android8.0裝置上,背景應用無法通過startService方式啟動背景服務,如果使用這種方式啟動,之間會抛出異常:java.lang.IllegalStateException: Not allowed to start service Intent { cmp=xxx }: app is in background uid UidRecord{xxx},異常會提示你的應用是背景應用,不允許啟動服務。

在谷歌官網上有以下描述:
Android 8.0 還對特定函數做出了以下變更:
	1. 如果針對 Android 8.0 的應用嘗試在不允許其建立背景服務的情況下使用 startService() 函數,則該函數将引發一個 IllegalStateException。
	2. 新的 Context.startForegroundService() 函數将啟動一個前台服務。現在,即使應用在背景運作,系統也允許其調用Context.startForegroundService()。不過,應用必須在建立服務後的五秒内調用該服務的 startForeground() 函數。
           

對于如何解決,官網上也給了我們解決方案,調用startForegroundService()函數,而且建立服務後5s内需要調用該服務的startForeground()函數,但是這種方式會在前台建立一個通知欄,使用者可以感覺的到。

下面從源碼中嘗試去尋找一下,看有沒有其他的解決方案。

首先,我們調用了Context的startService方法,其實最終調用的是android.app.ContextImpl的startService方法。

Android 8.0 背景應用無法啟動服務源碼分析

在該方法中又調用了它的startServiceCommon()方法,該方法中會抛出上面列印的異常,在當包名是“?”的時候,才會進入該分支,而包資訊又是通過ActivityManagerService的startService()方法擷取的,是以繼續檢視ActivityManagerService的startService()方法。

Android 8.0 背景應用無法啟動服務源碼分析

在ActivityManagerService中的startService方法中,繼續調用了ActiveServices的startServiceLocked方法,如下:

Android 8.0 背景應用無法啟動服務源碼分析

在該方法中會有如下一段代碼,首先會去判斷它的啟動類型是不是APP_START_MODE_NORMAL,如果不是的話,會進入目前分支,在最後會return一個新的ComponentName,指定了它的pkg為"?",并且指定了目前應用時背景應用,此時和ActivityManagerService的startService()方法中能夠對應上。

下面繼續看看allowed是怎麼指定的。

Android 8.0 背景應用無法啟動服務源碼分析

allowed是通過調用ActivityManagerService的getAppStartModeLocked擷取的,由于程式已經抛出異常,是以我們從上面代碼可以知道,allowed類型既不是APP_START_MODE_NORMAL,也不是APP_START_MODE_DELAYED,并且alwaysRestrict參數傳值為false。然後繼續檢視getAppStartModeLocked函數可以看到以下代碼,可以看出來最終allowed是通過startMode指派,并且是通過appServicesRestrictedInBackgroundLocked()函數擷取的。

Android 8.0 背景應用無法啟動服務源碼分析

下面我們繼續檢視appServicesRestrictedInBackgroundLocked()函數。

這個函數中做了三件事情:

1.判斷我們的應用是否是persistent app,具體什麼是persistent app,可檢視https://blog.csdn.net/u011959433/article/details/70324511

2.判斷我們的應用是否加入到背景白名單中,如果是,直接傳回APP_START_MODE_NORMAL類型

3.判斷我們應用是否加入到電池白名單中

Android 8.0 背景應用無法啟動服務源碼分析

找到這塊問題就很明了了,因為我們的應用這三種都不滿足,是以會抛出上述異常。那怎麼去解決呢,隻要能讓我們的應用startMode傳回值為APP_START_MDOE_NORMAL類型,這個問題就可以解決,并且不用擔心會彈出一個通知欄,讓使用者感覺到。

但是這種方式解決隻能适用系統開發時使用,普通應用無法采用這種方式解決,如果非要采用這種方式,那隻能通過聯系手機廠商,将自己的應用加入到他們的白名單中。