ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
第二个关键点是init函数,该函数将初始化PMS内部的一些重要成员变量,由于此函数代码较长,此处将分段讨论。 从流程角度看,init大体可分为三段。 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层的电源状态 } ~~~ 第一阶段工作可分为三步: - 对一些成员变量进行赋值。 - 调用nativeInit函数初始化Native层相关资源。 - 调用updateNativePowerStateLocked更新Native层的电源状态。这个函数的调用次数较为频繁,以后续分析时讨论。 先来看第一阶段出现的各类成员变量,如表5-1所示。 :-: 表5-1 成员变量说明 | 成员变量名 | 数据类型 | 作用 | | --- | --- | --- | | mLightsService | LightsService| 和LightsService交互用 | |mActivityService | IActivityManager | 和ActivityManagerService交互 | | mBatteryStats | IBatteryStats | 和BatteryStatsService交互,用于系统耗电量统计方面的工作 | | mBatteryService | BatteryService | 用于获取电源状态,例如是否为低电状态、查询电池电量等 | | mLcdLight、mButtonLight、mKeyboardLight、mAttentionLight| LightsService.Light| 由PMS控制,在不同状态下点亮或熄灭它们 | 下面来看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") { protectedvoid onLooperPrepared() { super.onLooperPrepared(); initInThread();//①初始化另外一些成员变量 } }; mHandlerThread.start(); ......//等待mHandlerThread创建完成 ~~~ 由于mHandlerThread承担了PMS的主要工作任务,因此需要先做一些初始化工作,相关的代码在initInThread中,拟放在单独一节中进行讨论。 (2) initInThread分析 initInThread本身比较简单,涉及三个方面的工作,总结如下: - PMS需要了解外面的世界,所以它会注册一些广播接收对象,接收诸如启动完毕、电池状态变化等广播。 - PMS所从事的电源管理工作需要遵守一定的规则,而这些规则在代码中就是一些配置参数,这些配置参数的值可以是固定写死的(编译完后就无法改动),也可以是经由Settings数据库动态设定的。 - PMS需要对外发出一些通知,例如屏幕关闭/屏幕开启。 了解initInThread的概貌后,再来看如下代码。 **PowerManagerService.java::initInThread** ~~~ void initInThread() { mHandler= new Handler(); //PMS内部也需要使用WakeLock,此处定义了几种不同的UnsynchronizedWakeLock。它们的 //作用见后文分析 mBroadcastWakeLock = newUnsynchronizedWakeLock( 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); //取配置参数,这些参数是编译时确定的,运行过程中无法修改 Resourcesresources = 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所示。 :-: 表5-2 PMS使用的配置参数 | 参数名:类型 | 来源 | 备注 | | --- | --- | --- | | mAnimateScreenLights:bool | config.xml[^write]| 关屏时屏幕光是否渐暗,默认为true | | mUnplugTurnsOnScreen:bool | config.xml | 拔掉USB线,是否点亮屏幕 | | mScreenBrightnessDim:int | config.xml | PMS可设置的屏幕亮度的最小值,默认20(单位lx) | | mUseSoftwareAutoBrightness:bool | config.xml | 是否启用Setting中的亮度自动调节,如果硬件不支持该功能,则可由软件控制。默认为false | |mAutoBrightnessLevels:int[]、mLcdBacklightValues:int[] 、…… | config.xml,具体值由硬件厂商定义 | 当使用软件自动亮度调节时,需配置不同亮度时对应的参数 | | STAY_ON_WHILE_PLUGGED_IN:int | Settings.db | 插入USB时是否保持唤醒状态 | | SCREEN_OFF_TIMEOUT:int | Settings.db | 屏幕超时时间 | | DIM_SCREEN:int | Settings.db |是否变暗(dim)屏幕 | | SCREEN_BRIGHTNESS_MODE:int | Settings.db |屏幕亮度模式(自动还是手动调节) | 除了获取配置参数外,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.Turnsthe device from whatever state it's in to full on, and resets the auto-offtimer.”简单翻译过来是:调用此函数后,手机将被唤醒。屏幕超时时间将重新计算。 userActivity是PMS中很重要的一个函数,本章后面将对其进行详细分析。 4. init函数总结 PMS的init函数比较简单,但是其众多的成员变量让人感到有点头晕。读者自行阅读代码时,不妨参考表5-1和表5-2。 [^write]: config.xml文件的全路径是4.0源码/frameworks/base/core/res/res/values/config.xml。