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 的详细流程。感谢大家的阅读,有什么不对的地方还望大家不吝赐教。