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所示。

5.2.2 init分析 - 图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所示。

figure_0161_0047

除了获取配置参数外,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。