第四章:Android灯光系统(6)-背光灯

上小节我们实现了对通知灯控制,该小节我们讲解怎么实现对背光灯的控制,一般来说,操作是比较简单的,我们只需要往数据库中写入亮度就可以了,那么为什么会这么简单,当然是有其他的程序(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的监听方法,如下:

猜你喜欢

转载自blog.csdn.net/weixin_43013761/article/details/87456707