Android startService启动流程详解
概述
基于Android 10.0源码(AOSP-10.0.0-r1)分析startService
流程,剖析相关流程的关键点和framework核心机制。
过程
service的启动过程主要分为三大部分,包括发出startService
调用的机制、system_server
中对service的处理、应用进程中对service的创建流程和生命周期回调。
Context.startService
Context
是Abstract类,它是由ContextImpl
实现的,并通常以装饰器模式被ContextWrapper
包装。App中Context.startService()
调用实质上是由ContextWrapper.mBase
即ContextImpl.startService()
实现的。ContextImpl.startService()
首先会调用warnIfCallingFromSystemProcess()
来检查调用者是否为系统进程。日志中的警告信息就是这里打印店额。ContextImpl.startService()
关键实现是ContextImpl.startServiceCommon
,它在完成对Intent的检查后(高版本SDK检查Intent是否是不被允许的隐式intent),调用ActivityManager.getService().startService()
通过Binder向AMS申请启动服务,并对该调用的结果做检查,其中startService
相关的permission异常就是在这里抛出。ActivityManager.getService()
获取了IActivityManager.Stub
的Binder Proxy实例,它的Server端实现就是ActivityManagerService
。- 通过Binder调用的
startService()
方法中有几个关键参数。IApplicationThread
是一个Binder,服务端为ActivityThread
,Proxy通过startService()
传递到system_server
,方便AMS
处理完成后通过该Binder回调到应用进程。另外两个重要参数是启动服务的Intent和确定是否为前台服务的requireForeground
。
SystemServer
ActivityManagerService.startService
AMS.startService()
- 经过Binder调用,
startService
会处理App的请求。该方法的第一步就是检查用户有没有启动服务的权限(检查调用者是否是isolated uid,不是常规的Android Manifest内的权限) - 调用
mServices.startServiceLocked()
,其中mServices
是ActiveServices
。启动服务的所有逻辑都是在这个方法实现的。
- 经过Binder调用,
ActiveServices
ActiveServices.startServiceLocked()
- 首先通过
retrieveServiceLocked()
检索服务信息(ServiceLookupResult
)。ServiceLookupResult
是ActiveiServices
的内部类,它仅包含ServiceRecord
和表示权限的String两个成员。 retrieveServiceLocked
的作用是返回service对应的ServiceRecord
。会从ServiceMap
中查找是否已有该ServiceRecord
(service已经启动的情况),否则就通过PackageManager
(mAm.getPackageManagerInternalLocked().resolveService
)查找已安装的应用是否有匹配该service intent的服务(ServiceInfo
)。如果ServiceMap
和PackageManager
都不能提供对应的ServiceRecrod
或ServiceInfo
,那么就是Service not found
的情况,无法启动服务。- 在
ServiceMap
不包含该ServiceRecord
时,ActiveServices
会从PackageManager
获取已安装应用中解析出来的ServiceInfo
,并根据此来生成返回给调用者的ComponentName
。这里可以看出,ServiceMap
是一个运行时缓冲状态表,维护一个已有的Record,减少访问PMS。但是实际上从PMS解析出来的ServiceInfo
对应的ServiceRecord
不一定会加入到这个Map中,对应的package必须相互允许集成对方包的情况下才会加入(mAm.validateAssociationAllowedLocked()
) validateAssociationAllowedLocked()
返回true时,在BIND_EXTERNAL_SERVICE
的情况下,ActiveServices
不仅会将创建的ComponentName
(这个对象返回给AMS时,AMS用这个来启动service,返回给App时,App根据返回值直到自己的启动情况和结果)修改为自身的包名(即bind的external service的包名改为发起startService
的包名,还会将内部使用的ServiceInfo
和方法外部入参Intent
都做对应的修改。- 最后,从PMS成功解析后,创建
ServiceRecord
并放入ServiceMap
。到这里找到了目标service,创建了ServiceRecord
,实际上retrieveServiceLocked()
的关键流程就已经完成了,但是还有一些额外的功能在最后可能影响服务的启动。 - 在最后时刻,
ActiveServices
会最后确定解析的ServiceLookupResult
是否真的应该返回。最后有三道检测关卡,包括IntentFirewall
、Permission
机制和AppOps
机制。其中通过mAm.mIntentFirewall.checkService()
调用IntentFirewall
来检查是否应该"blocked by firewall"
。IntentFirewall
的机制是,它读取/data/system/ifw
内的xml配置文件创建Firewall rules,当Intent被rules匹配时阻止Intent启动四大组件。另外两个Permission
和AppOps
是Android常规的权限机制。 - 最后,
retrieveServiceLocked()
检查一下权限后返回包装了ServiceRecord
的ServiceLookupResult
到ActiveServices.startServiceLocked()
。
- 首先通过
fgRequired
- 返回的
ServiceLookupResult
内的ServiceRecord
有效时,当调用的是startForegroundService
时,会通过AppOps
机制检查是否允许启动前台service,否则会退出,并从Binder返回foreground not allowed
。 - 接下来处理一下延时启动相关的东西(后台进程申请启动的后台服务可能会被延迟启动)
- 返回的
startServiceInnerLocked
- 将上面的
ServiceMap
、Intent
、ServiceRecord
等作为参数调用startServiceInnerLocked()
。它首先将ServiceRecord.ServiceState
标记为STARTED、启动StatsLog
统计使用信息。 - 然后调用
bringUpServiceLocked()
方法启动服务。该方法开头检查ServiceRecord
的ProcessRecord
和IApplicationThread
是否存在,如果存在则表明该Service
在之前就已经启动了(被其他startService
调用创建过,onCreate()
流程已经调用过了)。这种情况下不需要再次创建服务,只需要回调服务的onStartCommand()
即可——调用sendServiceArgsLocked()
并提前return。它通过IApplicationThread
这个Binder回调到应用进程,调用对应service的onStartCommand()
。具体流程是,该方法内部调用了r.app.thread.scheduleServiceArgs()
,这个方法定义于IApplicationThread.aidl
,system_server的AMS调用这个Binder的Proxy,对应server即App内的scheduleServiceArgs()
方法。与启动Activity一样,这个方法向ActivityThread
的Handler发送了一个Message,Handler内回调onServiceArgs()
,最终调用了service的onStartCommand()
。 bringUpServiceLocked()
在service没有创建的情况下往下运行。有一个细节,首先它通过AppGlobals.getPackageManager().setPackageStoppedState()
的调用,短暂地阻止ServiceRecord
对应的package不能被stop,然后再执行启动service的流程。- 在启动service之前,需要先确保service对应的进程已经存在。否则需要进行创建。而创建service的时候又要分两种情况:service运行于调用者同一个进程、运行于另一个进程。
- 将上面的
startServiceInnerLocked创建进程
- 接下来区分service是否为独立的进程(isolated)来进行启动。当service运行于同一个进程时,通过
mAm.getProcessRecordLocked()
获取ProcessRecord
,调用realStartServiceLocked()
启动服务。否则先启动对应的进程。 - 如果目标进程还没有启动,则启动进程。调用
AMS.startProcessLocked()
来启动目标进程。该方法可以接收一个HostingRecord
可以携带Service的ComponentName,AMS在启动进程时会启动该服务,再次回到本方法的已创建进程的流程里面来启动服务。
- 接下来区分service是否为独立的进程(isolated)来进行启动。当service运行于同一个进程时,通过
realStartServiceLocked
realStartServiceLocked()
做了一些准备工作。其中通过bumpServiceExecutingLocked()
调用scheduleServiceTimeoutLocke()
向AMS发送一个Message,该Message的目的是,监控service的启动耗时。service启动完成后会清除该Message,而超时没有清除时,就会触发该Message,会让AMS认为该服务启动异常,做出响应。(ActivityThread.handleServiceCreate()
完成后,通过Binder调用AMS,AMS再调用ActiveServices
移除掉该Message)- 调用
ProcessRecord.IApplicationThread.scheduleCreateService()
回调到应用进程。在ActivityThread
中发送Message到Handler,回调handleCreateService()
方法。这个方法内部加载目标service,并回调onCreate()
。并调用AMS
的serviceDoneExecuting()
移除ActiveServices
发出的服务启动超时信息。 scheduleCreateService()
调用完成后,紧接着会调用ServiceRecord.postNotification()
在标题栏展示前台服务必备的Notification。直到这一步,通知栏的通知就可见了。- 下一步就是触发App进程的Service的
onStartCommand()
了。接着调用sendServiceArgsLocked()
通过IApplicationThread
发送Message到ActivityThread.H
,在对应的ActivityThread.handleServiceArgs()
调用service的onStartCommand()
。 - 到这里,整个Service的启动流程已经完成了。最后一步会通过检查
ServiceRecord.delayedStop
标志,如果它为true则表示服务启动过程中收到了停止该服务的请求。该flag表示该服务已经被请求关闭,只是它已经处于delayed
队列了,需要等当前动作完成后再处理stop请求。因此再start流程的最后一步发现这个关闭请求则会响应这个stop,将服务关闭。
ActivityThread
ActivityThread
主要是作为客户端使用ActivityManagerProxy
与AMS通信,主要是发出startService()
的API调用。ActivityThread
提供IApplicationThread
到AMS,用以接收四大组件的生命周期的回调。回调的处理函数全部都是向Handler发送Message,各个Message的处理函数创建对应的四大组件、调用四大组件的生命周期回调。
TODO: ActivityThread创建service的流程。
总结
AMS通过ActiveServices
以及ServiceMap
、ServiceRecord
来管理service,并基于它们来执行权限检查,通过IApplicationThread
回调到应用进程,并通过H
这个Handler来在主线程完成对Service的创建和关键生命周期方法的回调。
参考
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 PeaceMaker!
评论
DisqusWaline