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方法。

在该方法中又调用了它的startServiceCommon()方法,该方法中会抛出上面打印的异常,在当包名是“?”的时候,才会进入该分支,而包信息又是通过ActivityManagerService的startService()方法获取的,因此继续查看ActivityManagerService的startService()方法。


在ActivityManagerService中的startService方法中,继续调用了ActiveServices的startServiceLocked方法,如下:


在该方法中会有如下一段代码,首先会去判断它的启动类型是不是APP_START_MODE_NORMAL,如果不是的话,会进入当前分支,在最后会return一个新的ComponentName,指定了它的pkg为"?",并且指定了当前应用时后台应用,此时和ActivityManagerService的startService()方法中能够对应上。

下面继续看看allowed是怎么指定的。


allowed是通过调用ActivityManagerService的getAppStartModeLocked获取的,由于程序已经抛出异常,因此我们从上面代码可以知道,allowed类型既不是APP_START_MODE_NORMAL,也不是APP_START_MODE_DELAYED,并且alwaysRestrict参数传值为false。然后继续查看getAppStartModeLocked函数可以看到以下代码,可以看出来最终allowed是通过startMode赋值,并且是通过appServicesRestrictedInBackgroundLocked()函数获取的。


下面我们继续查看appServicesRestrictedInBackgroundLocked()函数。

这个函数中做了三件事情:

1.判断我们的应用是否是persistent app,具体什么是persistent app,可查看https://blog.csdn.net/u011959433/article/details/70324511

2.判断我们的应用是否加入到后台白名单中,如果是,直接返回APP_START_MODE_NORMAL类型

3.判断我们应用是否加入到电池白名单中


找到这块问题就很明了了,因为我们的应用这三种都不满足,因此会抛出上述异常。那怎么去解决呢,只要能让我们的应用startMode返回值为APP_START_MDOE_NORMAL类型,这个问题就可以解决,并且不用担心会弹出一个通知栏,让用户感知到。

但是这种方式解决只能适用系统开发时使用,普通应用无法采用这种方式解决,如果非要采用这种方式,那只能通过联系手机厂商,将自己的应用加入到他们的白名单中。

你可能感兴趣的:(Android 8.0 后台应用无法启动服务源码分析)