Service 的启动过程(上)
作为四大组件的 Service
,由于没有 UI 界面,只能默默无闻地在后台工作。虽然我们说他是后台工作的,但是他还是默认进程的主线程中运行的,除非我们给它指定了单独的进程。Service
的启动过程有两种,一种是 startService()
,另一种是 bindService()
。我会在接下来的两篇文章中分别来介绍着两种启动方式,首先我们来探究 startService()
启动服务的过程。
通过 startService 启动一个 Service
Activity
和 Service
都是从 ContextWrapper
继承而来,而 ContextWrapper
继承自 Context
这个抽象类,所以我们通常把 Activity
和 Service
看作是 Context
对象。ContextWrapper
的实现用的是典型的装饰者模式,它的一系列的方法都靠内部的被装饰者的同名方法来实现,这和代理模式有点类似。而这个被装饰者通常是一个 ContextImpl
,它是 Context
的真正实现者。
startService()
是 Context
中定义的一个方法,由于 Activity
和 Service
属于 ContextWrapper
类型,所以这个方法真正的实现在 ContextImpl#startService()
中:
1 |
|
startService()
会调用 startServiceCommon()
方法:
1 | private ComponentName startServiceCommon(Intent service, UserHandle user) { |
ActivityManagerNative.getDefault()
返回的是一个 ActivityManagerProxy
对象,它作为 AMS 在本进程的代理,如果我们的应用程序要和 AMS 打交道,必须要以它为媒介。这样看来 ActvitiyManagerService
并不像它名字所暗示的那样只管理 Activity
,Servcie
也同样归它管。显然,这是一个远程调用,具体的实现在 ActivityManagerService#startService()
中:
1 |
|
这个方法并没有做什么,它把事情交给了 mService
, mService
是一个 ActiveServices
对象。在早期的安卓版本中并没有这个类,后来重构时抽出这个类专门用来管理 Service
,ActiveServices#startServiceLocked()
有点长,我们挑重要部分来看:
1 | ServiceLookupResult res = |
这个方法根据这个 Service
对应的 Intent
解析出一个 ServiceRecord
,然后把事情交给了 startServiceInnerLocked()
,这个方法的核心就是调用了 bringUpServiceLocked()
,因此我们进入 ActiveService#bringUpServiceLocked()
看看:
1 | private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, |
这段代码中有一个分支:
- 如果
Service
要运行在启动服务的进程中(默认情况),就直接转到realStartServiceLocked()
方法; - 如果
Service
需要运行在指定进程(注册时给Service
指定进程名)中就会通知 AMS 新开一个进程(如果这个进程不存在的话),并把这个Service
对应的ServiceRecord
对象放进等待列表mPendingServices
中,当进程开启成功后会从列表中取出ServiceRecord
对象,然后把ServiceRecord
对应的Service
在该进程中创建并运行。
下面我们分别对这两种情况进行研究。
在启动服务的进程中创建 Service
前面说了,如果在启动服务的进程中启动 Service
会进入到 ActiveService#realStartServiceLocked()
方法中,它有两行代码值得关注:
第一行代码
1 | app.thread.scheduleCreateService(r, r.serviceInfo, |
关于这行代码说明两点:
app
是要运行Service
的进程对应的ProcessRecord
对象,系统源码很多地方用ProcessRecord
代表一个应用进程,这和用ActivityRecord
代表一个Activity
以及用ServiceRecord
代表一个Service
是同样的道理。thread
是一个ApplicationThreadProxy
对象,它是应用进程的ApplicatonThread
对象在 AMS 端的代理,AMS 靠它来和应用进程进行通信。你可能会感到奇怪,前面不是说了应用进程和 AMS 通信靠的是ActivityManagerProxy
吗,这里怎么出来了一个ApplicationThreadProxy
?我们要清楚,Binder
实现的进程间通信是单向的,其信息传递方向是BinderProxy
—>Binder
。但是很显然,应用进程和 AMS 是需要双向通信的,所以要想实现双向通信,必须有两对Binder
-BinderProxy
才行,这就是ApplicationThread
和ApplicationThreadProxy
存在的的原因。应用进程和 AMS 的通信可以用下图来表示:
虽然在这里是 ActiveServices
与应用进程通信,但 ActiveServices
也是用来辅助 AMS 管理 Service
的,所以也可以把这个过程看作是 AMS 与 应用进程的通信。(要知道早期的安卓版本没有 ActiveServices
这个类,这些逻辑都是在 AMS 中的)
这行代码的作用是通知应用进程根据已知信息创建一个 Service
,那么应用进程是怎样创建这个 Service
的呢?进入到 ApplicationThread#scheduleCreateService()
:
1 | public final void scheduleCreateService(IBinder token, |
在 scheduleCreateService()
中,先是把 AMS 传来的信息封装成一个 CreateServcieData
对象,然后调用 sendMessage()
把信息发送出去。注意,sendMessage()
是 ActivitytThead
的方法,因为 ApplicationThread
是 ActivityThread
的内部类,所以对 ActivityThread
有完全的访问权限。这样一来消息就从 ApplicationThread
传到了 ActivityThread
,我们来看看 ActivityThread#sendMessage()
:
1 | private void sendMessage(int what, Object obj) { |
转到另一个重载方法:
1 | private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) { |
这里把信息传给了 mH
这个 Handler
对象,这个 Handler
对象是在应用进程的主线程中创建的,所以最终的结果是把创建 Service
的消息传到了主线程。现在你终于明白了为什么 Service
会在主线程运行吧?看看 mH
是怎样处理这个消息的:
1 | case CREATE_SERVICE: |
百转千回,消息最终传到了 handleCreateService()
:
1 | private void handleCreateService(CreateServiceData data) { |
这里是 Service
真正被创建的地方,这个方法做了以下几件事:
- 根据类名用应用程序的类加载器加载并实例化一个
Service
。 - 创建一个
ContextImpl
对象,把Service
作为它的额外层Context
对象。 - 为这个
Service
创建一个Application
对象,当然,如果应用进程已经存在Application
就不会重复创建,具体大家可以看看LoadedAPK#makeApplication()
的实现,这里就不详细讲了。 - 用
Service
的attach()
方法把相应信息附加到Service
,其中,context
是之前创建的ContextImpl
对象,在attach()
方法中会把它作为Service
内部的被装饰对象;this
代表本进程中的ActivityThread
对象;data.info.name
是这个Service
的名称;data.token
是从 AMS 传过来的ServiceRecord
对象,它是一个Binder
,作为这个Service
的标识。 - 调用
Service
的onCreate()
方法。 - 把
token
和Service
的映射关系保存在mServices
中。 - 通过
ActivityManagerProxy
告知 AMSService
已经创建好了,让其完成后续的工作。
关于后续 AMS 做了哪些事,我们就不深究了,大家有兴趣可以自行阅读源码。现在我们看看 ActiveService#realStartServiceLocked()
中的另一行代码。
第二行代码
1 | sendServiceArgsLocked(r, execInFg, true); |
这个方法的核心部分在这里:
1 | r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent); |
这里又通过 ApplicationThreadProxy
和应用进程进行了通信,我们看看应用进程是怎样响应这个方法调用的:
1 | public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId, |
和前面一样,同样是把信息封装好后通过安卓的消息机制投递到主线程中,我们看看 Handler:mH
是怎样处理这个消息的:
1 | case SERVICE_ARGS: |
转入 ActivityThread#handleServiceArgs
:
1 | private void handleServiceArgs(ServiceArgsData data) { |
这里先根据 token
取出保存的 Service
,然后根据 data.taskRemoved
的值回调 Service
中的相应方法,一般情况下这个值是为 false
的,也就是说 Service
的 onStartCommand()
方法会得到调用。至此在服务启动者的进程中创建服务的过程就分析完了,现在我们看看指定进程中启动服务的过程是怎样的。
在指定进程中创建并启动 Service 的过程
如果注册 Service
的时候我们给 Service
指定了进程名,那么 Service
就会在那个进程中被创建并运行,这个时候启动过程就会走向另一条分支,还记得出现分支的地方吗?它在 ActiveService#bringUpServiceLocked()
中,这条分支会执行以下代码:
1 | private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, |
注意到这行代码 mAm.startProcessLocked()
,mAm
就是 AMS,这里通过 AMS 开启了一个进程。现在我们进入 ActivityManagerService#startProcessLocked()
这个方法:
1 | private final void startProcessLocked(ProcessRecord app, |
转向它的一个重载方法,这个重载方法有点长,但它的核心代码也就一句话:
1 | Process.ProcessStartResult startResult = Process.start(entryPoint, |
Process#start()
会利用这些参数向 native 层 fork 一个进程。注意到这个方法的第一参数 String:entryPoint
,顾名思义,它是进程的入口点,其值为 "android.app.ActivityThread"
。对于 ActivityThread
我们应该很熟悉了,每个应用进程都有一个 ActivityThread
对象,它代表着应用进程的主线程,处理着内部类 ApplicationThread
发送过来的来自 AMS 的各种消息。进程创建好后 native 层代码后就会调用这个类的静态方法 main()
,它的实现如下:
1 | public static void main(String[] args) { |
它很简短,主要做了这几件事:
- 为主线程准备消息循环
- 为应用进程创建一个
ActivityThread
对象 - 调用
ActivityThread
的attach()
方法 - 开启消息循环
这样我们的应用进程就进入到一个死循环中了,不断的接受消息并执行。所以我们会说安卓应用是消息驱动的。我们重点关注 attach()
方法,它有这样一段代码值得关注:
1 | final IActivityManager mgr = ActivityManagerNative.getDefault(); |
这里通过 ActivityManagerProxy
远程调用了 AMS 的 attachApplication()
方法,参数是 mAppThread
,而它是这样初始化的:
1 | final ApplicationThread mAppThread = new ApplicationThread(); |
说明它就是 ApplicationThread
对象,前面说了,AMS 要想向应用进程发送消息,需要借助 ApplicationThreadProxy
对象。而通过 Binder
机制 ApplicationThread
在 AMS 那边就转化成了 ApplicationThreadProxy
对象,所以这个对象就是在此时传给 AMS 的。在 AMS 中,attachApplication()
会直接调用 attachApplicationLocked()
,对于这个方法,我们挑需要的代码段来看:
1 | if (!badApp) { |
mServices
就是 ActiveServices
,进入 ActiveServices#attachApplicationLocked()
:
1 | boolean attachApplicationLocked(ProcessRecord proc, String processName) |
大家还记得 mPendingServices
吗?前面说了,如果在指定进程当中运行一个 Service
,会先创建一个进程,然后在把该 Service
对应的 ServiceRecord
对象放入 mPendingServices
中,待进程创建好了就会从中取出 ServiceRecord
,然后根据它在进程中创建 Service
。具体过程是怎样的呢?我们看看这个方法主要作了哪些事:
遍历 mPendingServices
,根据 ProcessRecord:proc
和 String:processName
提供的进程信息找出要运行在这个进程的 ServiceRecord
,然后调用 realStartServiceLocked()
方法并把找到的 ServiceRecord
作为参数传入其中。realStartServiceLocked()
之后的流程前面已经有介绍,这里就不重复讲了。
至此,通过 startService()
启动服务的整个过程就介绍完了,在接下来的一篇文章中我会介绍通过 bindService()
创建并绑定一个 Service
的详细流程。感谢大家的阅读,有什么不对的地方还望大家不吝赐教。