6.2 初识ActivityManagerService
AMS由system_server的ServerThread线程创建,提取它的调用轨迹,代码如下:
[—>system_server.java:ServerThread的run函数]
//①调用main函数,得到一个Context对象
context=ActivityManagerService.main(factoryTest);
//②setSystemProcess:这样system_server进程可加到AMS中,并被它管理
ActivityManagerService.setSystemProcess();
//③installSystemProviders:将SettingsProvider放到system_server进程中来运行
ActivityManagerService.installSystemProviders();
//④在内部保存WindowManagerService(以后简称WMS)
ActivityManagerService.self().setWindowManager(wm);
//⑤和WMS交互,弹出“启动进度“对话框
ActivityManagerNative.getDefault().showBootMessage(
context.getResources().getText(
//该字符串中文意思为:正在启动应用程序
com.android.internal.R.string.android_upgrading_starting_apps),
false);
//⑥AMS是系统的核心,只有它准备好后,才能调用其他服务的systemReady
//注意,有少量服务在AMS systemReady之前就绪,它们不影响此处的分析
ActivityManagerService.self().systemReady(new Runnable(){
public void run(){
startSystemUi(contextF);//启动systemUi。如此,状态栏就准备好了
if(batteryF!=null)batteryF.systemReady();
if(networkManagementF!=null)networkManagementF.systemReady();
……
Watchdog.getInstance().start();//启动Watchdog
……//调用其他服务的systemReady函数
}
在以上代码中,一共列出了6个重要调用及这些调用的简单说明,本节将分析除与WindowManagerService(以后简称WMS)交互的④、⑤外的其余四项调用。
先来分析①处调用。
6.2.1 ActivityManagerService的main函数分析
AMS的main函数将返回一个Context类型的对象,该对象在system_server中被其他服务大量使用。Context,顾名思义,代表了一种上下文环境(笔者觉得其意义和JNIEnv类似),有了这个环境,我们可以做很多事情(例如获取该环境中的资源、Java类信息等)。那么AMS的main将返回一个怎样的上下文环境呢?来看以下代码:
[—>ActivityManagerService.java:main]
public static final Context main(int factoryTest){
AThread thr=new AThread();//①创建一个AThread线程对象
thr.start();
……//等待thr创建成功
ActivityManagerService m=thr.mService;
mSelf=m;
//②调用ActivityThread的systemMain函数
ActivityThread at=ActivityThread.systemMain();
mSystemThread=at;
//③得到一个Context对象,注意调用的函数名为getSystemContext,何为System Context
Context context=at.getSystemContext();
context.setTheme(android.R.style.Theme_Holo);
m.mContext=context;
m.mFactoryTest=factoryTest;
//ActivityStack是AMS中用来管理Activity的启动和调度的核心类,以后再分析它
m.mMainStack=new ActivityStack(m, context, true);
//调用BSS的publish函数,这个知识点我们在第5章中介绍过了
m.mBatteryStatsService.publish(context);
//另外一个service:UsageStatsService。读者阅读完本章后自行分析它
m.mUsageStatsService.publish(context);
synchronized(thr){
thr.mReady=true;
thr.notifyAll();//通知thr线程,本线程工作完成
}
//④调用AMS的startRunning函数
m.startRunning(null, null, null, null);
return context;
}
在main函数中,我们又列出了4个关键函数,分别是:
创建AThread线程。虽然AMS的main函数由ServerThread线程调用,但是AMS自己的工作并没有放在ServerThread中去做,而是新创建了一个线程,即AThread线程。
ActivityThread. systemMain函数。初始化ActivityThread对象。
ActivityThread. getSystemContext函数。用于获取一个Context对象,从函数名上看,该Context代表了System的上下文环境。
AMS的startRunning函数。
注意,main函数中有一处等待(wait)、一处通知(notifyAll),这是因为:
main函数首先需要等待AThread所在线程启动并完成一部分工作。
AThread完成那一部分工作后,将等待main函数完成后续的工作。
这种双线程互相等待的情况,在Android代码中比较少见,读者只需了解它们的目的即可。下边来分析以上代码中的第一个关键点。
1.AThread分析
(1)AThread分析
AThread的代码如下:
[—>ActivityManagerService.java:AThread]
static class AThread extends Thread{//AThread从Thread类派生
ActivityManagerService mService;
boolean mReady=false;
public AThread(){
super("ActivityManager");//线程名就叫ActivityManager
}
public void run(){
Looper.prepare();//看来,AThread线程将支持消息循环
android.os.Process.setThreadPriority(//设置线程优先级
android.os.Process.THREAD_PRIORITY_FOREGROUND);
android.os.Process.setCanSelfBackground(false);
//创建AMS对象
ActivityManagerService m=new ActivityManagerService();
synchronized(this){
mService=m;//为AThread内部成员变量mService赋值,指向AMS
notifyAll();//通知main函数所在线程
}
synchronized(this){
while(!mReady){
try{
wait();//等待main函数所在线程的notifyAll
}……
}
}……
Looper.loop();//进入消息循环
}
}
从本质上说,AThread是一个支持消息循环及处理的线程,其主要工作就是创建AMS对象,然后通知AMS的main函数。这样看来,main函数等待的就是这个AMS对象。
(2)AMS的构造函数分析
AMS的构造函数的代码如下:
[—>ActivityManagerService.java:ActivityManagerService]
private ActivityManagerService(){
File dataDir=Environment.getDataDirectory();//指向/data/目录
File systemDir=new File(dataDir,"system");//指向/data/system/目录
systemDir.mkdirs();//创建/data/system/目录
//创建BatteryStatsService(以后简称BSS)和UsageStatsService(以后简称USS)
//我们在分析PowerManageService时已经见过BSS了
mBatteryStatsService=new BatteryStatsService(new File(
systemDir,"batterystats.bin").toString());
mBatteryStatsService.getActiveStatistics().readLocked();
mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
mOnBattery=DEBUG_POWER?true
:mBatteryStatsService.getActiveStatistics().getIsOnBattery();
mBatteryStatsService.getActiveStatistics().setCallback(this);
//创建USS
mUsageStatsService=new UsageStatsService(new File(
systemDir,"usagestats").toString());
//获取OpenGl版本
GL_ES_VERSION=SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
//mConfiguration类型为Configuration,用于描述资源文件的配置属性,例如
//字体、语言等
mConfiguration.setToDefaults();
mConfiguration.locale=Locale.getDefault();
//mProcessStats为ProcessStats类型,用于统计CPU、内存等信息。其内部工作原理就是
//读取并解析/proc/stat文件的内容。该文件由内核生成,用于记录kernel及system
//一些运行时的统计信息。读者可在Linux系统上通过man proc命令查询详细信息
mProcessStats.init();
//解析/data/system/packages-compat.xml文件,该文件用于存储那些需要考虑屏幕尺寸
//的APK的一些信息。读者可参考AndroidManifest.xml中compatible-screens相关说明。
//当APK所运行的设备不满足要求时,AMS会根据设置的参数以采用屏幕兼容的方式去运行它
mCompatModePackages=new CompatModePackages(this, systemDir);
Watchdog.getInstance().addMonitor(this);
//创建一个新线程,用于定时更新系统信息(和mProcessStats交互)
mProcessStatsThread=new Thread("ProcessStats"){……//先略去该段代码}
mProcessStatsThread.start();
}
AMS的构造函数比想象得要简单些,下面回顾一下它的工作:
创建BSS、USS、mProcessStats(ProcessStats类型)、mProcessStatsThread线程,这些都与系统运行状况统计相关。
创建/data/system目录,为mCompatModePackages(CompatModePackages类型)和mConfiguration(Configuration类型)等成员变量赋值。
AMS main函数的第一个关键点就分析到此,接下来来分析它的第二个关键点。
2.ActivityThread.systemMain函数分析
ActivityThread是Android Framework中一个非常重要的类,它代表一个应用进程的主线程(对于应用进程来说,ActivityThread的main函数确实是由该进程的主线程执行),其职责就是调度及执行在该线程中运行的四大组件。
注意 应用进程指那些运行APK的进程,它们由Zyote派生(fork)而来,上面运行了dalvik虚拟机。与应用进程相对的就是系统进程(包括Zygote和system_server)。
另外,读者须将“应用进程和系统进程”与“应用APK和系统APK”的概念区分开来。对APK的判别依赖其文件所在位置(如果APK文件在/data/app目录下,则为应用APK)。
ActivityThread. systemMain函数代码如下:
[—>ActivityThread.java:systemMain]
public static final ActivityThread systemMain(){
HardwareRenderer.disable(true);//禁止硬件渲染加速
//创建一个ActivityThread对象,其构造函数非常简单
ActivityThread thread=new ActivityThread();
thread.attach(true);//调用它的attach函数,注意传递的参数为true
return thread;
}
在分析ActivityThread的attach函数之前,先提一个问题供读者思考:前面所说的ActivityThread代表应用进程(其上运行了APK)的主线程,而system_server并非一个应用进程,那么为什么此处也需要ActivityThread呢?
还记得在PackageManagerService分析中提到的framework-res.apk吗?这个APK除了包含资源文件外,还包含一些Activity(如关机对话框),这些Activity实际上运行在system_server进程中[1]。从这个角度看,system_server是一个特殊的应用进程。
通过ActivityThread可以把Android系统提供的组件之间的交互机制和交互接口(如利用Context提供的API)也拓展到system_server中使用。
提示 解答这个问题,对于理解system_server中各服务的交互方式是尤其重要的。
下面来看ActivityThread的attach函数。
(1)attach函数分析
[—>ActivityThread.java:attach]
private void attach(boolean system){
sThreadLocal.set(this);
mSystemThread=system;//判断是否为系统进程
if(!system){
……//应用进程的处理流程
}else{//系统进程的处理流程,该情况只在system_server中处理
//设置DDMS时看到的system_server进程名为system_process
android.ddm.DdmHandleAppName.setAppName("system_process");
try{
//ActivityThread的几员“大将”出场,见后文的分析
mInstrumentation=new Instrumentation();
ContextImpl context=new ContextImpl();
//初始化context,注意第一个参数值为getSystemContext
context.init(getSystemContext().mPackageInfo, null, this);
Application app=//利用Instrumentation创建一个Application对象
Instrumentation.newApplication(Application.class, context);
//一个进程支持多个Application, mAllApplications用于保存该进程中的
//Application对象
mAllApplications.add(app);
mInitialApplication=app;//设置mInitialApplication
app.onCreate();//调用Application的onCreate函数
}……//try/catch结束
}//if(!system)判断结束
//注册Configuration变化的回调通知
ViewRootImpl.addConfigCallback(new ComponentCallbacks2(){
public void onConfigurationChanged(Configuration newConfig){
……//当系统配置发生变化(如语言切换等)时,需要调用该回调
}
public void onLowMemory(){}
public void onTrimMemory(int level){}
});
}
attach函数中出现了几个重要成员,其类型分别是Instrumentation类、Application类及Context类,它们的作用如下(为了保证准确,这里先引用Android的官方说明)。
Instrumentation:Base class for implementing application instrumentation code. Whenrunning with instrumentation turned on, this class will be instantiated for you before any of the application code, allowing you to monitor all of the interaction the system has with the application.An Instrumentation implementation is described to the system through an AndroidManifest.xml's<instrumentation>tag.大意是:Instrumentation是一个工具类。当它被启用时,系统先创建它,再通过它来创建其他组件。另外,系统和组件之间的交互也将通过Instrumentation来传递,这样,Instrumentation就能监测系统和这些组件的交互情况了。在实际使用中,我们可以创建Instrumentation的派生类来进行相应的处理。读者可查询Android中Junit的使用来了解Instrumentation的作用。本书不讨论Instrumentation方面的内容。
Application:Base class for those who need to maintain global application state. Youcan provide your own implementation by specifying its name in your AndroidManifest.xml's<application>tag, which will cause that class to be instantiated for you when the process for your application/package is created.大意是:Application类保存了一个全局的application状态。Application由AndroidManifest.xml中的<application>标签声明。在实际使用时需定义Application的派生类。
Context:Interface to global information about an application environment. This is anabstract class whose implementation is provided by the Android system.It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.大意是:Context是一个接口,通过它可以获取并操作Application对应的资源、类,甚至包含于Application中的四大组件。
提示 此处的Application是Android中的一个概念,可理解为一种容器,其内部包含四大组件。另外,一个进程可以运行多个Application。
Context是一个抽象类,而由AMS创建的是它的子类ContextImpl。如前所述,Context提供了Application的上下文信息,这些信息是如何传递给Context的呢?此问题包括两个方面:
Context本身是什么?
Context背后所包含的上下文信息又是什么?
下面来关注上边代码中调用的getSystemContext函数。
(2)getSystemContext函数分析
getSystemContext函数的实现代码如下:
[—>ActivityThread.java:getSystemContext]
public ContextImpl getSystemContext(){
synchronized(this){
if(mSystemContext==null){//单例模式
ContextImpl context=ContextImpl.createSystemContext(this);
//LoadedApk是Android2.3引入的一个新类,代表一个加载到系统中的APK
LoadedApk info=new LoadedApk(this,"android",context, null,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
//初始化该ContextImpl对象
context.init(info, null, this);
//初始化资源信息
context.getResources().updateConfiguration(
getConfiguration(),getDisplayMetricsLocked(
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, false));
mSystemContext=context;//保存这个特殊的ContextImpl对象
}
}
return mSystemContext;
}
以上代码无非是先创建一个ContextImpl,然后再将其初始化(调用init函数)。为什么函数名是getSystemContext呢?因为在初始化ContextImp时使用了一个LoadedApk对象。如注释中所说,LoadedApk是Android 2.3引入的一个类,该类用于保存一些和APK相关的信息(如资源文件位置、JNI库位置等)。在getSystemContext函数中初始化ContextImpl的这个LoadedApk所代表的package,名为“android”,也就是framework-res.apk,由于该APK仅供system_server进程使用,所以此处称为getSystemContext。
上面这些类的关系比较复杂,可通过图6-2展示它们之间的关系。
图 6-2 ContextImpl和它的“兄弟”们
由图6-2可知:
先来看派生关系,ApplicationContentResolver从ConentResolver派生,它主要用于和ContentProvider打交道。ContextImpl和ContextWrapper均从Context继承,而Application则从ContextWrapper派生。
从涉及的面来看,ContextImpl涉及的面最广。它通过mResources指向Resources,通过mPackageInfo指向LoadedApk,通过mMainThread指向ActivityThread,通过mContentResolver指向ApplicationContentResolver。
ActivityThread代表主线程,它通过mInstrumentation指向Instrumentation。另外,它还保存多个Application对象。
注意 在函数中有些成员变量的类型为其基类的类型,而在图6-2中直接指向了实际类型。
(3)systemMain函数总结
systemMain函数调用结束后,我们得到了什么?
得到一个ActivityThread对象,它代表应用进程的主线程。
得到一个Context对象,它背后所指向的Application环境与framework-res.apk有关。费了如此大的工夫,systemMain函数的目的到底是什么?一针见血地说:systemMain函数将为system_server进程搭建一个和应用进程一样的Android运行环境。这句话涉及两个概念。
进程:来源于操作系统,是在OS中看到的运行体。我们编写的代码一定要运行在一个进程中。
Android运行环境:Android努力构筑了一个自己的运行环境。在这个环境中,进程的概念被模糊化了。组件的运行及它们之间的交互均在该环境中实现。
Android运行环境是构建在进程之上的。有Android开发经验的读者可能会发现,应用程序一般只和Android运行环境交互。基于同样的道理,system_server希望它内部的那些service也通过Android运行环境交互,因此也需为它创建一个运行环境。由于system_server的特殊性,此处调用了systemMain函数,而普通的应用进程将在主线程中调用ActivityThread的main函数来创建Android运行环境。
另外,ActivityThread虽然本意是代表进程的主线程,但是作为一个Java类,它的实例到底由什么线程创建,恐怕不是ActivityThread自己能做主的,所以在system_server中可以发现,ActivityThread对象由其他线程创建,而在应用进程中,ActivityThread将由主线程来创建。
3.ActivityThread.getSystemContext函数分析
ActivityThread. getSystemContext函数在上一节已经见过了。调用该函数后,将得到一个代表系统进程的Context对象。到底什么是Context?先来看图6-3所示的Context家族图谱。
注意 该族谱成员并未完全列出。另外,Activity、Service和Application所实现的接口也未画出。
由图6-3可知:
ContextWrapper比较有意思,其在SDK中的说明为Proxying implementation ofContext that simply delegates all of its calls to another Context.Can be subclassed to modify behavior without changing the original Context.大概意思是:ContextWrapper是一个代理类,被代理的对象是另外一个Context。在图6-3中,被代理的类其实是ContextImpl,由ContextWrapper通过mBase成员变量指定。读者可查看Context-Wrapper.java,其内部函数功能的实现最终都由mBase完成。这样设计的目的是想把ContextImpl隐藏起来。
图 6-3 Context家族图谱
Application从ContextWrapper派生,并实现了ComponentCallbacks2接口。Application中有一个LoadedApk类型的成员变量mLoadedApk。LoadedApk代表一个APK文件。由于一个AndroidManifest.xml文件只能声明一个Application标签,所以一个Application必然会和一个LoadedApk绑定。
Service从ContextWrapper派生,其中Service内部成员变量mApplication指向Application(在AndroidManifest.xml中,Service只能作为Application的子标签,所以在代码中Service必然会和一个Application绑定)。
ContextThemeWrapper重载了和Theme(主题)相关的两个函数。这些和界面有关,所以Activity作为Android系统中的UI容器,必然也会从ContextThemeWrapper派生。与Service一样,Activity内部也通过mApplication成员变量指向Application。对Context的分析先到这里,再来分析第三个关键函数startRunning。
4.AMS的startRunning函数分析
startRunning函数的实现代码如下:
[—>ActivityManagerService.java:startRunning]
//注意调用该函数时所传递的4个参数全为null
public final void startRunning(String pkg, String cls, String action,
String data){
synchronized(this){
if(mStartRunning)return;//如果已经调用过该函数,则直接返回
mStartRunning=true;
//mTopComponent最终赋值为null
mTopComponent=pkg!=null&&cls!=null
?new ComponentName(pkg, cls):null;
mTopAction=action!=null?action:Intent.ACTION_MAIN;
mTopData=data;//mTopData最终为null
if(!mSystemReady)return;//此时mSystemReady为false,所以直接返回
}
systemReady(null);//这个函数很重要,可惜不在本次startRunning中调用
}
startRunning函数很简单,此处不赘述。
至此,AMS的main函数所涉及的4个知识点已全部分析完。下面回顾一下AMS的main函数的工作。
5.ActivityManagerService的main函数总结
AMS的main函数的目的有两个:
第一个也是最容易想到的目的是创建AMS对象。
另外一个目的比较隐晦,但是非常重要,那就是创建一个供system_server进程使用的Android运行环境。
根据目前所分析的代码,Android运行环境将包括两个类成员:ActivityThread和Context-Impl(一般用它的基类Context)。
图6-4展示了在这两个类中定义的一些成员变量,通过它们可看出ActivityThread及Context-Impl的作用。
图 6-4 ActivityThread和ContextImpl中的部分成员变量
由图6-4可知:
ActivityThread中有一个mLooper成员,它代表一个消息循环。这恐怕是ActivityThread被称做“Thread”的一个直接证据。另外,mServices用于保存Service, Activities用于保存ActivityClientRecord, mAllApplications用于保存Application。关于这些变量的具体作用,以后遇到时再介绍。
对于ContextImpl,其成员变量表明它和资源、APK文件有关。
AMS的main函数先分析到此,其创建的Android运行环境将在后面的分析中用到。接下来分析AMS的第三个调用函数setSystemProcess。
[1]实际上,SettingsProvider.apk也运行于system_server进程中。