Android四大组件系列5 Service启动流程

一 概述

Service 的启动流程还是比较简单的,不像 Activity 那么复杂,相关联的类和方法也少了很多,不像 Activity 启动流程里面走着走着就会迷失方向,在之前两篇文章我们主要分析了 Activity 的启动流程,今天我们主要来探索 Service 的启动过程。

Service 是 Android 四大组件,地位仅次于 Activity,Activity 位于前台向用户展示界面;Service 位于后台不可见,但是它承担了大部分的的数据处理工作,主要为其他组件提供后台服务,监控其他组件的运行状态。下面我们来具体分析。

分析的系统源码为 android 12 。

涉及代码如下:

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
frameworks/base/services/core/java/com/android/server/am/ProcessRecord.java
frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java

frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/core/java/android/app/Service.java

二 APP发起启动请求

2.1 时序图

在这里插入图片描述

2.2 ContextImpl.startService

frameworks/base/core/java/android/app/ContextImpl.java

@Override
public ComponentName startService(Intent service) {
   
   
    warnIfCallingFromSystemProcess();// 系统进程启动 service 的话,打印 log
    // 第二个参数为 requireForeground,默认为 false 表示启动非前台服务,
    // 区别于 startForegroundService
    return startServiceCommon(service, false, mUser);
}

@Override
public ComponentName startForegroundService(Intent service) {
   
   
     warnIfCallingFromSystemProcess();
     return startServiceCommon(service, true, mUser);
}
 
private ComponentName startServiceCommon(Intent service,
        boolean requireForeground, UserHandle user) {
   
   
    validateServiceIntent(service);// 对 Intent 的属性进行检查和准备
    service.prepareToLeaveProcess(this);
    // Binder 跨进程调用到 AMS
    ComponentName cn = ActivityManager.getService().startService(
        mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                    getContentResolver()), requireForeground,
                    getOpPackageName(), user.getIdentifier());
    if (cn != null) {
   
   
          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("?")) {
   
   
               throw new IllegalStateException(
                   "Not allowed to start service " + service + ": " +
                    cn.getClassName());
          }
    }
    return cn;
}

需要注意的是 startService 和 startForegroundService 差别是入参 requireForeground 的值,其他代码逻辑完全一样。

默认情况下目标 Service 会以后台服务的方式进行启动。与前台服务相比,后台服务不会在下拉通知栏显示通知,同时优先级较低,当系统出现内存不足情况时容易被回收。

validateServiceIntent() 对参数 Intent 进行合法性检查,确保其中与 Service 启动的相关属性值不为 null。此外,Android 5.0 后强制要求 Service 必须通过显式 Intent 启动,否则会直接抛出异常

当由于权限、安全性等问题导致 Service 无法正常启动时,返回值 cn 的成员变量 mPackage 会被设置为 “!”、“?” 等特殊值,此时应用进程会进行处理并抛出对应的异常

三 AMS处理启动请求

3.1 ActivityManagerService.startService

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

@Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, boolean requireForeground, String callingPackage,
                int userId) throws TransactionTooLargeException {
   
   
    enforceNotIsolatedCaller("startService");
    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;
    }
}

startService() 获取客户端进程的 callingPid 与 callingUid,然后将它们连同其它参数传递给 ActiveServices 对象的 startServiceLocked() 进行处理。

ActivityManagerService 把 Service 的所有事务都交给 ActiveServices 处理。

3.2 ActiveServices.startServiceLocked

frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

ComponentName startServiceLocked(IApplicationThread caller, Intent service,
		String resolvedType, int callingPid, int callingUid, boolean fgRequired, 
		String callingPackage, final int userId)
        throws TransactionTooLargeException {
   
   
	// 倒数第二个参数是 allowBackgroundActivityStarts,默认
	// 为 false, 表示不允许后台 Activity 启动 Service
	return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, 
	fgRequired, callingPackage, callingFeatureId, userId, false, null);
}

继续调用 startServiceLocked,倒数第二个参数 allowBackgroundActivityStarts,默认为 false,表示不允许后台 Activity 启动 Service。

继续调用:

 ComponentName startServiceLocked(IApplicationThread caller, Intent service, 
            String resolvedType, int callingPid, int callingUid, boolean fgRequired,
            String callingPackage, @Nullable String callingFeatureId, final int userId,
            boolean allowBackgroundActivityStarts, 
            @Nullable IBinder backgroundActivityStartsToken)
            throws TransactionTooLargeException {
   
   

    final boolean callerFg;
    if (caller != null) {
   
   
        // 从 mLruProcesses 列表中找出指定进程
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
   // 关键点1:callerFg 用于标记发起端进程是前台还是后台,
   // 当发起方进程不等于SCHED_GROUP_BACKGROUND
   // 或者发起方为空,则 callerFg= true,否则为 false
        callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
    } else {
   
   
        callerFg = true;
    } 
    // 关键点2:从 ServiceMap 中根据 ComponentName 查找 ServiceRecord,不存在的话就创建
    // 并封装成 ServiceLookupResult, 见 # 3.2.1
    ServiceLookupResult res = retrieveServiceLocked(service, null, resolvedType,
		    callingPackage, callingPid, callingUid, userId, true, callerFg, 
		    false, false); 
    // 获取 ServiceRecord,并构建
    ServiceRecord r = res.record; 
    ......
    // 是否后台启动, Android 8.0+ 有后台启动限制
    final boolean bgLaunch = !mAm.isUidActiveLOSP(r.appInfo.uid);
    boolean forcedStandby = false;
    // 如果后台启动 Service,且 app 有后台启动限制,则 forcedStandby 设置为 true
    if (bgLaunch && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
   
   
        forcedStandby = true;
    }
    // 如果是 startForegroundService 启动前台服务,检查前台启动操作权限是否允许
    if (fgRequired) {
   
   
        final int mode = mAm.mAppOpsService.checkOperation(
                AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
        switch (mode) {
   
   
            case AppOpsManager.MODE_ALLOWED:
            case AppOpsManager.MODE_DEFAULT:
                // All okay.
                break;
            case AppOpsManager.MODE_IGNORED:
                // Not allowed, fall back to normal start service
                // failing siliently. if background check restricts that.
                fgRequired = false;
                forceSilentAbort = true;
                break;
        }
    }
    // 如果是后台启动或者启动非前台服务,检查是否允许后台启动,如果不能的话返回 null
    if (forcedStandby || (!r.startRequested && !fgRequired)) {
   
   
    final int allowed = mAm.getAppStartModeLocked(r.appInfo.uid, r.packageName,
         r.appInfo.targetSdkVersion, callingPid, false, false, forcedStandby);
        if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
   
   
            if (allowed == ActivityManager.APP_START_MODE_DELAYED ||
                forceSilentAbort) {
   
   
                // In this case we are silently disabling the app, to disrupt as
                // little as possible existing apps.
                return null;
            }
            if (forcedStandby) {
   
   
                if (fgRequired) {
   
   
                    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.mProcessList.getUidRecordLocked(r.appInfo.uid);
            return new ComponentName("?", "app is in background uid " + uidRec);
        }
    }
// Android 8.0 之前启动前台服务不需要调用 startForegroundService(), 即 fgRequired 为 false
    if (r.appInfo.targetSdkVersion < Build.VERSION_CODES.O && fgRequired) {
   
   
        fgRequired = false;
    } 
NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
     callingUid, r.packageName, service, service.getFlags(), null, r.userId); 
    // 权限检查 permission review
    if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
            callingUid, service, callerFg, userId)) {
   
   
        return null;
    } 
    ......
    // 设置 ServiceRecord 启动参数, pendingStarts 中添加启动项 StartItem
    r.lastActivity = SystemClock.uptimeMillis();
    // startRequested 是 ServiceRecord 类的一个 boolean 类型的成员变量,该值为 true 表示 Service
    // 是通过 startService 方式(而不是 bindService 方式)启动的
    r.startRequested = true;
    r.delayedStop = false;
    r.fgRequired = fgRequired;
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
            service, neededGrants, callingUid));
 
    if (fgRequired) {
   
   
        // 如果是 Android 8.0+ 调用的 startForegroundService() 启动前台服务的话,
        // 更新 ServiceState
        ServiceState stracker = r.getTracker();
        if (stracker != null) {
   
   
            stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(),
                    r.lastActivity);
        }
     mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
           AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, true);
    }
    // 获取 ServiceMap
    final ServiceMap smap = getServiceMapLocked(r.userId);
    boolean addToStarting = false;//重点变量
    // 如果“ caller app 不是前台应用”、“目标 Service 不是前台服务”
    // “目标 Service 处于未运行状态”
    // “发起请求的用户已经启动”,以上条件均满足则进入此分支,判断目标 Service 是否需要
    // 延迟启动,否则,将调用 startServiceInnerLocked() 立即启动目标 Service
    if (!callerFg && !fgRequired && r.app == null &&
            mAm.mUserController.hasStartedUserState(r.userId)) {
   
   
        // 从进程列表中获取启动 Service 所在的进程
        ProcessRecord proc = mAm.getProcessRecordLocked(r.processName,
                r.appInfo.uid, false);
        // 当目标 Service 所属的进程尚未创建,或者已创建但优先级较低时
        //(即 proc.curProcState 的值
        // 大于常量10,注意它的取值越小反而表示进程优先级越高
        if (proc == null ||
                proc.getCurProcState() > ActivityManager.PROCESS_STATE_RECEIVER) {
   
   
            // 如果 delayed 为 true,延迟启动
            if (r.delayed) {
   
   
                return r.name;
            }
            // 如果后台启动服务数超过 mMaxStartingBackground,则延迟启动
            if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
   
   
                // Something else is starting, delay!
                smap.mDelayedStartList.add(r);
                r.delayed = true;
                return r.name;
            }
            // addToStarting 设置为 true
            addToStarting = true;
        } else if (proc.getCurProcState<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值