深入AndroidFramework源码内部剖析Service的
你在
Activity
中调用startService(Intent)
,轻松启动了一个Service
,但是你不知道,为了完成这么一个简单的功能,Android系统在背后为我们做了多少事情,本文将为你揭晓......
须知
要理解本文所讲内容,必须对binder机制非常非常清楚,因为下面的内容就像在走盘山公路,弯弯绕绕不少,没有binder这个指南针,你很快就会晕头转向。milter之前有写过一篇binder机制的文章,你可以用来热身,请戳这里
情景
我们在一个应用的Activity中通过startService(Intent)
启动的Service
是另一个应用中的名为RemoteService
的Service
。That's all。
总纲
要理解startService(Intent)
背后发生了什么,只需要看懂下面两张图即可(现在看不懂不要紧,看完本文后还看不懂,那就...嘎嘎)。
图1:(强烈呼吁简书支持markdown书写时序图,害的我还得截图)

图2:

开扒
下面,要开车了,大家坐好,系好安全带,路线是图1中的五个步骤,图2是用来辅助理解图1的。
1、深入Activity
中的startService()
在该方法内部,关键动作有两个:
- 获得一个
ActivityManagerProxy
对象 - 调用该对象的
startService( IApplicationThread caller, Intent service, String resolvedType );
说明:要理解这两步最主要的是搞清楚ActivityManagerProxy
与IApplicationThread
是做什么的。下面进行讲解。
这里通过和本博客另一篇讲binder机制文章中的例子做个对比,方便大家理解。
IActivityManager---------------------IPlus
ActivityManagerNative--------------plus
ActivityManagerProxy---------------PlusProxy
Activity--------------------------------进程A
ActivityManagerService-------------进程B
实际上,IActivityManager
接口中定义了ActivityManagerService
向应用程序(本例中即Activity
)提供的多种API,Activity
通过ActivityManagerProxy
就可以使用这些API,向ActivityManagerService
发出请求。在本例中,发出的请求是:
startService( IApplicationThread caller, Intent service, String resolvedType );
第二个参数service
中包含着要启动的Service
的信息,第三个参数在本例中不重要,简单起见,可以忽略。关键是第一个参数caller
是干什么的?
简单来讲,caller
相当于Activity
所在应用程序的一个控制接口,其实际类型为ApplicationThreadNative
,Activity把它提供给ActivityManagerService
,ActivityManagerService
通过它来控制Activity
所在的应用程序。
startService()
调用后,经过系统的中转,最终会调用ActivityManagerNative
中的onTransact()
方法,在该方法中,将会利用caller
完成ActivityManagerService
与Activity
的连接并调用ActivityManagerService
的startService()
方法,当然,这里的连接用到的也是Binder
机制,我们还是通过与本博客中讲binder机制文章中的例子做个对比来理解。
IApplicationThread---------------------IPlus
ApplicationThreadNative--------------plus
ApplicationThreadProxy---------------PlusProxy
Activity-----------------------------------进程B
ActivityManagerService----------------进程A
现在再来看一下这张图。

是不是感觉清楚多了?这就是
Activity
与ActivityManagerService
之间的双向Binder连接。Activity
用IActivityManager
提供的API向ActivityManagerService
提出执行某个动作的请求(本例中是启动RemoteService
),ActivityManagerService
通过IApplicationThread
提供的API来控制Activity
所在的应用程序,这些API包括schedulePauseActivity()、scheduleStopActivity()
等。
2、转场进入ActivityManagerService
好了,Activity
通过ActivityManagerProxy
的startService(...,Intent service,...)
方法,向ActivityManagerService
提出了启动RemoteService
的要求,并与ActivityManagerService
建立了双向binder
连接并调用了ActivityManagerService
的startService()
方法,在该方法中有这么关键几步:
- 首先,根据传过来的Intent service中关于
RemoteService
的信息,去查找一个Service
表,这个表中保存着该手机上所有的应用的<manifest>
文件中声明的所有Service
的信息(晕不晕?放心,不会有更绕的啦。),而且每个Service
的信息都用一个ServiceRecord
对象封装。经过查找,得到了RemoteService
对应的ServiceRecord
对象。 -
ServiceRecord
对象中有一条信息用于说明该RemoteService
应该运行在哪个进程中,通常情况下,就是RemoteService
所在应用的进程。程序接下来会利用该信息查找另外一张表,这张表保存着当前设备上所有正在运行的进程的信息,且每个进程的信息都用一个ProcessRecord
对象封装了。我们假设RemoteService
所在应用还没有启动,那么显然,这次查找是落空了。
此时,程序会先将RemoteService
的ServiceRecord
信息暂存入一个名为mPendingServices
(后面还会用到哦!)的数组中,转而去创建运行RemoteService
所需要的进程,方法就是调用Process
类的start("android.app.ActivityThread")
方法。
说明:以上过程中涉及到的两张表都归ActivityManagerService
管理。
你有没有感觉到ActivityManagerService
的强大威力,有木有?但是,还有比它更牛逼的,接下来就是。
3、再次转场进入Zygote
ActivityManagerService
通过Process
的start("android.app.ActivityThread")
方法,实际上是要求创建一个进程(为了运行RemoteService
),并在进程中加载ActivityThread
类,然后运行该类的main
方法。这一 要求将会被Zygote
收到,Zygote
将会满足ActivityManagerService
的要求。
在继续进行之前,我们必须停下来解决两个问题,第一个就是Zygote
是什么,它能干什么?第二个就是ActivityThread
是个什么梗?这玩意儿怎么还有main
方法?(我们现在是在Android应用中呢还是在普通的java程序中?天哪!)
首先来看Zygote
,它是系统中一个进程,每当我们要启动一个应用时,都是由它派生出一个子进程,然后在该子进程中运行我们的应用,也就是说,Android手机上运行的所有应用的进程都是Zygote
进程的子进程。Zygote
在英文中的意思是“受精卵”,你可以体会下Google工程师为什么将该进程命名为Zygote
。
第二个问题。没错,从Zygote
创建子进程,加载ActivityThread
类,运行它的main
方法,这一过程就是一个普通的java程序的启动过程。区别在于ActivityThread
运行的方式不同于一般的java程序,这种运行方式是专为Android应用程序设计的。那么,ActivityThread
的运行方式特殊在什么地方呢?所谓一言不合,就看源码,下面就是ActivityThread
的主要代码:
public final class ActivityThread {
final ApplicationThread mAppThread = new ApplicationThread();
public static void main(String[] args) {
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
Looper.loop();
}
public void handleMessage(Message msg) {
switch (msg.what) {
......//很多很多cases
case CREATE_SERVICE:
handleCreateService((CreateServiceData)msg.obj);
break;
......//还有很多很多cases
}
}
}
此时,我们必须要强调,ActivityThread main
方法的运行,标志着一个新的Android应用的运行,这是一个milestone。
下面我们来分析一下ActivityThread
中的代码。
简言之,以上代码就干了两件事:
- 一是通过
thread.attach(false)
方法,将该应用的控制接口mAppThread
(类型为ApplicationThread
,就是上面图中的ApplicationThreadNative
)传递给ActivityManagerService
,好让ActivityManagerService
来控制这个应用,这是通过Binder
机制实现的。由此可见,每个应用在混沌初开的时候,就乖乖把自己的控制权交给了ActivityManagerService
,ActivityManagerService
不可谓不牛逼!!! - 二是初始化并启动一个
MainLooper
,而它唯一的handler
就是ActivityThread
对象thread
(在main
方法中创建)。
4、终于再次转场进入ActivityManagerService
前面说了,新建的应用通过thread.attach(false)
方法把自己的控制接口交给了ActivityManagerService
。
还记得第2步中ActivityManagerService
调用Process
的start
方法不?此时,该方法终于返回了,返回值是一个pid
,然后ActivityManagerService
就用这个pid
还有接收到的ApplicationThread
对象 mAppThread
(虽然是个代理的),组装出一个ProcessRecord
对象,再把这个对象放入正在运行的进程的记录表中。
然后呢?进程已经有了,ActivityManagerService
就会把之前暂存入mPendingServices
数组中的ServiceRecord
取出来,把它的信息通过mAppThread
发一个消息给新建的应用进程,表达了让它创建一个RemoteService
并运行它的要求。
5、最后一站,转场进入新建的ActivityThread
此时,ActivityThread
的对象thread
会收到这个消息,然后它在handleMessage
方法中的case CREATE_SERVICE:
下创建了RemoteService
,并把它初始化。
没错,就是handleCreateService((CreateServiceData)msg.obj);
这句。这个方法的内部主要代码如下:
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo);
Service service = null;
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
service.onCreate();
}
好了,经过千回百转,柳暗花明,殚精竭虑,矢志不移,衣带渐宽终不悔的努力,我们的
RemoteService
它
它
它终于运行起来啦!!!