深入分析Android 9.0源代码——Service启动流程(startService方式)

本文深入剖析Android 9.0中Service的启动流程,从应用进程发起请求到AMS处理,再到创建Service所属进程,最后在应用进程中启动Service。详细梳理了startService方法在ContextWrapper、ContextImpl中的调用,以及AMS如何处理请求,涉及进程间通信、权限检查、ServiceRecord等关键步骤。最后,阐述了Service在新进程中启动的完整过程。
摘要由CSDN通过智能技术生成

引言

点击此处查看《深入分析Android 9.0源代码》系列的组织结构和相关说明。


1 应用进程发起启动请求

本章的调用流程如下图所示:

(Context) ContextWrapper ContextImpl [1] startService() [2] startService() [3] startServiceCommon() (Context) ContextWrapper ContextImpl

注:
(1)由于编辑器渲染的时序图不支持“无触发对象消息”(在标准的时序图中,这种消息的起点为实心圆形,表示流程的开始),此处用“(Context)”虚指触发流程的起始对象(而不是Android中的Context类)。
(2)对于时序图中形如“A—[#num] method()—>B”的消息(连线为实线),它的含义是控制流由先前的对象A(直接)转移到对象B,然后调用对象B的编号为#num的method()方法。


1.1 startService@ContextWrapper

Android系统为开发者提供了startService和bindService两种启动Service的方式,其中前者对应于Context类的startService()。由于Context类本身是一个抽象类,所以该方法实际上是在它的子类ContextWrapper中实现的。

// #1 {root}/core/java/ android.content.ContextWrapper (L663)
public ComponentName startService(Intent service) {
   
    return mBase.startService(service); // 调用#2
}

【L-03】return mBase.startService(service);
mBase是一个Context类型的成员变量,它的实际类型是ContextImpl。由此可见,Context抽象类提供了ContextWrapper和ContextImpl两个直接子类。其中ContextImpl是Context的功能实现类,它为Service等组件提供context这个重要概念的基础实现。但是,Android系统并不允许开发者直接使用ContextImpl类1,而是额外引入了一个ContextWrapper包装类(Activity、Service和Application类均直接或间接继承自该类),它负责将应用程序中与Context相关的方法调用转发给内部持有的ContextImpl引用。


1.2 startService@ContextImpl

startService()对系统进程的Service启动请求打印额外的调试信息2,然后调用startServiceCommon()处理启动请求。

// #2 {root}/core/java/ android.app.ContextImpl (L1530)
public ComponentName startService(Intent service) {
   
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, false, mUser); // 调用#3
}

【L-04】return startServiceCommon(…, false, …);
此处调用startServiceCommon()传入的第二个参数为false,表明默认情况下目标Service会以后台服务的方式进行启动。与前台服务相比,后台服务不会在下拉通知栏显示通知,同时优先级较低,当系统出现内存不足情况时容易被回收3


1.3 startServiceCommon@ContextImpl

startServiceCommon()首先对Intent的属性进行检查和准备,然后然后将Service的启动请求通过IPC(进程间通信)发送给系统服务ActivityManagerService(下文简称为AMS,它所属的system_server系统进程简称AMS进程)进行处理。由于AIDL实现的IPC默认是同步的,所以应用进程的当前线程将会挂起,直到AMS进程的startService()返回。需要特别注意的是,虽然该方法本身是同步调用,但是后续AMS处理Service的启动过程是异步的,也即AMS进程的startService()并不会一直阻塞直到Service启动完毕4。事实上,当应用进程调用的startServiceCommon()返回时,其返回值仅包含需要启动的Service的相关信息(如果该Service存在),此时AMS并不保证目标Service已经创建成功(如果需要)并且执行了onStartCommand()回调方法。

// #3 {root}/core/java/ android.app.ContextImpl (L1557)
private ComponentName startServiceCommon(Intent service, boolean requireForeground, 
        UserHandle user) {
   
    ...
    validateServiceIntent(service);
    service.prepareToLeaveProcess(this);
    ComponentName cn = ActivityManager.getService().startService(mMainThread.getApplicationThread(), 
            service, service.resolveTypeIfNeeded(getContentResolver()), requireForeground, 
            getOpPackageName(), user.getIdentifier()); // IPC调用#4
    if (cn != null) {
    ... }
    return cn;
}

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

【L-06】service.prepareToLeaveProcess(this);
prepareToLeaveProcess()对Intent的属性进行离开应用进程前的准备工作。在StrictMode下,该方法还会进行跨进程安全性的检查,此时不允许使用scheme为“file://”的URI进行数据传输,而必须使用FileProvider作为替代6

【L-07】ComponentName cn = ActivityManager.getService().startService(…);
ActivityManager.getService()获取AMS的Binder代理对象,然后通过AIDL调用AMS的startService()。对AMS和IPC更详细的介绍参见本系列的《Activity启动流程》的1.3节。该方法的返回类型为ComponentName,它内部有两个String类型的成员变量mPackagemClass,分别表示组件(此处为Service)对应的应用包名和组件类名7

【L-10】if (cn != null) { … }
当由于权限、安全性等问题导致Service无法正常启动时,返回值cn的成员变量mPackage会被设置为"!"、“?”等特殊值,此时应用进程会进行处理并抛出对应的异常。


2 AMS处理启动请求

本章的调用流程如下图所示:

ComtextImpl [AMS] ActiveServices [4] startService() [5] startServiceLocked() [6] startServiceInnerLocked [7] bringUpServiceLocked ComtextImpl [AMS] ActiveServices

注:由于编辑器限制对象文本的最大长度,时序图中对于名称较长的类使用方括号(“[]”)和缩写来表示。上图中[AMS]=ActivityManagerService。


2.1 startService@ActivityManagerService

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

// #4 {root}/services/core/java com.android.server.am.ActivityManagerService (L20342)
public ComponentName startService(IApplicationThread caller, Intent service, 
        String resolvedType, boolean requireForeground, String callingPackage, 
        int userId) throws TransactionTooLargeException {
   
    ...
    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); // 调用#5
        }
        finally {
   
            Binder.restoreCallingIdentity(origId);
        }
        return res;
    }
}

【L-09】final long origId = Binder.clearCallingIdentity();
Binder.clearCallingIdentity()将远程调用端的PID和UID的初始值暂存于返回值origId中,然后将其替换为当前进程的PID和UID8。因为包括AMS在内的众多系统服务都运行在同一个system_server进程中,所以当AMS通过AIDL接口调用其它系统服务时,实质上只是一个同进程的本地方法调用。在这种情况下,Binder驱动不再(像跨进程调用一样)提供调用方进程的PID和UID,故此处需要显式地设置当前进程的PID和UID9

【L-12】res = mServices.startServiceLocked(…);
mServices是一个ActiveServices类型的成员变量,它的主要职责是辅助AMS管理应用服务10(与系统服务相对11)。

【L-16】Binder.restoreCallingIdentity(origId);
Binder.restoreCallingIdentity()利用origId恢复远程调用端的UID和PID的原始值,通常它与先前提到的Binder.clearCallingIdentity()总是成对出现12


2.2 startServiceLocked@ActiveServices

startServiceInnerLocked()首先尝试获取目标Service对应的SerivceRecord缓存对象(如果不存在则创建一个新的SerivceRecord),然后检查启动权限的合法性13,最后判断Service能够立即启动还是需要延迟启动。

// #5 {root}/services/core/java com.android.server.am.ActiveServices (L389)
ComponentName startServiceLocked(IApplicationThread caller, Intent service, 
        String resolvedType, int callingPid, int callingUid, boolean fgRequired, 
        String callingPackage, final int userId) throws TransactionTooLargeException {
   
    ...
    ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, callingPackage, 
            callingPid, callingUid, userId, true, callerFg, false, false);
    ...
    ServiceRecord r = res.record;
    ...
    r.startRequested = true;
    ...
    r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
            service, neededGrants, callingUid));
    .
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值