看完startService的源码之后发现,只要我们的targetSDK设置成小于26的依然还是可以在8.0的手机上后台启动service的。来简单看下源码吧:
ContextImpl$startServiceCommon:
private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) { // ... ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( getContentResolver()), requireForeground, getOpPackageName(), user.getIdentifier()); if (cn != null) { // ... // 8.0其实就是这里加了个判断。 } else if (cn.getPackageName().equals("?")) { throw new IllegalStateException( "Not allowed to start service " + service + ": " + cn.getClassName()); } } return cn; }
ActivityManagerService$startService:
public ComponentName startService(IApplicationThread caller, Intent service, String resolvedType, boolean requireForeground, String callingPackage, int userId) throws TransactionTooLargeException { // ... ComponentName res = mServices.startServiceLocked(caller, service, resolvedType, callingPid, callingUid, requireForeground, callingPackage, userId); return res; } }
ActiveServices$startServiceLocked:
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()); // ... // retrieve是取回的意思 ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg, false); // ... ServiceRecord r = res.record; // If this isn't a direct-to-foreground start, check our ability to kick off an // 这个方法是不允许后台启动服务的关键,第一次启动一个 // service会进来,因为r是刚new出来的,r.startRequested一定是false,其他还有地方把r.startRequested置为false的状态,这个状态很重要。 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) { if (allowed == ActivityManager.APP_START_MODE_DELAYED) { // 如果是这种情况,不会启动服务而且什么提示都没有. return null; } UidRecord uidRec = mAm.mActiveUids.get(r.appInfo.uid); // 只有这个地方才会返回ComponentName("?") return new ComponentName("?", "app is in background uid " + uidRec); } } // .... 如果那里没被返回ComponentName("?"),这里会被置为true。 r.startRequested = true; r.fgRequired = fgRequired; r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, neededGrants, callingUid)); final ServiceMap smap = getServiceMapLocked(r.userId); boolean addToStarting = false; // ... ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); return cmp; }
ActiveServices$retrieveServiceLocked
private ServiceLookupResult retrieveServiceLocked(Intent service, String resolvedType, String callingPackage, int callingPid, int callingUid, int userId, boolean createIfNeeded = true, boolean callingFromFg = false, boolean isBindExternal = false) { ServiceRecord r = null; if (r == null) { // ... if (r == null && createIfNeeded) { final Intent.FilterComparison filter = new Intent.FilterComparison(service.cloneFilter()); final ServiceRestarter res = new ServiceRestarter(); // ... // 创建的时候并没有设置startRequested这个变量,所以默认是false的。 r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res); res.setService(r); smap.mServicesByName.put(name, r); smap.mServicesByIntent.put(filter, r); } } if (r != null) { // ... 这里省略的部分都是检验权限之类的 return new ServiceLookupResult(r, null); } return null; }
ActivityManagerService$getAppStartModeLocked:
int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk, int callingPid, boolean alwaysRestrict = false, boolean disabledOnly = false) { UidRecord uidRec = mActiveUids.get(uid); // 如果是在前台启动service,就不会进这里了,主要还是uidRec.idle这个值。 if (uidRec == null || alwaysRestrict || uidRec.idle) { // ... if (disabledOnly) { return ActivityManager.APP_START_MODE_NORMAL; } final int startMode = (alwaysRestrict) ? appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk) : appServicesRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk); 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; }
ActivityManagerService$appServicesRestrictedInBackgroundLocked:
这个方法主要是检查是否系统应用,白名单等
int appServicesRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) { // Persistent app? if (mPackageManagerInt.isPackagePersistent(packageName)) { return ActivityManager.APP_START_MODE_NORMAL; } // Non-persistent but background whitelisted? if (uidOnBackgroundWhitelist(uid)) { return ActivityManager.APP_START_MODE_NORMAL; } // Is this app on the battery whitelist? if (isOnDeviceIdleWhitelistLocked(uid)) { return ActivityManager.APP_START_MODE_NORMAL; } // None of the service-policy criteria apply, so we apply the common criteria return appRestrictedInBackgroundLocked(uid, packageName, packageTargetSdk); }
ActivityManagerService$appRestrictedInBackgroundLocked:这个方法是关键
// Unified app-op and target sdk check int appRestrictedInBackgroundLocked(int uid, String packageName, int packageTargetSdk) { // Apps that target O+ are always subject to background check // 最重要的就是这里了,如果targerSDK >=26,才会返回不允许状态。 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; } // ...and legacy apps get an AppOp check int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName); switch (appop) { case AppOpsManager.MODE_ALLOWED: return ActivityManager.APP_START_MODE_NORMAL; case AppOpsManager.MODE_IGNORED: return ActivityManager.APP_START_MODE_DELAYED; default: return ActivityManager.APP_START_MODE_DELAYED_RIGID; } }
AppOpsService$noteOperation:
@Override public int noteOperation(int code, int uid, String packageName) { // ... return noteOperationUnchecked(code, uid, resolvedPackageName, 0, null); }
AppOpsService$noteOperationUnchecked
private int noteOperationUnchecked(int code, int uid, String packageName, int proxyUid, String proxyPackageName) { synchronized (this) { Ops ops = getOpsRawLocked(uid, packageName, true); Op op = getOpLocked(ops, code, true); // private Op getOpLocked(Ops ops, int code, boolean edit = false) { // Op op = ops.get(code); // if (op == null) { // ops是刚new出来的,肯定也没有code的值 // if (!edit) return null; // op = new Op(ops.uidState.uid, ops.packageName, code); // ops.put(code, op); // } // return op; // } // 这里其实是看是不是AppOpsManager一对一的那个权限列表,因为Op分为运行时权限和其他的一些 switchCode = code final int switchCode = AppOpsManager.opToSwitch(code); UidState uidState = ops.uidState; // ... final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op; // switchOp.mode是default mode,而OP_RUN_IN_BACKGROUND的default mode是allowed的 // 所以这里就返回了allowd的结果 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) { if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " + switchCode + " (" + code + ") uid " + uid + " package " + packageName); op.rejectTime = System.currentTimeMillis(); return switchOp.mode; // ... }
其他地方把ServiceRecord的startRequested置为false的情况:
①调用了stopService;
②service调用了stopSelf();
③系统杀掉了service。
总结就是service死掉之后这个状态就会被置为false.
一开始有这样的想法,既然成功启动过后startRequested会被置为true,那我们先在程序一打开的时候先去startService,后面在后台去调用的时候,不就不会进入那个分支了吗?但是行不通,因为系统会去杀掉后台的serivce.
8.0还有一个比较狠的是,启动了一个service,app退到后台后很快service会在短时间内(一分钟左右)就被系统干掉。原因是这样的,ActivityManagerService有个方法叫updateOomAdjLocked,用来计算进程adj值的,这个方法会被频繁调用,调用的时候还会发送一个调用idleUids 这个方法的广播,idleUids里面会去判断进程在后台的时间,接着会调用ActiveServices.stopInBackgroundLocked(uid);这个方法,把后台服务给杀掉,这个机制在8.0以前就有,但在8.0的时候是否要杀掉的逻辑变更了:
8.0以前是这样的:
这个很少进来的,一般是用户手动去设置里面不让这个进程在后台启动服务,就会进去那个分支。
而在8.0的时候就变成:
这个getAppStartModeLocked上面我们分析过了,只要targetSdk还是小于26那个分支就不会进去。一旦service进了stopping这个集合里,等会就会马上把它干掉: