这篇文章开始,准备分析一下service的源码。之前我们有分析过AMS,AMS其实还是比较复杂的,里面包含了很多的内容,之前我们分析的时候主要以Activity为主来分析的,当然AMS包含了不止Activity,我们熟悉的四大组件都有包含,所以这篇文章准备分析一下service相关的内容。相对于前面以Activity为主的AMS分析来说,service要稍微容易一些,相信如果理解了前面的AMS后,对于service的分析,起码在调用流程上不会有太多的难度。我们这里分析的版本还是android 8.0,因为从8.0开始google对service做了限制,所以我们就从这个版本来看。android虽然还是一直在发展,但是到了8.0后主要的部分我们如果能理解透,看后面的新版本也会比较轻松,所以我们还是分析8.0的版本。
大家都知道,调用service有startService和bindService,一个是直接调用service就可以了,不需要有返回的数据。另一个是可以和service之间进行通信。其实他们本质都差不多,后面的源码分析我们也可以看到他们之间有很多地方都是一样的,只不过bindService会多一些流程,这个我们后面看源码就知道了。好了,下面就开始从startService说起。
$ startService入口
我们平时开发中启动一个service,最常用的就是startService这个方法,我们看下他的入口方法:
```java
// ContextImpl.java
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
```
我们可以看到这个方法是在ContextImpl中的,之前我们分析AMS启动Activity的时候有看到,每个创建的Activity都会有创建Context,他们的实现类就是ContextImpl,所以我们是通过Context来启动service。起始不止Activity,所以有Context的组件都可以启动service,比如我们四大组件等等,后面我们在分析创建service的时候会看到service和Activity一样会创建Context。好了,我们继续看后面调用的方法startServiceCommon:
```java
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
// AMS的startService
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
if (cn != null) {
//包含"!"表示没有权限启动service
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
// 这里是不允许后台启动service
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
```
这个方法可以看到开始会通过AMS调用startService,这个套路和分析Activity时候的是一模一样,具体调过去的细节这里就不分析了,没看过AMS可以去那里看看,当然还需要知道binder和aidl的知识,之前的文章都有分析过可以去那里看看。我们这里这里看到调用后会返回一个结果,如果是错误的结果会报错。这里我们看到最后有个"?"号的报错,这里是指不允许启动service。我们知道在android 8后,如果想在后台悄悄的启动一个service做点"坏"事,是不允许的,这个我们后面分析代码的时候会看一下这个错误是怎么处理的,这里我们这里先提一下。好了,接着我们到AMS中看startService方法。
# startService第一次进入AMS
```java
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
// 继续调用
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
return res;
}
}
```
方法开始做了一些校验,后面继续调用ActiveServices的startServiceLocked方法:
```java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
final boolean callerFg;
if (caller != null) {
// 获取调用者的ProcessRecord
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + callingPid
+ ") when starting service " + service);
}
callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
} else {
callerFg = true;
}
// 获取service启动对象,ServiceLookupResult里面有ServiceRecord
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false);
if (res == null) { // 启动对象空,return
return null;
}
if (res.record == null) { // 启动对象空,报错
return new ComponentName("!", res.permission != null
? res.permission : "private to package");
}
```
这个方法比较长,我们一段段来分析。这里第一段首先我们获得调用者的IApplicationThread,这个是一个aidl,作为一个客户端进程我们AMS通信,这个在AMS也分析过了,不多说。然后调用AMS的getRecordForAppLocked方法来获取该进程在AMS中的ProcessRecord,每个启动的进程都会在启动过程中把他的进程封装成ProcessRecord对象保存在AMS,以便AMS后面使用的方便。这些都是AMS中的一些基本概念,可见AMS确实是非常重要,理解了AMS对后续我们阅读android源码起到了非常重要的作用。
这里我们看到有个callerFg变量,表示这个进程是否属于前台。在android中每个进程都有个进程调度组,优先级越高的调度组,cpu在分配资源时候给予的资源就越多,比如超时等待,或者时间片的执行时间等。目前来说,从源码的定义来看一共有四种不同的调度组:
```java
static final int SCHED_GROUP_BACKGROUND = 0;
// Activity manager's version of Process.THREAD_GROUP_DEFAULT
static final int SCHED_GROUP_DEFAULT = 1;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
static final int SCHED_GROUP_TOP_APP = 2;
// Activity manager's version of Process.THREAD_GROUP_TOP_APP
// Disambiguate between actual top app and processes bound to the top app
static final int SCHED_GROUP_TOP_APP_BOUND = 3;
```
可以看到第一个SCHED_GROUP_BACKGROUND就是属于后台调度组的,这种优先级是最低了,其余三种都属于前台调度组,每个调度组都有一些固定的场景。比如默认情况下会是SCHED_GROUP_DEFAULT这个调度组,比如一个Activity处于可见状态默认就是SCHED_GROUP_DEFAULT。又比如一个应用长时间不活跃进入睡眠状态就是一个后台调度组。关于进程的优先级是个非常复杂的问题,我们在分析AMS源码时候会进程看到update_oom_adj此类名字的方法,就是更新进程的优先级,这个问题太复杂,这里就不说了,总之这里如果调用者进程不是SCHED_GROUP_BACKGROUND调度组那么就属于前台调度组,后面的处于就会根据前台还是后台来处理。
之后会调用retrieveServiceLocked方法来看下这个intent对应的service是否存在,如果不存在就return了,如果存在还会取出他的serviceRecord,如果是空,那么也会返回错误。具体我们来看下retrieveServiceLocked这个方法的处理:
```java
private ServiceLookupResult retrieveServiceLocked(Intent service,
String resolvedType, String callingPackage, int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal) {
ServiceRecord r = null;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "retrieveServiceLocked: " + service
+ " type=" + resolvedType + " callingUid=" + callingUid);
// 获取调用者userId
userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE, "service", null);
// 获取userId下的所有Service
ServiceMap smap = getServiceMapLocked(userId);
// 获取待启动的Service的ComponentName,即包名和类名
final ComponentName comp = service.getComponent();
if (comp != null) {
// 获取Component存在,获取他的ServiceRecord
r = smap.mServicesByName.get(comp);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by component: " + r);
}
// 这里isBindExternal表示是否允许在调用者的进程中,正常startService默认会是false
// startService没有这个特性,这个只有在bindService才会有,所以startService这个永远应该是false
if (r == null && !isBindExternal) { // 没找到ServiceRecord,继续通过FilterComparison寻找
Intent.FilterComparison filter = new Intent.FilterComparison(service);
r = smap.mServicesByIntent.get(filter);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE, "Retrieved by intent: " + r);
}
// FLAG_EXTERNAL_SERVICE即为android:externalService
// 如果找到一个ServiceRecord,同时这个是需要运行在调用者进程中的,那么把找到的这个ServiceRecord置空
if (r != null && (r.serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0
&& !callingPackage.equals(r.packageName)) {
// If an external service is running within its own package, other packages
// should not bind to that instance.
r = null; // 要允许在调用者进程中,所以包名和userId要相同。这里不同包名的话,把ServiceRecord设置为null
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Whoops, can't use existing external service");
}
```
这个方法也是比较长,我们分几段来分析。这里第一段,首先获取调用者的userId,然后调用getServiceMapLocked方法获得这个用户的ServiceMap,ServiceMap类封装了这个用户下的所有的ServiceRecord,我们简单看下这个类:
```java
final class ServiceMap extends Handler {
final int mUserId;
// ComponentName为key的ServiceRecord的map
final ArrayMap<ComponentName, ServiceRecord> mServicesByName = new ArrayMap<>();
// FilterComparison为key的ServiceRecord的map,FilterComparison即可以看做Intent
final ArrayMap<Intent.FilterComparison, ServiceRecord> mServicesByIntent = new ArrayMap<>();
......................
```
我们看到这个类是一个handler,一些前台或者后台启动的service也会在这个类里处理,这个我们先不管,我们这里主要看到这里有两个map,其实都是保存ServiceRecord的,只不过一个的key是ComponentName,另一个是FilterComparison。也即,一个是通过包名类名来寻找ServiceRecord,另一个是通过Intent来寻找Service,所以这里等于给我们提供了两种寻找serviceRecord的方法。好了我们回到retrieveServiceLocked方法。
接着就是分别通过ComponentName和FilterComparison来寻找serviceRecord。之后如果找到了,会有一个新的场景。可以看到这里有一个FLAG_EXTERNAL_SERVICE的flag,这个flag是在bindService中起作用,表示的是这个service是需要运行在调用者进程中的,具体来说,就是包名需要和调用者的包名一样,否则会把找到的serviceRecord置为null。虽然我们这里是说的startService,但是既然说到这里了也就顺便说一下这个,说完startService后就会说bindService,这个方法也是两者共用的到时候这里也就不用说了。具体怎么处理FLAG_EXTERNAL_SERVICE这种类型的service呢,下面一段代码就是具体的处理过程,我们来看一下:
```java
if (r == null) { // 没找到ServiceRecord
try {
// TODO: come back and remove this assumption to triage all services
// 通过PMS来获取minifest中的service内容
ResolveInfo rInfo = mAm.getPackageManagerInternalLocked().resolveService(service,
resolvedType, ActivityManagerService.STOCK_PM_FLAGS
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
userId, callingUid);
// 获取minifest的service
ServiceInfo sInfo =
rInfo != null ? rInfo.serviceInfo : null;
if (sInfo == null) {
Slog.w(TAG_SERVICE, "Unable to start service " + service + " U=" + userId +
": not found");
return null;
}
// 根据minifest的内容new一个ComponentName,即service的包名和类名
// 这个name是被调用者的minifest中的内容
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
// 如果这个service是需要允许在调用者进程中的
// 满足这个需要有四个条件
// 1. android:externalService = true和flag需要一样,否则以flag为准
// 2. android:exported = true
// 3. android:isolatedProcess = true
// 4. flag需要有BIND_EXTERNAL_SERVICE
if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
// startService没有这个特性,所以isBindExternal肯定是false,应该连这个分支也不会进来
if (isBindExternal) {
// android:exported 需要true
if (!sInfo.exported) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not exported");
}
// android:isolatedProcess 需要true
if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not an isolatedProcess");
}
// Run the service under the calling package's application.
// 获取service的mnifeset解析出来的Application
ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
if (aInfo == null) { // application为null,报错
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
"could not resolve client package " + callingPackage);
}
// 下面是自己拼装ServiceInfo和设置ComponentName
sInfo = new ServiceInfo(sInfo); // service的minifest是被调用者的
sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo); // application也是调用者的
sInfo.applicationInfo.packageName = aInfo.packageName; // 包名是调用者的包名
sInfo.applicationInfo.uid = aInfo.uid; // uid是调用者的uid
// 包名是调用者的包名,类型是从被调用者minifest中解析出来的,应该是完整类名
name = new ComponentName(aInfo.packageName, name.getClassName());
service.setComponent(name); // service的intent被重新设置
} else {
throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
name);
}
} else if (isBindExternal) {
// isBindExternal 应该和BIND_EXTERNAL_SERVICE的flag一致,否则报错
throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
" is not an externalService");
}
// 更新ServiceInfo对象,如果是系统进程的话userId为0,smap是系统用户的map
if (userId > 0) {
if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
sInfo.name, sInfo.flags)
&& mAm.isValidSingletonCall(callingUid, sInfo.applicationInfo.uid)) {
userId = 0;
smap = getServiceMapLocked(0);
}
sInfo = new ServiceInfo(sInfo);
sInfo.applicationInfo = mAm.getAppInfoForUser(sInfo.applicationInfo, userId);
}
// 由于上面有过处理,所以再次从map中获取ServiceRecord,看看有没有
r = smap.mServicesByName.get(name);
if (DEBUG_SERVICE && r != null) Slog.v(TAG_SERVICE,
"Retrieved via pm by intent: " + r);
// 如果ServiceRecord还是null,说明确实没有了
if (r == null && createIfNeeded) {
final Intent.FilterComparison filter
= new Intent.FilterComparison(service.cloneFilter());
// 重启一个service的时候会执行这个runnable
final ServiceRestarter res = new ServiceRestarter();
final BatteryStatsImpl.Uid.Pkg.Serv ss;
final BatteryStatsImpl stats = mAm.mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
ss = stats.getServiceStatsLocked(
sInfo.applicationInfo.uid, sInfo.packageName,
sInfo.name);
}
// new一个ServiceRecord
r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
res.setService(r);
// 把这个ServiceRecord放入smap的两个map中
smap.mServicesByName.put(name, r); // ComponentName为key
smap.mServicesByIntent.put(filter, r); // FilterComparison,即可以看做intent为key
// Make sure this component isn't in the pending list.
// 如果在等待集合中有这个要启动的Service,那么从mPendingServices这个集合中移除
for (int i=mPendingServices.size()-1; i>=0; i--) {
final ServiceRecord pr = mPendingServices.get(i);
if (pr.serviceInfo.applicationInfo.uid == sInfo.applicationInfo.uid
&& pr.name.equals(name)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Remove pending: " + pr);
// 移除
mPendingServices.remove(i);
}
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Retrieve created new service: " + r);
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
}
```
这段代码我们看到首先从PMS中获取这个service在minifest中的信息,然后封装成ServiceInfo对象,之后重新封装新的ComponentName对象。之后如果flag是FLAG_EXTERNAL_SERVICE的话,我们看到必须还要满足两个条件,一个是android:exported要为true,另一个是android:isolatedProcess也要为true,即需要是一个独立的进程。可见要允许在调用者的进程中不光是一个FLAG_EXTERNAL_SERVICE就可以了,满足的条件也挺多了。这些条件都符合了之后,获取调用者的进程信息ApplicationInfo,然后把前面minifest解析出的ServiceInfo的包名和uid用调用者的替换,最后ComponentName重新创建新的再设给要启动的Intent。
之后由于FLAG_EXTERNAL_SERVICE这个flag的操作可能会改变service的一些属性,所以下面重新获取下userId和ServiceMap,然后再从ServiceMap中根据ComponentName获取下serviceRecord,如果还是没有的话那就需要new一个ServiceRecord了,然后把他放入ServiceMap中,最后看看mPendingServices这个集合中是否有这个service,这个集合是等待启动但是还没有得到执行的机会的集合,目前这个启动的service由于新new了一个,所以把老的移除了,后面步骤再添加进这个集合。好了,涉及到FLAG_EXTERNAL_SERVICE这个flag的处理就完成了,我们再继续往下看:
```java
if (r != null) {
// 检查下权限,返回0有权限
if (mAm.checkComponentPermission(r.permission,
callingPid, callingUid, r.appInfo.uid, r.exported) != PERMISSION_GRANTED) {
if (!r.exported) {
Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+ " from pid=" + callingPid
+ ", uid=" + callingUid
+ " that is not exported from uid " + r.appInfo.uid);
return new ServiceLookupResult(null, "not exported from uid "
+ r.appInfo.uid);
}
Slog.w(TAG, "Permission Denial: Accessing service " + r.name
+ " from pid=" + callingPid
+ ", uid=" + callingUid
+ " requires " + r.permission);
return new ServiceLookupResult(null, r.permission);
} else if (r.permission != null && callingPackage != null) {
final int opCode = AppOpsManager.permissionToOpCode(r.permission);
if (opCode != AppOpsManager.OP_NONE && mAm.mAppOpsService.noteOperation(
opCode, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
Slog.w(TAG, "Appop Denial: Accessing service " + r.name
+ " from pid=" + callingPid
+ ", uid=" + callingUid
+ " requires appop " + AppOpsManager.opToName(opCode));
return null;
}
}
if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
resolvedType, r.appInfo)) {
return null;
}
// 正常获取一个ServiceRecord
return new ServiceLookupResult(r, null);
}
return null;
```
这是这个方法的最后一段代码,这里主要是做一些权限方法的检查,如果一切都正常最后把serviceRecord封装为ServiceLookupResult对象返回。我们回到前面的startServiceLocked方法,我们继续看这个方法的下一段:
```java
ServiceRecord r = res.record; // 取出serviceRecord
// 启动的Service的userId不存在,return
if (!mAm.mUserController.exists(r.userId)) {
Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId);
return null;
}
// If this isn't a direct-to-foreground start, check our ability to kick off an
// arbitrary service
// 还没开始请求启动并且非前台的启动,那么下面要检查下
if (!r.startRequested && !fgRequired) {
// Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
r.appInfo.targetSdkVersion, callingPid, false, false);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
Slog.w(TAG, "Background start not allowed: service "
+ service + " to " + r.name.flattenToShortString()
+ " from pid=" + callingPid + " uid=" + callingUid
+ " pkg=" + callingPackage);
if (allowed == ActivityManager.APP_START_MODE_DELAYED) {
// In this case we are silently disabling the app, to disrupt as
// little as possible existing apps.
return null;
}
// This app knows it is in the new model where this operation is not
// allowed, so tell it what has happened.
UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid);
// 后台启动service报错
return new ComponentName("?", "app is in background uid " + uidRec);
}
}
NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
callingUid, r.packageName, service, service.getFlags(), null, r.userId);
// If permissions need a review before any of the app components can run,
// we do not start the service and launch a review activity if the calling app
// is in the foreground passing it a pending intent to start the service when
// review is completed.
// 是否要启动一个权限的界面
if (mAm.mPermissionReviewRequired) {
if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
callingUid, service, callerFg, userId)) {
return null;
}
}
// 由于这个service要启动了,所以如果之前有重启的话,先移除
if (unscheduleServiceRestartLocked(r, callingUid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
}
r.lastActivity = SystemClock.uptimeMillis(); // 最后活跃时间
r.startRequested = true; // 开始请求调用
r.delayedStop = false; // 是否需要延迟关闭,这里false不需要延迟关闭
r.fgRequired = fgRequired; // 是否是前台请求
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid)); // 还没有处理过的启动请求添加到
```
这个方法开始先取出ServiceRecord,然后校验下这个useId是否存在,都正常的话,下面开始检查我们平时比较常见的一个在后台默默启动service时候会报的错误,这里是调用getAppStartModeLocked方法检查是否允许这个后台的service允许:
```java
// 这个方法会判断是否允许启动一个后台的service
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk,
int callingPid, boolean alwaysRestrict, boolean disabledOnly) {
// uid的包装类
UidRecord uidRec = mActiveUids.get(uid);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid + " pkg="
+ packageName + " rec=" + uidRec + " always=" + alwaysRestrict + " idle="
+ (uidRec != null ? uidRec.idle : false));
// UidRecord是null表示这个进程还没有启动,或者alwaysRestrict是true,或者当前这个UidRecord是idle(表示这个进程不活跃)就需要进一步判断
if (uidRec == null || alwaysRestrict || uidRec.idle) {
boolean ephemeral;
if (uidRec == null) {
ephemeral = getPackageManagerInternalLocked().isPackageEphemeral(
UserHandle.getUserId(uid), packageName);
} else {
ephemeral = uidRec.ephemeral;
}
if (ephemeral) {
// We are hard-core about ephemeral apps not running in the background.
return ActivityManager.APP_START_MODE_DISABLED;
} else {
if (disabledOnly) {
// The caller is only interested in whether app starts are completely
// disabled for the given package (that is, it is an instant app). So
// we don't need to go further, which is all just seeing if we should
// apply a "delayed" mode for a regular app.
return ActivityManager.APP_START_MODE_NORMAL;
}
// 正常startService这里alwaysRestrict是false,这里的结果就决定了是否允许后台启动service
final int startMode = (alwaysRestrict)
? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk)
: appServicesRestrictedInBackgroundLocked(uid, packageName,
packageTargetSdk);
if (DEBUG_BACKGROUND_CHECK) Slog.d(TAG, "checkAllowBackground: uid=" + uid
+ " pkg=" + packageName + " startMode=" + startMode
+ " onwhitelist=" + isOnDeviceIdleWhitelistLocked(uid));
if (startMode == ActivityManager.APP_START_MODE_DELAYED) {
// This is an old app that has been forced into a "compatible as possible"
// mode of background check. To increase compatibility, we will allow other
// foreground apps to cause its services to start.
if (callingPid >= 0) {
ProcessRecord proc;
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(callingPid);
}
if (proc != null &&
!ActivityManager.isProcStateBackground(proc.curProcState)) {
// Whoever is instigating this is in the foreground, so we will allow it
// to go through.
return ActivityManager.APP_START_MODE_NORMAL;
}
}
}
return startMode;
}
}
return ActivityManager.APP_START_MODE_NORMAL;
}
```
这个方法我们看到首先取得当前已经在运行的uid里面是否有启动进程的uid,如果为null或者这个调用者是idle状态,这两种情况都是属于非正常情况,所以会继续进入if分支判断,alwaysRestrict这个值为true表示需要限制启动所以也会进入这个分支。正常情况下我们这个alwaysRestrict调用的时候是false,而且如果我们一个进程还没启动或者启动后处于idle状态了,就是我们平时最常遇到了在后台想启动一个service而不被允许的情况,所以这里会进入if分支。
这里其他的情况我们先不管,主要看下最常遇到的情况会执行appServicesRestrictedInBackgroundLocked这个方法,我们看下:
```java
// 检查这个app是否在白名单里面,不在的话调用appRestrictedInBackGroundLocked方法,看是否是android 8以上的
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Persistent app?
// 常驻app不受后台启动限制
if (mPackageManagerInt.isPackagePersistent(packageName)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " is persistent; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Non-persistent but background whitelisted?
// 检查下这个uid的app是否在白名单里面
if (uidOnBackgroundWhitelist(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on background whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// Is this app on the battery whitelist?
// 是否在电池白名单里面
if (isOnDeviceIdleWhitelistLocked(uid)) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName
+ " on idle whitelist; not restricted in background");
}
return ActivityManager.APP_START_MODE_NORMAL;
}
// None of the service-policy criteria apply, so we apply the common criteria
return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk);
}
```
这个方法首先调用isPackagePersistent这个方法,这个方法是判断常驻进程,他们不受后台启动的限制,所以正常返回。接着调用uidOnBackgroundWhitelist方法,看看是否在白名单里面,如果在的话,也正常返回。最后调用isOnDeviceIdleWhitelistLocked方法看看是否在设备idle的白名单里,如果在的话也正常返回。所以上面这三种情况都是例外的情况,如果都不在的话,接着继续调用appRestrictedInBackgroundLocked方法来判断:
```java
int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) {
// Apps that target O+ are always subject to background check
// android O 开始不允许后台启动servicce,这里返回APP_START_MODE_DELAYED_RIGID
if (packageTargetSdk >= Build.VERSION_CODES.O) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
}
return ActivityManager.APP_START_MODE_DELAYED_RIGID;
}
............
}
```
到了这里大家就可以看到只要大于等于android O中,后台启动的话,就会返回APP_START_MODE_DELAYED_RIGID这个值了,我们已经可以猜测他就是限制我们在android O上启动后台service的地方了。我们回到startServiceLocked方法。
如果返回的是APP_START_MODE_DELAYED_RIGID,那么最后会返回new ComponentName("?", "app is in background uid " + uidRec),可以看到这里的返回就是我们最开始调用startService方法的时候抛出异常的结果。到这里,我们就明白了android O上是怎么限制一个Service的了。好了,这里就说到这,我们回到startServiceLocked方法,继续看后面代码。
之后会判断是否在启动一个service前有一个授权权限的界面。我们一般第一次打开一个app都会有个授权界面让用户知道需要给予什么权限,service也是一样的,如果需要弹出这样一个界面会调用requestStartTargetPermissionsReviewIfNeededLocked让用户知道,我们简单看下这个方法,其实就是发出一个Intent:
```java
private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
String callingPackage, int callingUid, Intent service, boolean callerFg,
final int userId) {
if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
r.packageName, r.userId)) {
// Show a permission review UI only for starting from a foreground app
if (!callerFg) {
Slog.w(TAG, "u" + r.userId + " Starting a service in package"
+ r.packageName + " requires a permissions review");
return false;
}
IIntentSender target = mAm.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_SERVICE, callingPackage,
callingUid, userId, null, null, 0, new Intent[]{service},
new String[]{service.resolveType(mAm.mContext.getContentResolver())},
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_IMMUTABLE, null);
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, r.packageName);
intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
if (DEBUG_PERMISSIONS_REVIEW) {
Slog.i(TAG, "u" + r.userId + " Launching permission review for package "
+ r.packageName);
}
mAm.mHandler.post(new Runnable() {
@Override
public void run() {
mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
}
});
return false;
}
return true;
}
```
这里可以看到如果要弹出这个界面,调用者必须是前台的,否则就会退出。这样其实也是对的,如果在后台,无缘无故突然弹出一个界面,用户也会觉得莫名其妙,所以如果是前台的话,最后会调用startActivityAsUser来启动这个Intent显示界面。好,我们稍微看一眼知道就可以了,我们回去再接着看代码。
之后会调用unscheduleServiceRestartLocked方法,这个方法是把当前如果有重启这个service的话需要取消,因为我们现在已经在处理这个service的启动了,所以取消之前的重启,我们也看一眼这个方法:
```java
// 从重启集合中移除这个ServiceRecord
private final boolean unscheduleServiceRestartLocked(ServiceRecord r, int callingUid,
boolean force) {
if (!force && r.restartDelay == 0) {
return false;
}
// Remove from the restarting list; if the service is currently on the
// restarting list, or the call is coming from another app, then this
// service has become of much more interest so we reset the restart interval.
boolean removed = mRestartingServices.remove(r);
if (removed || callingUid != r.appInfo.uid) {
r.resetRestartCounter();
}
if (removed) {
clearRestartingIfNeededLocked(r);
}
mAm.mHandler.removeCallbacks(r.restarter);
return true;
}
```
可以看到这里有个mRestartingServices集合,这个集合中保存的都是需要重启的service。一般有些service运行过程中crash了,会被要求重启,所以都会放到这个集合中,现在既然我们要启动一个service了,所以先从重启的集合中移除掉。
我们再回到startServiceLocked方法,接着设置一些启动前的数据,比如启动时间,是前台还是后台等,最后把这个serviceRecord放入pendingStarts这个集合,这个集合是那些待启动的service,后面再启动的时候我们会在遇见他,目前我们知道启动的service已经在这个集合里了。我们继续看下一段代码:
```java
final ServiceMap smap = getServiceMapLocked(r.userId);
// addToStarting表示是否可以在后台运行,true表示可以,默认false,不允许后台运行
boolean addToStarting = false;
// 调用调用者非前台的,并且也是一个非前台的请求,同时启动的Service的进程是null
if (!callerFg && !fgRequired && r.app == null
&& mAm.mUserController.hasStartedUserState(r.userId)) {
// 进入这里说明进程还没启动,先获取下ProcessRecord有没有
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
// 进程还没起来或者进程当前状态是大于PROCESS_STATE_RECEIVER(后台reciver以上的状态,其实就是优先级比较低的情况)
if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of "
+ r + " in " + proc);
if (r.delayed) { // 已经是个延迟启动的service了,既然优先级比较低的或者进程还没起来的继续延迟
// This service is already scheduled for a delayed start; just leave
// it still waiting.
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
return r.name;
}
// 后台进程已经大于了最大允许后台运行的数量,return
if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
// Something else is starting, delay!
Slog.i(TAG_SERVICE, "Delaying start of: " + r);
// 放入延迟启动service的集合
smap.mDelayedStartList.add(r);
r.delayed = true;
return r.name;
}
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
// 没有上面2个限制,那么就是可以启动的
addToStarting = true;
} else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
// 进入这里说明是一个后台service后者receiver,说明已经有个service或者receiver在运行了,那么就直接允许允许了
addToStarting = true;
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Not delaying, but counting as bg: " + r);
} else if (DEBUG_DELAYED_STARTS) {
StringBuilder sb = new StringBuilder(128);
sb.append("Not potential delay (state=").append(proc.curProcState)
.append(' ').append(proc.adjType);
String reason = proc.makeAdjReason();
if (reason != null) {
sb.append(' ');
sb.append(reason);
}
sb.append("): ");
sb.append(r.toString());
Slog.v(TAG_SERVICE, sb.toString());
}
} else if (DEBUG_DELAYED_STARTS) {
if (callerFg || fgRequired) {
Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + " uid="
+ callingUid + " pid=" + callingPid + " fgRequired=" + fgRequired + "): " + r);
} else if (r.app != null) {
Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r);
} else {
Slog.v(TAG_SERVICE,
"Not potential delay (user " + r.userId + " not started): " + r);
}
}
// 继续启动
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
```
这里是startServiceLocked这个方法的最后一部分代码了。如果能走到这里,说明前面限制启动service已经通过了,可以启动service了,如果是一个前台的service那自然不用说当然可以启动,如果是一个后台的service,那么这里会再最后进行一下过滤,我们看具体的代码。
这里还是取出前面介绍过的ServiceMap,这里包含了这个用户下的ServiceRecord。接着addToStarting这个变量表示是否在后台运行,true表示可以。首先这里会判断调用者是否是后台,启动的Service是否要求前台,在android O上如果要启动一个前面的service需要调用startForegroundService方法,如果调用的是他,这里的fgRequired就是true。同时如果启动的service还没有启动过,那么这里会进入第一个if分支。这个分支首先从AMS中获取下这个进程的ProcessRecord,如果没有的话或者这个进程当前状态是大于PROCESS_STATE_RECEIVER值的。这里关于进程的状态也简单提一下,前面介绍过一个进程调用组的概念,调用者优先级的高低决定了CPU在分配资源的时候的倾向性,而这里每个进程自己也有个优先级,数值越小,优先级越高表示这个进程越重要,我们看下面这个表:

从这个表来看,我们代码中大于PROCESS_STATE_RECEIVER就是大于11,这些进程都是优先级比较低的,不立马运行也不会有太大的影响,所以这里代码会判断,如果这个service是需要延迟的,那么先return,等下次再说。另外这里有个mStartingBackground集合,这里面保存了所有将要在后台启动的service,如果这里面的数量大于了mMaxStartingBackground这个值,那么把他加入mDelayedStartList这个集合,这个集合是所有延迟执行的service。最后把addToStarting置为true,表示可以在后台运行。
之后一个else分支表示当进程存在的时候,并且进程当前状态大于PROCESS_STATE_SERVICE,从上面的表格中我们看到,这表示这个进程是service和receiver,那么就运行后台运行了,把addToStarting也置为true。
这里设置addToStarting为true表示这个启动的service是在后台运行的,这个标识会传入后面的方法,在启动完service后,会通过这个参数不同值来进行下一步的处理。我们下面就看后面的方法startServiceInnerLocked:
```java
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
ServiceState stracker = r.getTracker();
if (stracker != null) {
// 更新下service state状态
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
}
r.callStart = false;
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
// 继续调用
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
if (error != null) {
return new ComponentName("!!", error);
}
// 如果这个service已经请求启动了,并且是后台服务
if (r.startRequested && addToStarting) {
boolean first = smap.mStartingBackground.size() == 0;
// 先加入后台服务请求启动集合中
smap.mStartingBackground.add(r);
r.startingBgTimeout = SystemClock.uptimeMillis() + mAm.mConstants.BG_START_TIMEOUT;
if (DEBUG_DELAYED_SERVICE) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r, here);
} else if (DEBUG_DELAYED_STARTS) {
Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r);
}
// 如果后台服务请求启动集合中就一个,那么执行rescheduleDelayedStartsLocked
if (first) {
smap.rescheduleDelayedStartsLocked();
}
} else if (callerFg || r.fgRequired) {
// 如果是前台服务,或者还没有请求启动,调用这里
smap.ensureNotStartingBackgroundLocked(r);
}
return r.name;
}
```
这个方法里面就开始要启动service了。这里会继续调用启动service的方法bringUpServiceLocked。这个方法呆会儿看,我们先把这个方法后面的代码讲完。启动完之后,会根据这个service是前台还是后台做不同处理。如果是后台service的话,会把这个service加入一个正在启动的后台service集合中,即mStartingBackground,同时设置这个service的超时时间,如果这个是最后一个启动的后台service了,那么就会从延迟的service启动集合中再启动一个。如果这个是前台的service,那么会调用ensureNotStartingBackgroundLocked方法,这个方法会查看正在后台启动的service集合和延迟启动的service集合中是否有它,如果有的话会从那里移除,同时如果移除了一个正在启动的后台service,那么也会从延迟启动的service集合中在启动一个。
这个方法最主要的是bringUpServiceLocked,这个方法会继续启动service,由于这个是核心方法,顺着这个方法就一直会分析下去了,所以我们等会讲,先看看后面这些启动回来后处理的相关方法,一个是调用延迟启动service集合的方法rescheduleDelayedStartsLocked,另一个是如果是前台service启动完后调用的ensureNotStartingBackgroundLocked。由于ensureNotStartingBackgroundLocked方法中包含了rescheduleDelayedStartsLocked,所以我们就讲ensureNotStartingBackgroundLocked这个方法就可以了:
```java
void ensureNotStartingBackgroundLocked(ServiceRecord r) {
if (mStartingBackground.remove(r)) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"No longer background starting: " + r);
rescheduleDelayedStartsLocked();
}
if (mDelayedStartList.remove(r)) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "No longer delaying start: " + r);
}
}
```
这个方法比较简单,把这个前台启动的service从mStartingBackground和mDelayedStartList中移除,如果是从mStartingBackground移除的话,会继续调用rescheduleDelayedStartsLocked来启动一个延迟启动的service。我们看下这个方法:
```java
void rescheduleDelayedStartsLocked() {
removeMessages(MSG_BG_START_TIMEOUT);
final long now = SystemClock.uptimeMillis();
// 遍历所有正在后台启动的service
for (int i=0, N=mStartingBackground.size(); i<N; i++) {
ServiceRecord r = mStartingBackground.get(i);
// 如果超时了,移除
if (r.startingBgTimeout <= now) {
Slog.i(TAG, "Waited long enough for: " + r);
mStartingBackground.remove(i);
N--;
i--;
}
}
while (mDelayedStartList.size() > 0
&& mStartingBackground.size() < mMaxStartingBackground) {
// 获取一个延迟service
ServiceRecord r = mDelayedStartList.remove(0);
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"REM FR DELAY LIST (exec next): " + r);
if (r.pendingStarts.size() <= 0) {
Slog.w(TAG, "**** NO PENDING STARTS! " + r + " startReq=" + r.startRequested
+ " delayedStop=" + r.delayedStop);
}
if (DEBUG_DELAYED_SERVICE) {
if (mDelayedStartList.size() > 0) {
Slog.v(TAG_SERVICE, "Remaining delayed list:");
for (int i=0; i<mDelayedStartList.size(); i++) {
Slog.v(TAG_SERVICE, " #" + i + ": " + mDelayedStartList.get(i));
}
}
}
// 设置不延迟了
r.delayed = false;
try {
// 启动
startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true);
} catch (TransactionTooLargeException e) {
// Ignore, nobody upstack cares.
}
}
if (mStartingBackground.size() > 0) {
// 如果有正在后台启动的service
ServiceRecord next = mStartingBackground.get(0);
// 获取他的超时时间
long when = next.startingBgTimeout > now ? next.startingBgTimeout : now;
if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Top bg start is " + next
+ ", can delay others up to " + when);
// 发送handler消息,这个消息在超时后会重新调用这个方法,以便移除超时的后台service,启动延迟的service
Message msg = obtainMessage(MSG_BG_START_TIMEOUT);
sendMessageAtTime(msg, when);
}
if (mStartingBackground.size() < mMaxStartingBackground) {
// 处理广播
mAm.backgroundServicesFinishedLocked(mUserId);
}
}
```
这个方法开始遍历正在启动的后台service集合mStartingBackground,如果超时了,将他移除。之后遍历延迟启动的service集合,启动他们。之后如果正在启动的后台service集合中有数据,那么就发送一个handler的消息,消息以这个后台service超时为时间点,一旦超时了还会调用rescheduleDelayedStartsLocked这个方法,继续上面的处理。之后还会处理广播,这个这里就不说了。
上面这个就是在启动一个service后的处理。我们回到前面说的启动service核心方法bringUpServiceLocked,继续主线流程的分析:
```java
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
//Slog.i(TAG, "Bring up service:");
//r.dump(" ");
// service进程已经启动了
if (r.app != null && r.app.thread != null) {
// 发送消息给service,即会调用startCommand
sendServiceArgsLocked(r, execInFg, false);
return null;
}
// service正在重启
if (!whileRestarting && mRestartingServices.contains(r)) {
// If waiting for a restart, then do nothing.
return null;
}
if (DEBUG_SERVICE) {
Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent + " fg=" + r.fgRequired);
}
// We are now bringing the service up, so no longer in the
// restarting state.
// 到这里,这个service准备启动了,从mRestartingServices中移除
if (mRestartingServices.remove(r)) {
clearRestartingIfNeededLocked(r);
}
// Make sure this service is no longer considered delayed, we are starting it now.
// 如果是延迟的service,从mDelayedStartList中移除
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}
// Make sure that the user who owns this service is started. If not,
// we don't want to allow it to run.
// 如果这个用户还没有开始启动,停止
if (!mAm.mUserController.hasStartedUserState(r.userId)) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": user " + r.userId + " is stopped";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
// Service is now being launched, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.packageName, false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.packageName + ": " + e);
}
```
这个方法也比较长,我们分两段来看。这里第一段,首先如果这个service已经启动了,那么会调用sendServiceArgsLocked方法,这个方法最终会调用到service的onStartCommand方法,这个方法我们稍后来看。
之后如果这个进程正在重启中,并且此次调用非重启,而是一个正常启动,那么就return了,因为既然只是想启动,只要启动了就可以,现在已经在重启了,那么就让他重启吧,也不要在做启动的事情了。
但是如果此次调用本来就是要重启的,那么就从重启的集合中移除他,因为想自己重来来启动,所以即使你已经重启了,我还是要自己来重启,所以会从重启集合中移除掉现有的重启service。
再往后,如果这个service在延迟启动集合中有的话,也要移除,因为现在已经在启动了。
下面的话如果这个用户不允许启动这个service,那么会调用bringDownServiceLocked这个方法关闭service,这个方法等我们分析完service的启动流程后再来说,这里知道有这个方法就可以了。
最后设置启动的这个进程包不能是停止状态。
上半段代码主要是处理些异常和校验,下面我们在看下半段代码:
```java
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
// 进程名
final String procName = r.processName;
String hostingType = "service";
ProcessRecord app;
if (!isolated) { // 非隔离进程
// 获取ProcessRecord
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
if (app != null && app.thread != null) { // 进程非空
try {
// 设置包名
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
// 继续启动
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
throw e;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
} else {
// If this service runs in an isolated process, then each time
// we call startProcessLocked() we will get a new isolated
// process, starting another process if we are currently waiting
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
// 隔离进程的话先看看serviceRecord是否有给出这个进程
// 如果给出了那么就继续,否则下面方法会根据app是否为null来创建进程
app = r.isolatedProc;
if (WebViewZygote.isMultiprocessEnabled()
&& r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
hostingType = "webview_service";
}
}
// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
// 如果进程是null,permissionsReviewRequired是false,正常startService调用时候传入的是false。继续启动
if (app == null && !permissionsReviewRequired) {
// 启动一个进程
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingType, r.name, false, isolated, false)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
// 如果启动失败的话,调用bringDownServiceLocked
bringDownServiceLocked(r);
return msg;
}
if (isolated) { // 隔离进程
r.isolatedProc = app;
}
}
// 如果等待进程集合中没有包括这个serviceRecord,那么就加入
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
if (r.delayedStop) { // 如果设置了delayedStop表示一旦启动了,那么就会stop
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) { // 如果启动了,下面调用stopServiceLocked
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (in bring up): " + r);
stopServiceLocked(r);
}
}
return null;
```
首先这里先获取是否启动要在一个隔离的进程中,如果不是的话,从AMS中获取ProcessRecord,如果存在的话会继续调用realStartServiceLocked方法继续启动。如果是要启动在一个隔离进程的话,这个会取出serviceRecord的isolatedProc变量,如果非空的话,说明之前已经创建过这个隔离进程了,可能是之前启动没成功,现在又重新启动了,所以还是用这个进程来启动。
之后就会判断这个要启动的service到底有没有进程,如果没有的话会就会调用startProcessLocked来创建进行,这个方法我们在AMS中也有看到过这里就不在说了,后续会专门有启动一个进程的文章,到那里再细讲。如果进程启动失败还是会调用bringDownServiceLocked方法关闭service,如果启动成功了,是隔离进程的话,会把这个进程保存在serviceRecord中。
之后会把这个serviceRecord保存在mPendingServices这个集合中,这个集合我们之前也看到过,是所有待启动的集合,我们在后面真正启动的时候再会看到他。
最后如果这个service被要求停止了,那么如果已经准备启动了,那么会立即调用stopServiceLocked,关于stopService相关的,等分析完startService后再分析。
至此一个大的流程也就结束了,这个方法里面有好几个方法我们还没深入去讲。第一个sendServiceArgsLocked方法,是service已经启动的情况下,调用它会执行到onStartCommand方法。第二个如果service还没启动,但是进程已经启动了,那么会调用realStartServiceLocked继续启动。第三如果进程也没启动会先启动一个进程,然后再启动service。我们先看第一个sendServiceArgsLocked方法。
# service已经启动,调用sendServiceArgsLocked方法
```java
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
// 集合中没有service,return
final int N = r.pendingStarts.size();
if (N == 0) {
return;
}
// 这个是发送给客户端启动service的参数集合
ArrayList<ServiceStartArgs> args = new ArrayList<>();
// 遍历待启动的Service
while (r.pendingStarts.size() > 0) {
// 获取第一个
ServiceRecord.StartItem si = r.pendingStarts.remove(0);
if (DEBUG_SERVICE) {
Slog.v(TAG_SERVICE, "Sending arguments to: "
+ r + " " + r.intent + " args=" + si.intent);
}
// 如果serviceRecord的intent是null,但是pendingStarts里面还有,那么继续下一个
if (si.intent == null && N > 1) {
// If somehow we got a dummy null intent in the middle,
// then skip it. DO NOT skip a null intent when it is
// the only one in the list -- this is to support the
// onStartCommand(null) case.
continue;
}
// 更新启动这个service的时间
si.deliveredTime = SystemClock.uptimeMillis();
// 加入待分发的集合
r.deliveredStarts.add(si);
// 分发次数+1
si.deliveryCount++;
if (si.neededGrants != null) {
mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,
si.getUriPermissionsLocked());
}
mAm.grantEphemeralAccessLocked(r.userId, si.intent,
r.appInfo.uid, UserHandle.getAppId(si.callingId));
// 设置Handler超时
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
mAm.updateOomAdjLocked(r.app, true);
}
// startForground逻辑,请求前台service并且fgWaiting还是false
if (r.fgRequired && !r.fgWaiting) {
// 当前没有在前台
if (!r.isForeground) {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
}
// startForgroundService启动后,需要在规定时间内调用startForground,否则ANR,这里是发生handler消息
scheduleServiceForegroundTransitionTimeoutLocked(r);
} else {
if (DEBUG_BACKGROUND_CHECK) {
Slog.i(TAG, "Service already foreground; no new timeout: " + r);
}
// 到这里service已经在前台了,所以不需要再进行前台处理,fgRequired置为false
r.fgRequired = false;
}
}
int flags = 0;
if (si.deliveryCount > 1) { // 如果分发次数大于1,添加以下flag
flags |= Service.START_FLAG_RETRY;
}
if (si.doneExecutingCount > 0) { // 执行返回次数大于0,添加以下flag
flags |= Service.START_FLAG_REDELIVERY;
}
// 封装请求到集合中
args.add(new ServiceStartArgs(si.taskRemoved, si.id, flags, si.intent));
}
```
这个方法我们也分两部分讲。首先,pendingStarts这个待启动集合中如果没有要启动的service,那么就退出。
之后开始遍历pendingStarts集合,这个集合是在startServiceLocked方法中,确定可以启动一个service后添加的,忘记的可以去startServiceLocked方法中看下。这里取出每个启动项,如果启动项的intent是null的话就跳过,但是如果只有这一个启动项的话,那么也只有继续执行下去了,只不过最后回调到客户端的onStartCommand方法的intent参数是null。
之后更新下启动的时间,并且把这个启动项从之前的pendingStarts转移到deliveredStarts中,deliveredStarts相比起pendingStarts集合来说,都是要启动的service集合,只不过把pendingStarts理解成初步确定的启动项,而deliveredStarts是最终确定的启动项集合,比如这里intent为null的话有可能会被过滤掉,所以这里用两个集合来分开处理。
之后判断了一些权限之后,会调用bumpServiceExecutingLocked来添加启动service的超时消息,我们稍稍看下这个方法:
```java
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
+ why + " of " + r + " in app " + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
+ why + " of " + r.shortName);
long now = SystemClock.uptimeMillis();
if (r.executeNesting == 0) { // 是否是第一次执行启动
r.executeFg = fg; // 是否前台执行
ServiceState stracker = r.getTracker();
if (stracker != null) {
// 设置正在执行以及执行的时间
stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
}
// 如果这个进程存在的话
if (r.app != null) {
// 把这个service加入正在执行的集合中
r.app.executingServices.add(r);
r.app.execServicesFg |= fg; // 这里fg表示调用者是不是前台进程
if (r.app.executingServices.size() == 1) {
// 进入到这里的分支,说明,这个service是第一次执行,并且正在执行的service也是第一个,设置Handler的超时
scheduleServiceTimeoutLocked(r.app);
}
}
} else if (r.app != null && fg && !r.app.execServicesFg) {
// 调用者是前台的,启动进程在后台
r.app.execServicesFg = true;
scheduleServiceTimeoutLocked(r.app);
}
r.executeFg |= fg; // 是否是前台service
r.executeNesting++;
r.executingStart = now;
}
```
首先这里判断这个service是不是第一次执行,是的话,会把这个serviceRecord加入进程的executingServices集合中,如果这个集合中这个启动的service是第一个,那么就调用scheduleServiceTimeoutLocked方法向handler添加超时消息。否则如果不是第一次执行了,并且调用者是前台,进程是在后台,那么会再次添加一个超时消息。其实这里再次添加主要是因为进程的execServicesFg是false,表示是在后台,我们马上会看到scheduleServiceTimeoutLocked这个方法,前台进程超时20秒,后台进程超时200秒,这里既然execServicesFg是false那么就说明前面可能添加过1个200秒的后台超时,但是这里调用者是前台的所以这个service也会作为前台启动,而前台超时比较短,所以要在添加一个20秒的超时。这个其实ANR的超时处理了,其实ANR在触发后会自动更新下一次的ANR,所以对于同一个service启动,不需要每次都添加,这个考虑到了前后台超时不一样所以添加了2次,其实问题也不大,只不过后面接触ANR消息的时候也需要多处理一下,我们后台还是在分析关于ANR的代码,这里先知道是怎么添加Handler消息的。我们看下scheduleServiceTimeoutLocked这个方法:
```java
// 设置启动service时候Handler超时
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
```
这个方法很简单,我们注意到的是最后一行代码,如果是个前台service,超时时间是SERVICE_TIMEOUT,这个是20秒。后台service是SERVICE_BACKGROUND_TIMEOUT,这个是200秒。这个是android 8.0的数据,高版本可能有变化。具体Handler处理超时的方法我们到后面再看。这里我们先回到sendServiceArgsLocked方法。
之后的一段代码是startForgroundService的逻辑,我们知道在android O开始不允许后台起service,但是提供了一个新的方法startForgroundService,这个方法在后台起service后,需要在规定时间内执行startForground方法,这样就不会出现ANR,相信开发过这个的同学应该都很熟悉,我们直接看代码这个是怎么处理的。
serviceRecord的fgRequired变量表示的就是启动一个前台service,即调用startForgroundService方法时候会赋值这个变量。fgWaiting表示是否在等待前台回复startForground,首次调用肯定是false,所以这里会调用scheduleServiceForegroundTransitionTimeoutLocked这个方法:
```java
void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
if (r.app.executingServices.size() == 0 || r.app.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
msg.obj = r;
r.fgWaiting = true;
// 5秒内要调用startForground
mAm.mHandler.sendMessageDelayed(msg, SERVICE_START_FOREGROUND_TIMEOUT);
}
```
这个方法也很简答,向handler发送一个消息,这里可以看到在android 8.0中,前台需要SERVICE_START_FOREGROUND_TIMEOUT,即5秒内调用startForground方法,否则会出现ANR,高版本android这个时间会有不同。我们再回到sendServiceArgsLocked方法。
之后如果这不是第一次启动service,添加START_FLAG_RETRY的flag,如果不是第一次执行service完毕了,添加START_FLAG_REDELIVERY的flag。最后把这个启动项封装为ServiceStartArgs对象,添加到集合中。我们继续看sendServiceArgsLocked这个方法的后半段代码:
```java
ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
// 最多可以发送4个请求
slice.setInlineCountLimit(4);
Exception caughtException = null;
try {
// binder发送请求去,触发onStartCommand
r.app.thread.scheduleServiceArgs(r, slice);
} catch (TransactionTooLargeException e) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large for " + args.size()
+ " args, first: " + args.get(0).args);
Slog.w(TAG, "Failed delivering service starts", e);
caughtException = e;
} catch (RemoteException e) {
// Remote process gone... we'll let the normal cleanup take care of this.
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
Slog.w(TAG, "Failed delivering service starts", e);
caughtException = e;
} catch (Exception e) {
Slog.w(TAG, "Unexpected exception", e);
caughtException = e;
}
// 如果上面调用发送错误,下面会执行serviceDoneExecutingLocked
if (caughtException != null) {
// Keep nesting count correct
final boolean inDestroying = mDestroyingServices.contains(r);
for (int i = 0; i < args.size(); i++) {
// 发生错误,执行方法
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
}
if (caughtException instanceof TransactionTooLargeException) {
throw (TransactionTooLargeException)caughtException;
}
}
```
后半段代码就是把前半段代码封装的启动项集合再封装成ParceledListSlice集合,之后通过跨进程调用scheduleServiceArgs方法会执行到service进程中,我们稍等下再看,这里把最后一点代码先看完。
最后一段代码如果启动报错了,会看下mDestroyingServices集合中是否有这个service,有就表示这个servicce需要销毁,最终会调用serviceDoneExecutingLocked这个方法,这个方法后面我们还会看到,主要是执行一个service完毕后,最后的处理工作,我们等到后面再说这个方法。好了这里在AMS部分也就完成了,我们回过头看上面的scheduleServiceArgs方法,这个方法会走到service进程那里,我们跟过去看。
# service进场的scheduleServiceArgs方法
```java
public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
List<ServiceStartArgs> list = args.getList();
// 遍历发送过来的数据,继续通过hander发送下去
for (int i = 0; i < list.size(); i++) {
ServiceStartArgs ssa = list.get(i);
ServiceArgsData s = new ServiceArgsData();
s.token = token;
s.taskRemoved = ssa.taskRemoved;
s.startId = ssa.startId;
s.flags = ssa.flags;
s.args = ssa.args;
sendMessage(H.SERVICE_ARGS, s);
}
}
```
这种方法的套路,如果熟悉AMS的同学应该都懂了,我们直接到handler那里看后面的方法。
```java
case SERVICE_ARGS:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));
// startService继续发送
handleServiceArgs((ServiceArgsData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
```
继续看handleServiceArgs方法:
```java
private void handleServiceArgs(ServiceArgsData data) {
// 通过ServiceRecor取出service
Service s = mServices.get(data.token);
if (s != null) { // service不为空
try {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
data.args.prepareToEnterProcess();
}
int res;
if (!data.taskRemoved) {
// 这里就是回调service的onStartCommand方法了
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
// 这里说明taskRemoved是true,表示是应用的task被移除发起的请求,会回调onTaskRemoved方法
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
QueuedWork.waitToFinish();
try {
// 最后再通知AMS的serviceDoneExecuting方法
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
ensureJitEnabled();
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to start service " + s
+ " with " + data.args + ": " + e.toString(), e);
}
}
}
}
```
这里mServices是一个map,如果第一次创建service的话,会在进程这里保存要创建的那个service,由于现在我们分析的是service已经存在的情况,所以这里AMS那边传过来的serviceRecord取出具体的service。这个保存的过程我们会在后面分析创建service的时候说到。
之后这里taskRemoved为true表示这个应用所在的task被移除了,所以也会发出这个请求,这个情况我们先不管。为false的时候就是我们正常启动一个service的情况,这里可以看到onStartCommand方法,这个我们开发service的时候以后很熟悉了,这里就不多说了。
之后下面我们看到这里处理完成后,还会再次通知AMS,调用AMS的serviceDoneExecuting方法。
# service scheduleServiceArgs执行完毕通知AMS
```java
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
throw new IllegalArgumentException("Invalid service token");
}
mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
}
}
```
这里我们看到回调到AMS后,继续会调用serviceDoneExecutingLocked方法:
```java
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
boolean inDestroying = mDestroyingServices.contains(r); // destory中是否有这service
if (r != null) {
if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// This is a call from a service start... take care of
// book-keeping.
r.callStart = true;
switch (res) {
// service被干掉会自动重启,但是onStartCommand的参数intent是null
case Service.START_STICKY_COMPATIBILITY:
case Service.START_STICKY: {
// We are done with the associated start arguments.
// 把startId相同的从已分发deliveredStarts集合中移除
r.findDeliveredStart(startId, true);
// Don't stop if killed.
r.stopIfKilled = false;
break;
}
// service被杀,不会自动重启
case Service.START_NOT_STICKY: {
// We are done with the associated start arguments.
r.findDeliveredStart(startId, true);
if (r.getLastStartId() == startId) {
// There is no more work, and this service
// doesn't want to hang around if killed.
r.stopIfKilled = true;
}
break;
}
// 如果service被杀,会自动重启,并且回调onStartCommand的时候intent参数会保留
case Service.START_REDELIVER_INTENT: {
// We'll keep this item until they explicitly
// call stop for it, but keep track of the fact
// that it was delivered.
// 这个case虽然表示被杀后会重启service,但是不是依靠stopIfKilled这个标志的
// 这里开始findDeliveredStart方法第二个参数为false表示不从deliveredStarts集合中移除
// 后面重启的流程会从deliveredStarts中取出,在放入pendings集合来重启这个service
// 具体入口可以从AMS的handleAppDiedLocked方法看起
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
if (si != null) {
si.deliveryCount = 0;
si.doneExecutingCount++;
// Don't stop if killed.
r.stopIfKilled = true;
}
break;
}
case Service.START_TASK_REMOVED_COMPLETE: {
// Special processing for onTaskRemoved(). Don't
// impact normal onStartCommand() processing.
r.findDeliveredStart(startId, true);
break;
}
default:
throw new IllegalArgumentException(
"Unknown service start result: " + res);
}
// START_STICKY_COMPATIBILITY的话重启后不会调用onStartCommand方法
if (res == Service.START_STICKY_COMPATIBILITY) {
r.callStart = false;
}
} else if (type == ActivityThread.SERVICE_DONE_EXECUTING_STOP) {
// STOP Sservice会走到这里
// This is the final call from destroying the service... we should
// actually be getting rid of the service at this point. Do some
// validation of its state, and ensure it will be fully removed.
if (!inDestroying) {
// Not sure what else to do with this... if it is not actually in the
// destroying list, we don't need to make sure to remove it from it.
// If the app is null, then it was probably removed because the process died,
// otherwise wtf
if (r.app != null) {
Slog.w(TAG, "Service done with onDestroy, but not inDestroying: "
+ r + ", app=" + r.app);
}
} else if (r.executeNesting != 1) {
Slog.w(TAG, "Service done with onDestroy, but executeNesting="
+ r.executeNesting + ": " + r);
// Fake it to keep from ANR due to orphaned entry.
r.executeNesting = 1;
}
}
// 刚创建一个service后,获取第一次bind service(其实也就是创建)会直接到这里,上面type
// 的类型都不会匹配
final long origId = Binder.clearCallingIdentity();
// 拆掉之前创建的Handler的消息
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
Binder.restoreCallingIdentity(origId);
} else {
Slog.w(TAG, "Done executing unknown service from pid "
+ Binder.getCallingPid());
}
}
```
这个方法的整体逻辑还是比较清晰的。首先从mDestroyingServices集合看看这个service是否需要销毁,因为这个方法不止启动的时候会调用到,停止的时候也会调用,所以这里看看是不是停止的时候要求销毁。
这个方法的参数type和res都是从service进程那边传过来的。除了stop service外,大部分type的值是SERVICE_DONE_EXECUTING_START,这里启动也是这个值。
res的值和开发service的人设置的类型有关,这里几个不同类型的值代表意义如下:
START_STICKY:service被杀后会自动重启,但是重启后调用onStartCommand方法时候参数intent值为null
START_STICKY_COMPATIBILITY:和START_STICKY一样,但是重启后不会调用onStartCommand方法
START_NOT_STICKY: service被杀后不会自动重启
START_REDELIVER_INTENT:service被杀后会自动重启,并且会调用onStartCommand方法,调用时会保留原来的intent值
START_TASK_REMOVED_COMPLETE: 这个是在任务被移除时候调用的,一般我们不太用这个,知道下就好。
以上这几个case就是启动service时候会遇到的几种情况,其实从这几个case代表的意思来看,主要就是是否需要自动重启,所以如果不需要重启最后会调用serviceDoneExecutingLocked这个方法来结束这次调用的过程。如果需要重启,这个serviceRecord的stopIfKilled这个变量会被置为false,后面我们分析重启的时候会看到启动会根据这个变量再次启动这个service。值得注意的是stopIfKilled这个case,这个case也是重启的,但是他的重启不是依靠stopIfKilled这个变量的,从上面代码可以看到这个变量会被置为true,但是我们有注意到上面所有的情况都会调用findDeliveredStart这个方法,这个方法的第二个参数为true表示需要把这个启动的service从deliveredStarts集合中删除,所以当一个service启动完后,不管需不需要重启上面几个情况大部分这个参数都是true,除了START_REDELIVER_INTENT这个case,由于我们推测这个case的重启是不依赖于stopIfKilled这个变量的,而是会从deliveredStarts集合中取出启动项来重启。由于重启和后面的stop service有关联,只有stop后才会有重启,所以具体的重启我们在后面stop service的时候再仔细分析,这里我们大概知道了有这么个情况,后面讲到的时候不会太突兀。
好了,这个方法其实说了这么多,基本上他所做的事情都知道了,这么我们主要看下刚才提到的两个方法一个是findDeliveredStart,这个是把启动的这个service从deliveredStarts集合中移除,另一个是最终的结束工作会调用serviceDoneExecutingLocked方法。我们先看findDeliveredStart这个方法:
```java
public StartItem findDeliveredStart(int id, boolean remove) {
final int N = deliveredStarts.size();
for (int i=0; i<N; i++) {
StartItem si = deliveredStarts.get(i);
if (si.id == id) {
if (remove) deliveredStarts.remove(i);
return si;
}
}
return null;
}
```
这个方法很简单,遍历deliveredStarts这个集合,如果和启动项的id一样,并且传入remove参数是true,就从集合中移除他。这里id是startServiceLocked方法中创建启动项StartItem时候有serviceRecord生成的,生成的方法是serviceRecord的makeNextStartId方法:
```java
public int makeNextStartId() {
lastStartId++;
if (lastStartId < 1) {
lastStartId = 1;
}
return lastStartId;
}
```
这个比较简单,当时创建启动项StartItem没怎么说,所以这里提一下。好了,这个方法就这样了,我们看启动流程的最后一个方法serviceDoneExecutingLocked:
```java
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "<<< DONE EXECUTING " + r
+ ": nesting=" + r.executeNesting
+ ", inDestroying=" + inDestroying + ", app=" + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"<<< DONE EXECUTING " + r.shortName);
r.executeNesting--; //减1表示处理完一次请求(可能是正确的,也可能是报错的)
if (r.executeNesting <= 0) { // 如果没有正在执行的service了
if (r.app != null) { // 进程还存在
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"Nesting at 0 of " + r.shortName);
// 先把这个进程的是否还存在前台service的标志execServicesFg置为false
// 下面如果进程中没有执行的service了,那么自然还是false。
// 如果还有执行的service,会遍历executingServices判断是否有前台service,有的话会重新置true
r.app.execServicesFg = false;
r.app.executingServices.remove(r); // 从正在执行的集合中移除这个serviceRecord
if (r.app.executingServices.size() == 0) { // 没有正在执行的service了,移除hander超时
if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"No more executingServices of " + r.shortName);
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
} else if (r.executeFg) {
// 进入这个分支,说明这个进程还有正在执行的service,并且这个要结束的service是前台service
// 所以等他结束后,进程的execServicesFg是否还要保留true?那么就遍历executingServices集合
// 如果集合里面还有前台的service,那么还要置这个值为true
// Need to re-evaluate whether the app still needs to be in the foreground.
for (int i=r.app.executingServices.size()-1; i>=0; i--) {
if (r.app.executingServices.valueAt(i).executeFg) {
r.app.execServicesFg = true;
break;
}
}
}
if (inDestroying) { // 如果这个service是要销毁的
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"doneExecuting remove destroying " + r);
mDestroyingServices.remove(r); // 从mDestroyingServices集合中移除他
r.bindings.clear(); // 清除创建时候的数据
}
mAm.updateOomAdjLocked(r.app, true);
}
r.executeFg = false; //这个service执行要结束了,所以前台置为false
if (r.tracker != null) {
// 这个跟踪器把执行置为false
r.tracker.setExecuting(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
if (finishing) {
r.tracker.clearCurrentOwner(r, false);
r.tracker = null;
}
}
if (finishing) {
if (r.app != null && !r.app.persistent) {
// 如果进程非空,并且不是常驻的应用,从services集合中移除
r.app.services.remove(r);
// 由于whitelistManager为true,表示这个service所在的进程
// 可以临时绕过省电模式,现在他马上要结束了。看看剩下的service
// 中是否还有whitelistManager为true的
if (r.whitelistManager) {
updateWhitelistManagerLocked(r.app);
}
}
r.app = null;
}
}
}
```
这个方法作为整个流程的最后一个方法,主要做了一些清理的工作以及对于Service超时的处理。首先判断这个service的executeNesting减一后,是否小于等于0,是的话说明这个service已经没有都完成了,没有在执行的代码了,那么就会从进程的executingServices集合中把他移除。executingServices集合表示的是当前进程中当前所有正在执行的service,只有当为0的时候才去移除当初发送给hander的超时消息,因为这个超时的消息执行的方法每当到时间执行时候是会变量executingServices这个集合,如果还是service在里面执行的话,会自动添加上下一次的超时消息的,所以基本上我们只需要发送一次给Hander就可以了,后续会自动的更新,这就是前面我们分析bumpServiceExecutingLocked方法时候看到的,添加超时消息也是只在executingServices集合为1的时候添加一样的逻辑,这里只在executingServices为0时移除。我们具体看下SERVICE_TIMEOUT_MSG这个超时消息执行的方法:
```java
case SERVICE_TIMEOUT_MSG: {
if (mDidDexOpt) {
mDidDexOpt = false;
Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
nmsg.obj = msg.obj;
mHandler.sendMessageDelayed(nmsg, ActiveServices.SERVICE_TIMEOUT);
return;
}
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
```
这里我们看最后的serviceTimeout方法:
```java
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
// 当前时间
final long now = SystemClock.uptimeMillis();
// 计算目前超时的时间,now其实可以看做某个service的超时时间,减去前后台的值后就是这个service的开始时间
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
// 遍历每个service,取出超时的时间
for (int i=proc.executingServices.size()-1; i>=0; i--) {
ServiceRecord sr = proc.executingServices.valueAt(i);
// service的开始时间小于maxTime,maxTime根据上面的计算可以
// 看作是某个超时service的开始时间,如果比maxTime小了,说明
// 这个service超时了
if (sr.executingStart < maxTime) {
// 到这里超时了,timeout赋值
timeout = sr;
break;
}
// 到这里说明这个service目前没超时,保存下这个service超时的时间
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
// 超时了,并且这个进程还在运行中
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(timeout);
timeout.dump(pw, " ");
pw.close();
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
// 进这个分支,说明超时了
anrMessage = "executing service " + timeout.shortName;
} else { // 到这里说明目前没有超时,更新下一次超时
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
}
}
// ANR报错
if (anrMessage != null) {
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
```
这个方法就是处理service的ANR的方法了。在这个方法中maxTime这个变量表示这个触发超时的那个Service的启动时间是什么,把当前时间减去前台或者后台的超时时间就是当时启动service的时间。之后会遍历当前正在执行的service集合executingServices,如果小于maxTime说明这个service就超时了,我们看到后面会拼接字符串anrMessage,最后通过调用appNotResponding方法弹出ANR的弹窗。如果没有超时的话,会重新计算下一个超时的时间nextTime,然后再次给Handler发送消息,重复上面的过程直到所有service都处理完毕。
这里关于nextTime的计算我其实还是没有完全想通,照理说处理完一个超时消息后如果没有超时的话,会计算下一个离开当前最近的消息,但是从这段代码中计算的是所有正在执行的service中最晚启动的一个,那如果有比这个之前超时的不是就没有触发ANR了吗?这块还是不太理解,但是总体思路我们可以看出,除了有必要在开启一个service的时候发生超时消息外,其余的在每次触发超时消息但是没有超时service的时候,会自动更新下下一次的超时消息,这样就不用每个service都发送一个超时消息了,减小了系统的消耗,所以我们上面移除超时消息的时候看到是在executingServices集合为空的时候才去移除,因为没有执行的service了也就不需要更新超时消息了。
至此整个我们分析完了再一个service已经启动的情况下,调用startService的情况,相对来说service的流程还是比较清晰的,如果在理解了AMS的基础上再看service应该不难理解。最后我们在用时序图总结下整个startService流程。

这篇文章主要分析了在service已经启动的情况下,调用startService的整体流程。前面我们也说过,还有两种情况,一种是进程已经启动但是service还没启动的情况下,另一个是进程也没启动的情况下,这两种情况我们放在下一篇文章讲,这篇文章通过启动一个存在的service,我们其实把启动service的最主要流程都分析了,理解这些内容后后面的分析相信就比较轻松了,那么我们下篇文章继续分析。
Service源码分析(一)