基于Android10.0,分析startService的启动过程
一、概述
前面已经介绍了详细介绍了管理Android四大剑客Activity、Service、Broadcast、ContentProvider的ActivityManagerService启动的详细流程,这里讲从应用startService的启动过程来分析AMS。
ActivityManagerService相关的类图如下:
启动服务通过startServie或者bindService即可,该过程如下:
当应用调用Andorid API方法startServie或者bindService来启动服务的过程,主要是AMS来完成的。
1.AMS通过socket通信方式向zygote进程请求创建用于承载服务进程的ActivityThread。如果启动服务运行在本地服务则不需要再次创建进程。
2.zygote通过fork的方法,将zygote进程复制升级新的进程,并将ActivityThread相关的资源加载到新进程。
3.AMS向新生成的ActivityThread进程,通过Binder方式发送创建服务的请求
4.ActivityThread启动本地运行服务。
启动服务的流程如下:
二、启动服务进程端
在app中调用startService,调用的是ContextWrapper中的startService
1. CW.startService
[->ContextWrapper.java]
1 | @Override |
2. Cl.startService
[->ContextImpl.java]
1 | @Override |
3. CI.startServiceCommon
[->ContextImpl.java]
1 | private ComponentName startServiceCommon(Intent service, boolean requireForeground, |
4. AM.getService
这个方法和Android6.0不一样,没有了ActivityManagerNative和ActivityManagerProxy,直接通过IActivityManager.aidl生成的接口获得ActivityManagerService的代理。startService通过Binder机制,调用了服务器端AMS的startService方法。
1 | /** |
三、SystemServer端
5. AMS.startService
[->ActivityManagerService.java]
1 | @Override |
startServiceLocked方法参数说明:
caller:IApplicationThread类型
service:Intent类型,包含运行的Service信息
resolvedType:String类型
callingPid:调用者pid
callingUid:调用者uid
requireForeground:是否需要前台运行,前面传的是false
callingPackage:调用该方法的包名
userId:用户id
6. AS.startServiceLocked
[->ActiveServices.java]
1 | ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, |
可以看到,Android10.0对于后台服务的启动,要求更加的严格。如果allowed不等于APP_START_MODE_NORMAL,则后台服务将不允许被启动。
callerFg对用于标记前台还是后台,当发起方进程不等于SCHED_GROUP_BACKGROUND或者发起方为空,则callerFg= true,否则为false。
6.1 AMS.getAppStartModeLocked
[->ActivityManagerService.java]
1 | int getAppStartModeLocked(int uid, String packageName, int packageTargetSdk, |
getAppStartModeLocked方法获取是否允许是否后台启动,一般callerFg为false,从而会进行服务再一次判断,普通的服务会进入appServicesRestrictedInBackgroundLocked方法进行判断如下:
6.2 AMS.appServicesRestrictedInBackgroundLocked
[->ActivityManagerService.java]
1 | // Service launch is available to apps with run-in-background exemptions but |
可以看到对于persistent app、uid后台服务白名单、电池白名单里面都是可以启动后台服务的。
7. AS.startServiceInnerLocked
[->ActiveServices.java]
1 | ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, |
8.AS.bringUpServiceLocked
[->ActiveServices.java]
1 | private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, |
当目标进程已经存在,则直接执行realStartServiceLocked
当目标进程不存在,则先执行startProcessLocked创建进程,经过层层调用最后会调用到AMS.attachApplicationLocked,然后再执行realStartServiceLocked。
对于非前台进程调用而需要启动的服务,如果已经有其他的后台服务正在启动,则可能希望延迟其启动,从而避免同时启动过多的进程(非必须)。
8.1 AMS.attachApplicationLocked
[->ActivityManagerService.java]
1 | @GuardedBy("this") |
8.2 AS.attachApplicationLocked
[->ActiveServices.java]
1 | boolean attachApplicationLocked(ProcessRecord proc, String processName) |
9.AS.realStartServiceLocked
[->ActiveServices.java]
1 | private final void realStartServiceLocked(ServiceRecord r, |
bumpServiceExecutingLocked会发送一个延迟处理的消息SERVICE_TIMEOUT_MSG。在方法scheduleCreateService执行完成,如果onCreate回调执行完成后,便会remove掉该消息。但如果没有在延时的时间内移除掉消息,则会进入到service timeout流程
9.1 AS.bumpServiceExecutingLocked
[->ActiveServices.java]
1 | private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) { |
9.2 AS.scheduleServiceTimeoutLocked
[->ActiveServices.java]
1 | void scheduleServiceTimeoutLocked(ProcessRecord proc) { |
发送延迟消息SERVICE_TIMEOUT_MSG:
对于前台服务,则超时为SERVICE_TIMEOUT=20s
对于后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT=SERVICE_TIMEOUT * 10
app.thread.scheduleCreateService通过Binder机制调用,IApplicationThread.aidl在编译时会生成代理类和实现类。通过IApplicationThread代理,调用到目标进程端的scheduleCreateService具体实现。
四、目标进程端
10.AT.scheduleCreateService
[->ActivityThread.java]
1 | public final void scheduleCreateService(IBinder token, |
该方法执行在ActivityThread线程。
10.1 AT.handleMessage
[->ActivityThread.java]
1 | public void handleMessage(Message msg) { |
11.AT.handleCreateService
[->ActivityThread.java]
1 | @UnsupportedAppUsage |
11.1 Service.onCreate
[->Service.java]
1 | public abstract class Service extends ContextWrapper implements ComponentCallbacks2 { |
最终调用到了Service.onCreate方法,对于目标服务一般都是继承Service,并且覆写onCreate方法,到此终于进入到了Service的生命周期。
12.AMS.serviceDoneExecuting
[->ActivityManagerService.java]
1 | public void serviceDoneExecuting(IBinder token, int type, int startId, int res) { |
由流程9.1 bumpServiceExecutingLocked方法发送了一个延时消息SERVICE_TIMEOUT_MSG,现在onCreate执行完成,那么就需要移除该消息,否则会报超时。
12.1 AS.serviceDoneExecutingLocked
[->ActiveServices.java]
1 | void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) { |
12.2 AS.serviceDoneExecutingLocked
[->ActiveServices.java]
1 | private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, |
该方法将会移除SERVICE_TIMEOUT_MSG消息。Service启动过程出现ANR,会发送超时serviceRecord消息,这通常是onCreate的回调方法过长导致。
realStartServiceLocked方法,在完成onCreate操作时,后面进入到了onStartCoomnad方法.
13.AS.sendServiceArgsLocked
[->ActiveServices.java]
1 | private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg, |
realStartServiceLocked先后执行的方法如下:
执行scheduleCreateService方法,通过层层回调到Service.onCreate;
执行scheduleServiceArgs方法,通过层层回调到Service.onStartCommand,流程同onCreate.
五、总结
整个startService过程,从进程角度来看服务启动的过程
process A进程:是指调用startService方法所在的进程,也就是启动服务的发起端进程,如:桌面上点击图标,此处Process A就是Launcher所在的进程。
system_server进程:里面运行着大量的系统服务,如AMS,是运行在system_server不同的线程中,基于Binder接口,binder线程的创建与销毁都是由Binder驱动来决定的,每个进程的binder线程个数上线为16
Zygote进程:是由init进程孵化而来,用于创建java层进程的母体,所有的java层进程都是由Zygote进程孵化而来。
Remote Service进程:远程服务所在的进程,是由zygote进程孵化而来,用于运行Remote服务进程。主线程主要是负责Activity/Service等组件的生命周期以及UI相关的操作;另外,每个app进程至少会有两个binder线程,ApplicationThread和ActivityManagerService的代理。
上面涉及到IPC通信的三种方式,Binder、Socket、Handler。一般来说,同一进程内的线程间通信采用Handler消息队列机制,不同进程间的通信采用Binder机制,另外与Zygote通信采用Socket。
启动流程:
1.Process A进程采用Binder IPC向system_server进程发起startService请求
2.system_server进程接收到请求后,向zygote进程发送创建进程的请求
3.zygote进程fork出新的子进程Remote Service进程
4.RemoteService进程通过Binder IPC向system_server进程发起attachApplication请求
5.system_server进程收到请求后,进行一序列准备工作后,再通过Binder IPC向remote sevice进程发送scheduleCreateService请求
6.Remote Service进程的binder线程收到请求后,通过handler向主线程发送CREATE_SERVICE消息
7.主线程收到消息后,通过反射机制创建目标Service,并回调Service.onCreate方法。
到此,服务就正式启动了。当创建的是本地服务或者服务所属进程已经创建,则无需经过步骤2,3直接创建服务即可。
附录
源码路径
1 | frameworks/base/core/java/android/app/IActivityManager.aidl |