上小节我们实现了对通知灯控制,该小节我们讲解怎么实现对背光灯的控制,一般来说,操作是比较简单的,我们只需要往数据库中写入亮度就可以了,那么为什么会这么简单,当然是有其他的程序(ContentObserber)在监视数据库,当数据改变的时候,检测的程序,就会相应的去改变backlight的亮度。那么我们先来分析一下源码是怎么实现的
源码分析
倒序分析
还是根据lights.h文件中的
#define LIGHT_ID_BACKLIGHT "backlight"
在源码中搜索LIGHT_ID_BACKLIGHT,我们可以找到文件LocalDisplayAdapter.java
LightsManager lights = LocalServices.getService(LightsManager.class);
mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
与通知灯类似,先通过LocalServices.getService获取灯光服务,在通过灯光服务
lights.getLight获得一个Light类的实例化mBacklight,然后通过mBacklight我们就能实现所有对背光灯操作了。在文件中查找mBacklight被引用的地方,我们可以找到:
private void setDisplayBrightness(int brightness) {
if (DEBUG) {
Slog.d(TAG, "setDisplayBrightness("
+ "id=" + displayId + ", brightness=" + brightness + ")");
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
+ "id=" + displayId + ", brightness=" + brightness + ")");
try {
mBacklight.setBrightness(brightness);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
该为一个私有方法,只能在本类中使用,其中在 run()方法中被调用,看到run方法,我们知道这是一个线程,那么我们看看该线程做了那么事情,一般来说大部分都在休眠状态,只有在我们调节了屏幕亮度的时候他才会有所动作,其中有代码如下:
if (Display.isSuspendedState(oldState){......}
if (brightnessChanged) {
setDisplayBrightness(brightness);
}
// Enter the final desired state, possibly suspended.
if (state != currentState) {
setDisplayState(state);
}
可以看出,当背光灯改变的时候,就会调动 setDisplayBrightness(brightness)方法,方法会调用JNI访问C函数,最终实现对驱动的控制。下面我们从上层开始往底层分析,scheduleScreenUpdate
顺序分析
打开PowerManagerService.java文件,在其中搜索ContentObserver(内容观察),我们可以发现调用了很多次resolver.registerContentObserver()与mContext.registerReceiver()方法如下:
mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
resolver.registerContentObserver(SCREENSAVER_ENABLED)
resolver.registerContentObserver(SCREENSAVER_ACTIVATE_ON_SLEEP),
resolver.registerContentObserver(SCREENSAVER_ACTIVATE_ON_DOCK),
resolver.registerContentObserver(SCREEN_OFF_TIMEOUT),
....................................................
我们先分析 mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler),注册了一个电池监听,当电池发生时,执行BatteryReceiver方法中的onReceive方法内部调用过程如下:
BatteryReceiver():
onReceive()
handleBatteryStateChangedLocked()
updatePowerStateLocked();
DreamReceiver():该方法最终发送一个消息,然后handleMessage方法进行处理
DreamReceiver()
scheduleSandmanLocked();
sendMessage(msg)
handleMessage(Message msg)
handleSandman();
updatePowerStateLocked();
UserSwitchedReceiver():
UserSwitchedReceiver():
handleSettingsChangedLocked();
updateSettingsLocked();
DockReceiver()
DockReceiver()
updatePowerStateLocked();
通过上面的分析,我们很直观的了解到,mContext.registerReceiver注册的4个监听,到最后都是调用updatePowerStateLocked()方法,
下面我们分析resolver.registerContentObserver,假设我们注册SCREENSAVER_ENABLED之后,对应的
mSettingsObserver中某些方法被调用,如onChange:
onChange()
handleSettingsChangedLocked();
updatePowerStateLocked();
可以看到,最终函数调用了updatePowerStateLocked(),为了大家直观的分析,有如下截图:
那么我们现在来分析一下updatePowerStateLocked()方法:
updatePowerStateLocked()
// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
updateStayOnLocked(mDirty);
updateScreenBrightnessBoostLocked(mDirty);
// Phase 1: Update wakefulness.
// Loop because the wake lock and user activity computations are influenced
// by changes in wakefulness.
final long now = SystemClock.uptimeMillis();
int dirtyPhase2 = 0;
for (;;) {
int dirtyPhase1 = mDirty;
dirtyPhase2 |= dirtyPhase1;
mDirty = 0;
updateWakeLockSummaryLocked(dirtyPhase1);
updateUserActivitySummaryLocked(now, dirtyPhase1);
if (!updateWakefulnessLocked(dirtyPhase1)) {
break;
}
}
// Phase 2: Update display power state.
boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);
// Phase 3: Update dream state (depends on display ready signal).
updateDreamLocked(dirtyPhase2, displayBecameReady);
// Phase 4: Send notifications, if needed.
finishWakefulnessChangeIfNeededLocked();
// Phase 5: Update suspend blocker.
// Because we might release the last suspend blocker here, we need to make sure
// we finished everything else first!
updateSuspendBlockerLocked();
他做了五个步骤,但是现在我们只关注Phase 2: Update display power state.,即我们的显示器部分,updateDisplayPowerStateLocked方法,调用过程如下
updateDisplayPowerStateLocked()
mDisplayManagerInternal.requestPowerState()
其中mDisplayManagerInternal是DisplayManagerInternal(抽象类)的实例化,我们在源码中进行搜索,看他是被如何实现的,最终我们锁定DisplayManagerService.java文件,查看代码,可知由LocalService继承实现。可以在其中找到代码:
private DisplayPowerController mDisplayPowerController;
public boolean requestPowerState(DisplayPowerRequest request,
boolean waitForNegativeProximity) {
return mDisplayPowerController.requestPowerState(request,
waitForNegativeProximity);
}
我们在DisplayPowerController.java文件中可以找到requestPowerState的定义,
通过前面的分析现在我们来住一个总结:PowerManagerService是一个总入口,背光灯,电池等等和电源相关的,都从这里开始,其中我们的Display仅仅是其中的一部分,DisplayPowerController就是我们显示器电源的相关控制(即显示器电源管理),下面我们将对他进行分析。
DisplayPowerController分析
在其私有成员中,我们可以看到很多私有成员(只列举部分):
private final IBatteryStats mBatteryStats;
private final SensorManager mSensorManager;
......
从上面我们可以看到一个mSensorManager的成员,在我们打电话的时候,如果靠近耳朵,屏幕会自动熄灭,那么他肯定存在相关的传感器。当我们电池电量过低的时候,会发出通知询问我们是否降低屏幕的亮度。这些成员都是为了和我们屏幕的亮度进行配合使用,前面提到会调用requestPowerState()方法,我们现在看看做了哪些工作,
requestPowerState()
sendUpdatePowerStateLocked();
mHandler.sendMessage(msg);
updatePowerState();
animateScreenBrightness() //慢慢的改变屏幕的变化
mScreenBrightnessRampAnimator.animateTo() //于 RampAnimator.java文件定义
查看源码,我们可以知道,requestPowerState()最终发送一个消息,然后由updatePowerState()处理,下面我来查看:
mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
在源码中搜索SCREEN_BRIGHTNESS然后锁定DisplayPowerState.java 文件,找到setScreenBrightness方法
setScreenBrightness()
scheduleScreenUpdate()
scheduleScreenUpdate函数最终通过Handler发送mScreenUpdateRunnable对象来更新亮度值. 从run函数中可以看出只有当mColorFadeLevel > 0f时才能给brightness设置亮度值,.
private final Runnable mScreenUpdateRunnable = new Runnable() {
@Override
public void run() {
mScreenUpdatePending = false;
int brightness = mScreenState != Display.STATE_OFF
&& mColorFadeLevel > 0f ? mScreenBrightness : 0; //判断设置亮度值
if (mPhotonicModulator.setState(mScreenState, brightness)) {
if (DEBUG) {
Slog.d(TAG, "Screen ready");
}
mScreenReady = true;
invokeCleanListenerIfNeeded();
} else {
if (DEBUG) {
Slog.d(TAG, "Screen not ready");
}
}
}
};
之后调用DisplayManagerService中DisplayBlanker的requestDisplayState函数.
DisplayBlanker blanker = new DisplayBlanker() {
@Override
public void requestDisplayState(int state, int brightness) {
// The order of operations is important for legacy reasons.
if (state == Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness);
}
callbacks.onDisplayStateChange(state);
if (state != Display.STATE_OFF) {
requestGlobalDisplayStateInternal(state, brightness); //亮屏调用设置状态,亮度
}
}
};
private void requestGlobalDisplayStateInternal(int state, int brightness) {
if (state == Display.STATE_UNKNOWN) {
state = Display.STATE_ON;
}
if (state == Display.STATE_OFF) {
brightness = PowerManager.BRIGHTNESS_OFF; //灭屏设置屏幕亮度为0
} else if (brightness < 0) {
brightness = PowerManager.BRIGHTNESS_DEFAULT; //屏幕亮度小于0,设置为默认亮度
} else if (brightness > PowerManager.BRIGHTNESS_ON) {
brightness = PowerManager.BRIGHTNESS_ON; //屏幕亮度大于255设置最大亮度值255
}
synchronized (mTempDisplayStateWorkQueue) {
try {
// Update the display state within the lock.
// Note that we do not need to schedule traversals here although it
// may happen as a side-effect of displays changing state.
synchronized (mSyncRoot) {
if (mGlobalDisplayState == state
&& mGlobalDisplayBrightness == brightness) {
return; // no change 亮度与状态都没有改变就return
}
Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestGlobalDisplayState("
+ Display.stateToString(state)
+ ", brightness=" + brightness + ")");
mGlobalDisplayState = state;
mGlobalDisplayBrightness = brightness;
applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue); //应用全局状态
}
// Setting the display power state can take hundreds of milliseconds
// to complete so we defer the most expensive part of the work until
// after we have exited the critical section to avoid blocking other
// threads for a long time.
for (int i = 0; i < mTempDisplayStateWorkQueue.size(); i++) {
mTempDisplayStateWorkQueue.get(i).run(); //运行mTempDisplayStateWorkQueue队列中的runnable
}
Trace.traceEnd(Trace.TRACE_TAG_POWER);
} finally {
mTempDisplayStateWorkQueue.clear();
}
}
}
在applyGlobalDisplayStateLocked函数中获取所有的devices, 调用对应设备的requestDisplayStateLocked函数更新请求状态. 启动devices为LocalDisplayAdapter, 就会调用到该类的requestDisplayStateLocked获得runnable.
requestGlobalDisplayStateInternal() //于DisplayManagerService.java文件定义
applyGlobalDisplayStateLocked() //于DisplayManagerService.java文件定义
updateDisplayStateLocked() //于DisplayManagerService.java文件定义
requestDisplayStateLocked() //于LocalDisplayAdapter.java文件定义
run() //于LocalDisplayAdapter.java文件定义
setDisplayBrightness() //于LocalDisplayAdapter.java文件定义
setBrightness(brightness) //于BrightnessController.java文件定义
这样就和我们之前的倒序分析联系到一起了,实现对背光灯的控制。明白了之后,我们开始在原来APP的基础上进行修改,实现对backlight的控制
APP编写
在原来的AS工程上进行修改。
界面设计
首先我们要在原来的基础上增加一个小部件,该部件用来控制背光灯,该小部件为SeekBar(拖动条),在activity_main.xml文件中,添加如下代码:
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:orientation="vertical"
+ <SeekBar
+ android:id="@+id/seekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"/>
这样我们的Seekbar的界面就设计完成了如下:
下面我们将编写APP应用程序
APP应用程序
首先我们在MainActivity.java文件的class MainActivity中,定义一个私有成员:
+ private SeekBar mBackligtSeekBar = null;
然后我们在onCreate()方法中,对他进行实例化与我们界面设计的SeekBar绑定在一起。
+ mBackligtSeekBar = (SeekBar)findViewById(R.id.seekBar);
然后我们就需要实现Seekbar的监听方法,如下: