5.2.2 init分析
第二个关键点是init函数,该函数将初始化PMS内部的一些重要成员变量,由于此函数代码较长,此处将分段讨论。
从流程角度看,init大体可分为3段。
1.init分析之一
这部分的代码如下:
[—>PowerManagerService.java:init]
void init(Context context, LightsService lights, IActivityManager activity,
BatteryService battery){
//①保存几个成员变量
mLightsService=lights;//保存LightService
mContext=context;
mActivityService=activity;//保存ActivityManagerService
//保存BatteryStatsService
mBatteryStats=BatteryStatsService.getService();//
mBatteryService=battery;//保存BatteryService
//从LightService中获取代表不同硬件Light的Light对象
mLcdLight=lights.getLight(LightsService.LIGHT_ID_BACKLIGHT);
mButtonLight=lights.getLight(LightsService.LIGHT_ID_BUTTONS);
mKeyboardLight=lights.getLight(LightsService.LIGHT_ID_KEYBOARD);
mAttentionLight=lights.getLight(LightsService.LIGHT_ID_ATTENTION);
//②调用nativeInit函数
nativeInit();
synchronized(mLocks){
updateNativePowerStateLocked();//③更新Native层的电源状态
}
第一阶段的工作可分为3步:
对一些成员变量进行赋值。
调用nativeInit函数初始化Native层相关资源。
调用updateNativePowerStateLocked更新Native层的电源状态。这个函数的调用次数较为频繁,后续分析时会讨论。
先来看第一阶段出现的各类成员变量,如表5-1所示。
下面来看nativeInit函数,其JNI层实现代码如下:
[—>com_android_server_PowerManagerService.cpp]
static void android_server_PowerManagerService_nativeInit(JNIEnv*env,
jobject obj){
//非常简单,就是创建一个全局引用对象gPowerManagerServiceObj
gPowerManagerServiceObj=env->NewGlobalRef(obj);
}
init第一阶段工作比较简单,下面进入第二阶段的分析。
2.init分析之二
init第二阶段工作将创建两个HandlerThread对象,即创建两个带消息循环的工作线程。PMS本身由ServerThread线程创建,但它会将自己的工作委托给两个线程,它们分别是:
mScreenOffThread:按Power键关闭屏幕时,屏幕不是突然变黑的,而是一个渐暗的过程。mScreenOffThread线程就用于控制关闭屏幕过程中的亮度调节。
mHandlerThread:该线程是PMS的主要工作线程。
先来看这两个线程的创建。
(1)mScreenOffThread和mHandlerThread分析这部分的代码如下:
[—>PowerManagerService.java:init]
……
mScreenOffThread=new HandlerThread("PowerManagerService.mScreenOffThread"){
protected void onLooperPrepared(){
mScreenOffHandler=new Handler();//向这个handler发送的消息,将由此线程处理
synchronized(mScreenOffThread){
mInitComplete=true;
mScreenOffThread.notifyAll();
}
}
};
mScreenOffThread.start();//创建对应的工作线程
synchronized(mScreenOffThread){
while(!mInitComplete){
try{//等待mScreenOffThread线程创建完成
mScreenOffThread.wait();
}……
}
}
注意,在Android代码中经常出现“线程A创建线程B,然后线程A等待线程B创建完成”的情况,读者了解它们的作用即可。接着看以下代码。
[—>PowerManagerService.java:init]
mInitComplete=false;
//创建mHandlerThread
mHandlerThread=new HandlerThread("PowerManagerService"){
protected void onLooperPrepared(){
super.onLooperPrepared();
initInThread();//①初始化另外一些成员变量
}
};
mHandlerThread.start();
……//等待mHandlerThread创建完成
由于mHandlerThread承担了PMS的主要工作,因此需要先做一些初始化工作,相关的代码在initInThread中,我们会将这部分内容放在单独一节中进行讨论。
(2)initInThread分析
initInThread本身比较简单,涉及3个方面的工作,总结如下:
PMS需要了解外面的世界,所以它会注册一些广播接收对象,接收诸如启动完毕、
电池状态变化等广播。
PMS所从事的电源管理工作需要遵守一定的规则,而这些规则在代码中就是一些配置参数,这些配置参数的值可以是固定的(编译完后就无法改动),也可以是经由Settings数据库动态设定的。
PMS需要对外发出一些通知,例如屏幕关闭/开启。
了解initInThread的概貌后,再来看如下代码:
[—>PowerManagerService.java:initInThread]
void initInThread(){
mHandler=new Handler();
//PMS内部也需要使用WakeLock,此处定义了几种不同的UnsynchronizedWakeLock。它们的
//作用见后文分析
mBroadcastWakeLock=new UnsynchronizedWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,"sleep_broadcast",true);
//创建广播通知的Intent,用于通知SCREEN_ON和SCREEN_OFF消息
mScreenOnIntent=new Intent(Intent.ACTION_SCREEN_ON);
mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mScreenOffIntent=new Intent(Intent.ACTION_SCREEN_OFF);
mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
//取配置参数,这些参数是编译时确定的,运行过程中无法修改
Resources resources=mContext.getResources();
mAnimateScreenLights=resources.getBoolean(
com.android.internal.R.bool.config_animateScreenLights);
……//见下文的配置参数汇总
//通过数据库设置的配置参数
ContentResolver resolver=mContext.getContentResolver();
Cursor settingsCursor=resolver.query(Settings.System.CONTENT_URI, null,
……//设置查询条件和查询项的名字,见后文的配置参数汇总
null);
//ContentQueryMap是一个常用类,简化了数据库查询工作。读者可参考SDK中该类的说明文档
mSettings=new ContentQueryMap(settingsCursor, Settings.System.NAME,
true, mHandler);
//监视上边创建的ContentQueryMap中内容的变化
SettingsObserver settingsObserver=new SettingsObserver();
mSettings.addObserver(settingsObserver);
settingsObserver.update(mSettings, null);
//注册接收通知的BroadcastReceiver
IntentFilter filter=new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
mContext.registerReceiver(new BatteryReceiver(),filter);
filter=new IntentFilter();
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
mContext.registerReceiver(new BootCompletedReceiver(),filter);
filter=new IntentFilter();
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(new DockReceiver(),filter);
//监视Settings数据中secure表的变化
mContext.getContentResolver().registerContentObserver(
Settings.Secure.CONTENT_URI, true,
new ContentObserver(new Handler()){
public void onChange(boolean selfChange){
updateSettingsValues();
}
});
updateSettingsValues();
……//通知其他线程
}
在上述代码中,很大一部分用于获取配置参数。同时,对于数据库中的配置值,还需要建立监测机制,细节部分请读者自己阅读相关代码,这里总结一下常用的配置参数,如表5-2所示。
除了获取配置参数外,initInThread还创建了好几个UnsynchronizedWakeLock对象,它的作用是:在Android系统中,为了抢占电力资源,客户端要使用WakeLock对象。PMS自己也不例外,所以为了保证在工作中不至于突然掉电(当其他客户端都不使用WakeLock的时候,这种情况理论上是有可能发生的),PMS需要定义供自己使用的WakeLock。由于线程同步方面的原因,PMS封装了一个UnsynchronizedWakeLock结构,它的调用已经处于锁保护下,所以在内部无须再做同步处理。UnsynchronizedWakeLock比较简单,因此不再赘述。
下面来看init第三阶段的工作。
3.init分析之三
这部分内容的代码如下:
[—>PowerManagerService.java:init]
nativeInit();//不知道此处为何还要调用一次nativeInit,笔者怀疑此处为bug
synchronized(mLocks){
updateNativePowerStateLocked();//更新native层power状态,以后分析
forceUserActivityLocked();//强制触发一次用户事件
mInitialized=true;
}//init函数完毕
forceUserActivityLocked表示强制触发一次用户事件。这个解释是否会让读者丈二和尚摸不着头?先来看它的代码:
[—>PowerManagerService.java:forceUserActivityLocked]
private void forceUserActivityLocked(){
if(isScreenTurningOffLocked()){
mScreenBrightness.animating=false;
}
boolean savedActivityAllowed=mUserActivityAllowed;
mUserActivityAllowed=true;
//下面这个函数以后会分析,SDK中有对应的API
userActivity(SystemClock.uptimeMillis(),false);
mUserActivityAllowed=savedActivityAllowed;
}
forceUserActivityLocked内部为调用userActivity扫清了一切障碍。SDK中PowerManager.userActivity的说明文档“User activity happened.Turns the device from whatever state it’s in to full on, and resets the auto-off timer.”简单翻译过来是:调用此函数后,手机将被唤醒,屏幕超时时间也将重新计算。
userActivity是PMS中很重要的一个函数,本章后面将对其进行详细分析。
4.init函数总结
PMS的init函数比较简单,但是其众多的成员变量让人感到有点头晕。读者自行阅读代码时,不妨参考表5-1和表5-2。
[1]config.xml文件的全路径是Android 4.0源码中的/frameworks/base/core/res/res/values/config.xml。