Android7.0 PowerManagerService亮灭屏分析(二)

在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerRequest中传入DisplayPowerController中进一步处理。在亮屏过程中DisplayPowerController会根据传过来的数值来设置新的电源状态为亮屏,然后调用DisplayPowerState来对状态进行设置,在分析DisplayPowerState时会具体讲解。由于此时ColorFade level(就是在手机屏幕的一层surface,当level为0是为一层黑帧,level为1.0时为透明)的值为0,表示屏幕还没有绘制好,所以此时需要block screen直到window界面绘制完成。当需要亮屏时调用PhoneWindowManager的screenTurningOn函数,通知window屏幕就要点亮了。然后调用WMS中函数waitForAllWindowsDrawn函数等待将所有需要绘制的window绘制完成后回调回来,超时时间为1000ms。在WMS中获取需要绘制的window将其加入mWaitingForDrawn中等待绘制,通过检查mWaitingForDrawn是否为空来判断,window是否绘制完成。此时screenTurningOn函数就执行完了,剩下的就是等待windows绘制完成。


DisplayManagerService.java中的LocalService继承了DisplayManagerInternal.java, 所以就会调用LocalService的requestPowerState函数请求电源状态. 

[java]  view plain  copy
  1. private final class LocalService extends DisplayManagerInternal {  
  2.     @Override  
  3.     public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,  
  4.             SensorManager sensorManager) {    //初始化函数在PowerManagerService的systemReady函数中调用  
  5.         synchronized (mSyncRoot) {  
  6.             DisplayBlanker blanker = new DisplayBlanker() {   //创建blanker对象  
  7.                 @Override  
  8.                 public void requestDisplayState(int state, int brightness) {  
  9.                     // The order of operations is important for legacy reasons.  
  10.                     if (state == Display.STATE_OFF) {  
  11.                         requestGlobalDisplayStateInternal(state, brightness);  
  12.                     }  
  13.   
  14.                     callbacks.onDisplayStateChange(state);  
  15.   
  16.                     if (state != Display.STATE_OFF) {  
  17.                         requestGlobalDisplayStateInternal(state, brightness);  
  18.                     }  
  19.                 }  
  20.             };  
  21.             mDisplayPowerController = new DisplayPowerController(    //创建DisplayPowerController对象  
  22.                     mContext, callbacks, handler, sensorManager, blanker);  
  23.         }  
  24.     }  
  25.   
  26.     @Override  
  27.     public boolean requestPowerState(DisplayPowerRequest request,  
  28.             boolean waitForNegativeProximity) {  
  29.         return mDisplayPowerController.requestPowerState(request,  
  30.                 waitForNegativeProximity);     //调用DisplayPowerController请求电源状态  
  31.     }  
  32.    //.....  

    在DisplayPowerController中通过Handler发送MSG_UPDATE_POWER_STATE消息调用updatePowerState来更新电源状态. updatePowerState也是一个非常核心的函数,在该函数中将PowerManagerService中传过来的参数获取出来根据policy获取设备的请求状态是ON, OFF还是DOZE, 应用距离传感器, 将最终的请求状态调用DisplayPowerState进行设置. 判断设备是否打开了自动亮度调节, 计算最终屏幕要达到的目标亮度并通过亮度渐变动画慢慢的点亮屏幕,最终变为高亮.

[java]  view plain  copy
  1. private void updatePowerState() {  
  2.     // Update the power state request.  
  3.     final boolean mustNotify;  
  4.     boolean mustInitialize = false;  
  5.     boolean autoBrightnessAdjustmentChanged = false;  
  6.   
  7.     synchronized (mLock) {  
  8.         mPendingUpdatePowerStateLocked = false;  
  9.         if (mPendingRequestLocked == null) {  
  10.             return// wait until first actual power request  
  11.         }  
  12.   
  13.         if (mPowerRequest == null) { //判断mPowerRequest是否为NULL,如果为空新创建一个, 否则直接将mPendingRequestLocked复制给他    
  14.             mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);  
  15.             mWaitingForNegativeProximity = mPendingWaitForNegativeProximityLocked;  
  16.             mPendingWaitForNegativeProximityLocked = false;  
  17.             mPendingRequestChangedLocked = false;  
  18.             mustInitialize = true;  
  19.         } else if (mPendingRequestChangedLocked) {  
  20.             autoBrightnessAdjustmentChanged = (mPowerRequest.screenAutoBrightnessAdjustment  
  21.                     != mPendingRequestLocked.screenAutoBrightnessAdjustment);  
  22.             mPowerRequest.copyFrom(mPendingRequestLocked);  
  23.             mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;  
  24.             mPendingWaitForNegativeProximityLocked = false;  
  25.             mPendingRequestChangedLocked = false;  
  26.             mDisplayReadyLocked = false;  
  27.         }  
  28.   
  29.         mustNotify = !mDisplayReadyLocked;  
  30.     }  
  31.   
  32.     // Initialize things the first time the power state is changed.  
  33.     if (mustInitialize) {  
  34.         initialize();//power state第一次发生变化时需要做一些初始化,如:创建DisplayPowerState, 亮灭屏动画,亮度渐变动画实例等   
  35.     }  
  36.   
  37.     // Compute the basic display state using the policy.  
  38.     // We might override this below based on other factors.  
  39.     int state;  
  40.     int brightness = PowerManager.BRIGHTNESS_DEFAULT;//设置屏幕默认亮度  
  41.     boolean performScreenOffTransition = false;  
  42.     switch (mPowerRequest.policy) {  
  43.         case DisplayPowerRequest.POLICY_OFF: //设置设备状态为OFF   
  44.             state = Display.STATE_OFF;  
  45.             performScreenOffTransition = true//灭屏有过渡动画    
  46.             break;  
  47.         case DisplayPowerRequest.POLICY_DOZE:  //设置设备状态为DOZE  
  48.             if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {  
  49.                 state = mPowerRequest.dozeScreenState;  
  50.             } else {  
  51.                 state = Display.STATE_DOZE;  
  52.             }  
  53.             if (!mAllowAutoBrightnessWhileDozingConfig) {  
  54.                 brightness = mPowerRequest.dozeScreenBrightness;//如果在xml文件中配置了相关属性,就将dozeScreenBrightness赋给亮度值  
  55.             }  
  56.             break;  
  57.         case DisplayPowerRequest.POLICY_DIM:  
  58.         case DisplayPowerRequest.POLICY_BRIGHT:  
  59.         default:  
  60.             state = Display.STATE_ON;//如果policy为DIM, BRIGHT的话,设备状态都为ON  
  61.             break;  
  62.     }  
  63.     assert(state != Display.STATE_UNKNOWN);  
  64.   
  65.     // Apply the proximity sensor.  
  66.     if (mProximitySensor != null) {  //距离传感器相关处理  
  67.         if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {  
  68.             setProximitySensorEnabled(true);  
  69.             if (!mScreenOffBecauseOfProximity  
  70.                     && mProximity == PROXIMITY_POSITIVE) {  
  71.                 mScreenOffBecauseOfProximity = true;  
  72.                 sendOnProximityPositiveWithWakelock();  
  73.             }  
  74.         } else if (mWaitingForNegativeProximity  
  75.                 && mScreenOffBecauseOfProximity  
  76.                 && mProximity == PROXIMITY_POSITIVE  
  77.                 && state != Display.STATE_OFF) {  
  78.             setProximitySensorEnabled(true);  
  79.         } else {  
  80.             setProximitySensorEnabled(false);  
  81.             mWaitingForNegativeProximity = false;  
  82.         }  
  83.         if (mScreenOffBecauseOfProximity  
  84.                 && mProximity != PROXIMITY_POSITIVE) {  
  85.             mScreenOffBecauseOfProximity = false;  
  86.             sendOnProximityNegativeWithWakelock();  
  87.         }  
  88.     } else {  
  89.         mWaitingForNegativeProximity = false;  
  90.     }  
  91.     if (mScreenOffBecauseOfProximity) {  
  92.         state = Display.STATE_OFF;//如果因为距离传感器灭屏设置设备状态为OFF  
  93.     }  
  94.   
  95.     // Animate the screen state change unless already animating.  
  96.     // The transition may be deferred, so after this point we will use the  
  97.     // actual state instead of the desired one.  
  98.     animateScreenStateChange(state, performScreenOffTransition);//处理屏幕状态改变  
  99.     state = mPowerState.getScreenState();//获取新的屏幕状态   
  100.   
  101.     // Use zero brightness when screen is off.  
  102.     if (state == Display.STATE_OFF) {  
  103.         brightness = PowerManager.BRIGHTNESS_OFF; //如果新的状态是灭屏的话设置brightness为0  
  104.     }  
  105.   
  106.     // Configure auto-brightness.  
  107.     boolean autoBrightnessEnabled = false;  
  108.     if (mAutomaticBrightnessController != null) {  
  109.         final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig  
  110.                 && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND);  
  111.         autoBrightnessEnabled = mPowerRequest.useAutoBrightness  
  112.                 && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)  
  113.                 && brightness < 0;  
  114.         final boolean userInitiatedChange = autoBrightnessAdjustmentChanged  
  115.                 && mPowerRequest.brightnessSetByUser;  
  116.         mAutomaticBrightnessController.configure(autoBrightnessEnabled,  
  117.                 mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON,  
  118.                 userInitiatedChange, mPowerRequest.useTwilight);//配置控制自动亮度设置的对象  
  119.     }  
  120.   
  121.     // Apply brightness boost.  
  122.     // We do this here after configuring auto-brightness so that we don't  
  123.     // disable the light sensor during this temporary state.  That way when  
  124.     // boost ends we will be able to resume normal auto-brightness behavior  
  125.     // without any delay.  
  126.     if (mPowerRequest.boostScreenBrightness  
  127.             && brightness != PowerManager.BRIGHTNESS_OFF) {  
  128.         brightness = PowerManager.BRIGHTNESS_ON;  
  129.     }  
  130.   
  131.     // Apply auto-brightness.  
  132.     boolean slowChange = false;  
  133.     if (brightness < 0) {  
  134.         if (autoBrightnessEnabled) {  
  135.             brightness = mAutomaticBrightnessController.getAutomaticScreenBrightness();//获取自动亮度值  
  136.         }  
  137.         if (brightness >= 0) {  
  138.             // Use current auto-brightness value and slowly adjust to changes.  
  139.             brightness = clampScreenBrightness(brightness);//保证亮度值的有效性,不能在0~255之外  
  140.             if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {  
  141.                 slowChange = true// slowly adapt to auto-brightness  
  142.             }  
  143.             mAppliedAutoBrightness = true;//使用了自动亮度  
  144.         } else {  
  145.             mAppliedAutoBrightness = false;//没有使用自动亮度  
  146.         }  
  147.     } else {  
  148.         mAppliedAutoBrightness = false;  
  149.     }  
  150.   
  151.     // Use default brightness when dozing unless overridden.  
  152.     if (brightness < 0 && (state == Display.STATE_DOZE  
  153.             || state == Display.STATE_DOZE_SUSPEND)) {  
  154.         brightness = mScreenBrightnessDozeConfig;  
  155.     }  
  156.   
  157.     // Apply manual brightness.  
  158.     // Use the current brightness setting from the request, which is expected  
  159.     // provide a nominal default value for the case where auto-brightness  
  160.     // is not ready yet.  
  161.     if (brightness < 0) {  
  162.         brightness = clampScreenBrightness(mPowerRequest.screenBrightness);//如果亮度值小于0的话,就是用setting中设置的亮度值  
  163.     }  
  164.   
  165.     // Apply dimming by at least some minimum amount when user activity  
  166.     // timeout is about to expire.  
  167.     if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {  
  168.         if (brightness > mScreenBrightnessRangeMinimum) {  
  169.             brightness = Math.max(Math.min(brightness - SCREEN_DIM_MINIMUM_REDUCTION,  
  170.                     mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum);  
  171.         }  
  172.         if (!mAppliedDimming) {  
  173.             slowChange = false;  
  174.         }  
  175.         mAppliedDimming = true;  
  176.     } else if (mAppliedDimming) {  
  177.         slowChange = false;  
  178.         mAppliedDimming = false;  
  179.     }  
  180.   
  181.     // If low power mode is enabled, cut the brightness level by half  
  182.     // as long as it is above the minimum threshold.  
  183.     if (mPowerRequest.lowPowerMode) {  
  184.         if (brightness > mScreenBrightnessRangeMinimum) {  
  185.             brightness = Math.max(brightness / 2, mScreenBrightnessRangeMinimum);//低电模式, 亮度是目标亮度的1/2  
  186.         }  
  187.         if (!mAppliedLowPower) {  
  188.             slowChange = false;  
  189.         }  
  190.         mAppliedLowPower = true;  
  191.     } else if (mAppliedLowPower) {  
  192.         slowChange = false;  
  193.         mAppliedLowPower = false;  
  194.     }  
  195.   
  196.     // Animate the screen brightness when the screen is on or dozing.  
  197.     // Skip the animation when the screen is off or suspended.  
  198.     if (!mPendingScreenOff) {  
  199.         if (state == Display.STATE_ON || state == Display.STATE_DOZE) {  
  200.             animateScreenBrightness(brightness,  
  201.                     slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : mBrightnessRampRateFast);//使用亮度渐变动画设置亮度, 渐变速度依赖于rate  
  202.         } else {  
  203.             animateScreenBrightness(brightness, 0);//动画速率为0, 没有动画  
  204.         }  
  205.     }  
  206.     //..........  
  207. }  

根据上面代码分析在updatePowerState中主要做的事情主要有两件事:
1.调用animateScreenStateChange函数处理屏幕状态变化
2.调用animateScreenBrightness函数设置屏幕亮度
下面将详细讲解这两件事情: 
animateScreenStateChange函数处理屏幕状态变化

[java]  view plain  copy
  1. private void animateScreenStateChange(int target, boolean performScreenOffTransition) {  
  2.     // If there is already an animation in progress, don't interfere with it.  
  3.     if (mColorFadeOnAnimator.isStarted()      
  4.             || mColorFadeOffAnimator.isStarted()) {  
  5.         if (target != Display.STATE_ON) {  
  6.             return;    //如果此时有亮灭屏动画, 并且目标状态不是ON,就返回.  
  7.         }  
  8.         // If display state changed to on, proceed and stop the color fade and turn screen on.  
  9.         mPendingScreenOff = false;  
  10.     }  
  11.   
  12.     // If we were in the process of turning off the screen but didn't quite  
  13.     // finish.  Then finish up now to prevent a jarring transition back  
  14.     // to screen on if we skipped blocking screen on as usual.  
  15.     if (mPendingScreenOff && target != Display.STATE_OFF) {  
  16.         setScreenState(Display.STATE_OFF);  
  17.         mPendingScreenOff = false;  
  18.         mPowerState.dismissColorFadeResources();  
  19.     }  
  20.   
  21.     if (target == Display.STATE_ON) {    //亮屏  
  22.         // Want screen on.  The contents of the screen may not yet  
  23.         // be visible if the color fade has not been dismissed because  
  24.         // its last frame of animation is solid black.  
  25.         if (!setScreenState(Display.STATE_ON)) {    //设置屏幕状态,如果返回false就blocked亮屏, 不能往下执行  
  26.             return// screen on blocked  
  27.         }  
  28.         if (USE_COLOR_FADE_ON_ANIMATION && mPowerRequest.isBrightOrDim()) {  
  29.             // Perform screen on animation.       
  30.             if (mPowerState.getColorFadeLevel() == 1.0f) {   //执行亮屏动画, 将Color Fade level渐变为1.0f  
  31.                 mPowerState.dismissColorFade();  
  32.             } else if (mPowerState.prepareColorFade(mContext,  
  33.                     mColorFadeFadesConfig ?  
  34.                             ColorFade.MODE_FADE :  
  35.                                     ColorFade.MODE_WARM_UP)) {  
  36.                 mColorFadeOnAnimator.start();  
  37.             } else {  
  38.                 mColorFadeOnAnimator.end();  
  39.             }  
  40.         } else {  
  41.             // Skip screen on animation.  
  42.             mPowerState.setColorFadeLevel(1.0f);   //跳过亮屏, 直接设置Color Fade level为1.0f  
  43.             mPowerState.dismissColorFade();  
  44.         }  
  45.       //...........  
  46.     } else {  
  47.         // Want screen off.   //灭屏  
  48.         mPendingScreenOff = true;  
  49.         if (mPowerState.getColorFadeLevel() == 0.0f) {  
  50.             // Turn the screen off.  
  51.             // A black surface is already hiding the contents of the screen.  
  52.             setScreenState(Display.STATE_OFF);   //如果Color Fade level为0.0f, 设置屏幕状态为OFF  
  53.             mPendingScreenOff = false;  
  54.             mPowerState.dismissColorFadeResources();  
  55.         } else if (performScreenOffTransition  //performScreenOffTransition为true, 有灭屏过渡动画  
  56.                 && mPowerState.prepareColorFade(mContext,  
  57.                         mColorFadeFadesConfig ?  
  58.                                 ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)  
  59.                 && mPowerState.getScreenState() != Display.STATE_OFF) {  
  60.             // Perform the screen off animation.  
  61.             mColorFadeOffAnimator.start();    //灭屏动画,Color fade leve慢慢将1.0变为0.0f  
  62.         } else {  
  63.             // Skip the screen off animation and add a black surface to hide the  
  64.             // contents of the screen.  
  65.             mColorFadeOffAnimator.end();     //跳过灭屏动画  
  66.         }  
  67.     }  
  68. }  

    在上面函数中主要就是调用setScreenState设置屏幕状态, 只有设置好状态并且setScreenState返回true,之后才会判断是否有亮度渐变动画将Color Fade Level设置为1.0f. 否则一直会阻塞在setScreenState函数上.

[java]  view plain  copy
  1. private boolean setScreenState(int state) {  
  2.     if (mPowerState.getScreenState() != state) {  
  3.         final boolean wasOn = (mPowerState.getScreenState() != Display.STATE_OFF);  
  4.         mPowerState.setScreenState(state);   //调用DisplayPowerState设置屏幕状态  
  5.   
  6.         // Tell battery stats about the transition.  
  7.         try {  
  8.             mBatteryStats.noteScreenState(state);  
  9.         } catch (RemoteException ex) {  
  10.             // same process  
  11.         }  
  12.     }  
  13.   
  14.     // Tell the window manager policy when the screen is turned off or on unless it's due  
  15.     // to the proximity sensor.  We temporarily block turning the screen on until the  
  16.     // window manager is ready by leaving a black surface covering the screen.  
  17.     // This surface is essentially the final state of the color fade animation and  
  18.     // it is only removed once the window manager tells us that the activity has  
  19.     // finished drawing underneath.  
  20.     final boolean isOff = (state == Display.STATE_OFF);   //是否是灭屏状态  
  21.     if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF  
  22.             && !mScreenOffBecauseOfProximity) {  
  23.         mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_OFF;  
  24.         unblockScreenOn();  
  25.         mWindowManagerPolicy.screenTurnedOff();  
  26.         PMSFactory.getInstance().createExtraDisplayPowerState(mContext).setButtonOn(false);  
  27.     } else if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) {  
  28.         mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_TURNING_ON;  
  29.         if (mPowerState.getColorFadeLevel() == 0.0f) {    //亮屏处理, 此时Colorfade level为0.0f 为true  
  30.             blockScreenOn();        //block亮屏, 创建一个mPendingScreenOnUnblocker对象  
  31.         } else {  
  32.             unblockScreenOn(); //将创建的mPendingScreenOnUnblocker对象销毁,设置NULL  
  33.         }  
  34.         mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker);    //调用PhoneWindowManager进行处理  
  35.         if (PMSFactory.getInstance().createExtraDisplayPowerController(mContext).getButtonTimeout() != -2){  
  36.             PMSFactory.getInstance().createExtraDisplayPowerState(mContext).setButtonOn(true);  
  37.         }  
  38.     }  
  39.   
  40.     // Return true if the screen isn't blocked.  
  41.     return mPendingScreenOnUnblocker == null;   //blockScreenOn时创建一个mPendingScreenOnUnblocker对象,所以返回false, block亮屏  
  42. }  
[java]  view plain  copy
  1. private void blockScreenOn() {  
  2.     if (mPendingScreenOnUnblocker == null) {  
  3.         Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);  
  4.         mPendingScreenOnUnblocker = new ScreenOnUnblocker();  
  5.         mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();   //开始blocked亮屏的时间  
  6.         Slog.i(TAG, "Blocking screen on until initial contents have been drawn.");  
  7.     }  
  8. }  
  9.   
  10. private void unblockScreenOn() {  
  11.     if (mPendingScreenOnUnblocker != null) {  
  12.         mPendingScreenOnUnblocker = null;  
  13.         long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;  
  14.         Slog.i(TAG, "Unblocked screen on after " + delay + " ms");   //输出log,一共block亮屏多长时间  
  15.         Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);  
  16.     }  
  17. }  

在setScreenState函数中主要做了两件事情:
1.调用DisplayPowerState对屏幕状态进行设置
2.通知PhoneWindowManager对keyguard和windows进行绘制.
设置屏幕状态


    当设置屏幕状态时,如果新状态与之前的状态不同时就会将新的状态值记录在成员变量mScreenState中,这样可以让其它类来获取当前的屏幕状态。之后在一个Runnable对象的run函数中根据屏幕状态与ColorFade level来进行获取屏幕亮度,此时是亮屏操作但是windows尚未绘制完成level仍然是0.0,此时获取的亮度值为0,由此可知,只要windows绘制不完成亮度值会一直为0,即屏幕不会亮。

[java]  view plain  copy
  1. private final Runnable mScreenUpdateRunnable = new Runnable() {  
  2.     @Override  
  3.     public void run() {  
  4.         mScreenUpdatePending = false;  
  5.   
  6.         int brightness = mScreenState != Display.STATE_OFF  
  7.                 && mColorFadeLevel > 0f ? mScreenBrightness : 0;   //根据屏幕状态设置亮度值, 亮屏时如果color Fade Level为0.0f, brightness一直为0,不会亮屏  
  8.         if (mPhotonicModulator.setState(mScreenState, brightness)) {  
  9.             if (DEBUG) {  
  10.                 Slog.d(TAG, "Screen ready");  
  11.             }  
  12.             mScreenReady = true;  
  13.             invokeCleanListenerIfNeeded();  
  14.         } else {  
  15.             if (DEBUG) {  
  16.                 Slog.d(TAG, "Screen not ready");  
  17.             }  
  18.         }  
  19.     }  
  20. };  
    通过调用线程PhotonicModulator的setState函数来记录所请求的状态,如果线程此时处于wait状态,就调用NotifyAll函数将其唤醒。在线程PhotonicModulator的run函数中为一个死循环,不停的进行判断屏幕亮度与屏幕状态是否发生了改变,如果两者都没有发生变化,为了减轻cpu负载线程就会wait。由于此时屏幕状态发生变化,新的状态为STATE_ON,所以suspending为false。之后进行请求设备状态。

[java]  view plain  copy
  1. public void run() {  
  2.     for (;;) {  
  3.         // Get pending change.  
  4.         final int state;  
  5.         final boolean stateChanged;  
  6.         final int backlight;  
  7.         final boolean backlightChanged;  
  8.         synchronized (mLock) {  
  9.             state = mPendingState;  
  10.             stateChanged = (state != mActualState);  
  11.             backlight = mPendingBacklight;  
  12.             backlightChanged = (backlight != mActualBacklight);  
  13.             if (!stateChanged) {   //判断状态是否变化  
  14.                 // State changed applied, notify outer class.  
  15.                 postScreenUpdateThreadSafe();  
  16.                 mStateChangeInProgress = false;  
  17.             }  
  18.             if (!backlightChanged) {    
  19.                 mBacklightChangeInProgress = false;  
  20.             }  
  21.             if (!stateChanged && !backlightChanged) {   //如果亮度,状态都没有改变线程将wait  
  22.                 try {  
  23.                     mLock.wait();  
  24.                 } catch (InterruptedException ex) { }  
  25.                 continue;  
  26.             }  
  27.             mActualState = state;     //获取确实的状态.亮度  
  28.             mActualBacklight = backlight;  
  29.         }  
  30.   
  31.         // Apply pending change.  
  32.         if (DEBUG) {  
  33.             Slog.d(TAG, "Updating screen state: state="  
  34.                     + Display.stateToString(state) + ", backlight=" + backlight);  
  35.         }  
  36.         mBlanker.requestDisplayState(state, backlight);    //mBlanker为DisplayManagerService中创建的对象,前面有提到.  
  37.     }  
  38. }  

在DisplayManagerSerice中的LocalService初始化函数initPowerManagement中创建的mBlanker, 所以就调用他的requestDisplayState函数

[java]  view plain  copy
  1. DisplayBlanker blanker = new DisplayBlanker() {  
  2.     @Override  
  3.     public void requestDisplayState(int state, int brightness) {  
  4.         // The order of operations is important for legacy reasons.  
  5.         if (state == Display.STATE_OFF) {      
  6.             requestGlobalDisplayStateInternal(state, brightness);   //灭屏先调用该函数  
  7.         }  
  8.   
  9.         callbacks.onDisplayStateChange(state);    //callbacks为PowerManagerService中对象  
  10.   
  11.         if (state != Display.STATE_OFF) {  
  12.             requestGlobalDisplayStateInternal(state, brightness);   //亮屏调用该处函数  
  13.         }  
  14.     }  
  15. };  

    在此处先讲解回调PowerManagerService的onDisplayStateChange函数做了那些事情,耗时点在什么地方. 在该函数中会首先获取一个mLock的锁,这把锁是不是很熟悉,我们在前面讲绘制windows流程的时候window与power交互时需要获取mLock,如果该锁被别的线程持有,window绘制流程就会阻塞。而根据上面流程可以看出,请求设备状态是在绘制window之前发生的,所以调用函数onDisplayStateChange没有结束的话,该锁就一直被持有,导致window绘制流程不能及时返回。

[java]  view plain  copy
  1.     public void onDisplayStateChange(int state) {  
  2.         // This method is only needed to support legacy display blanking behavior  
  3.         // where the display's power state is coupled to suspend or to the power HAL.  
  4.         // The order of operations matters here.  
  5.         synchronized (mLock) {     //获取mLock锁  
  6.             if (mDisplayState != state) {  
  7.                 mDisplayState = state;  
  8.                 if (state == Display.STATE_OFF) {  //灭屏  
  9.                     if (!mDecoupleHalInteractiveModeFromDisplayConfig) {  
  10.                         setHalInteractiveModeLocked(false);  
  11.                     }  
  12.                     if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {  
  13.                         setHalAutoSuspendModeLocked(true);  
  14.                     }  
  15.                 } else {      //亮屏  
  16.                     if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {  
  17.                         setHalAutoSuspendModeLocked(false);    //设置自动suspend模式  
  18.                     }  
  19.                     if (!mDecoupleHalInteractiveModeFromDisplayConfig) {  
  20.                         setHalInteractiveModeLocked(true);   //设置hal交互模式  
  21.                     }  
  22.                 }  
  23.             }  
  24.         }  
  25.     }  
  26. private void setHalAutoSuspendModeLocked(boolean enable) {  
  27.     if (enable != mHalAutoSuspendModeEnabled) {  
  28.         if (DEBUG) {  
  29.             Slog.d(TAG, "Setting HAL auto-suspend mode to " + enable);  
  30.         }  
  31.         mHalAutoSuspendModeEnabled = enable;  
  32.         Trace.traceBegin(Trace.TRACE_TAG_POWER, "setHalAutoSuspend(" + enable + ")");  
  33.         try {  
  34.             nativeSetAutoSuspend(enable);   //调用JNI设置  
  35.         } finally {  
  36.             Trace.traceEnd(Trace.TRACE_TAG_POWER);  
  37.         }  
  38.     }  
  39. }  
之后就会通过函数setHalAutoSuspendModeLocked调用到底层进行处理。而在底层进行处理时可以通过log信息看出,耗时的长短。
[java]  view plain  copy
  1. static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {  
  2.     if (enable) {  
  3.         ALOGD_IF_SLOW(100"Excessive delay in autosuspend_enable() while turning screen off");  //灭屏执行超过100ms,会输出log  
  4.         autosuspend_enable();  
  5.     } else {  
  6.         ALOGD_IF_SLOW(100"Excessive delay in autosuspend_disable() while turning screen on"); //亮屏执行该函数超过100ms, 会输出该行log  
  7.         autosuspend_disable();  
  8.     }  
  9. }  

而在亮屏时调用autosuspend_disable()是往/sys/power/state写 on(亮屏)的函数。在往节点写入状态后,调用late_resume将LCD resume,关于late_resume 阶段 “sprdfb_dispc_resume”函数执行时间可能由于手机硬件信息的不同,导致执行时间相差较大。
上面执行完之后,现在重新回到DMS中继续向下进行执行。

在DMS中通过变量mGlobalDisplayState进行记录当前的设备状态,如果设备状态发生改变时就会重新记录,之后更新设备状态将device对应的Runnable放入队列。然后就会将Runnable从队列中取出执行run函数。根据dumpsys信息可以知道,设备信息中只有一个设备“内置屏幕”,并且对应的Adapter为LocalDisplayAdapter,所以就会调用LocalDisplayAdapter中的run函数。在run函数中会调用SurfaceControl中的setDisplayPowerMode函数处理。最终到JNI中,此处也有可能发生超时,也可从输出log中看出。

[cpp]  view plain  copy
  1. static void nativeSetDisplayPowerMode(JNIEnv* env, jclass clazz, jobject tokenObj, jint mode) {  
  2.     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));  
  3.     if (token == NULL) return;  
  4.   
  5.     ALOGD_IF_SLOW(100, "Excessive delay in setPowerMode()");  
  6.     SurfaceComposerClient::setDisplayPowerMode(token, mode);  
  7. }  
之后就调用frameworks/native/libs/gui/SurfaceComposerClient.cpp的setDisplayPowerMode函数中, 属于GUI模块.
[cpp]  view plain  copy
  1. void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,  
  2.         int mode) {  
  3.     ComposerService::getComposerService()->setPowerMode(token, mode);  
  4. }  
当该流程结束后,设置设备状态就结束了。

绘制keyguard与windows

在DisplayPowerController中调用screenTurningOn函数到PhoneWindowManager中.将mPendingScreenOnUnblocker对象传过来方便完成绘制后回调回来.

[java]  view plain  copy
  1. // Called on the DisplayManager's DisplayPowerController thread.  
  2. @Override  
  3. public void screenTurningOn(final ScreenOnListener screenOnListener) {  
  4.     if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");  
  5.   
  6.     updateScreenOffSleepToken(false);  
  7.     synchronized (mLock) {  
  8.         mScreenOnEarly = true;  
  9.         mScreenOnFully = false;  
  10.         mKeyguardDrawComplete = false;  
  11.         mWindowManagerDrawComplete = false;  
  12.         mScreenOnListener = screenOnListener;  
  13.   
  14.         if (mKeyguardDelegate != null) {  
  15.             mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);  
  16.             mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);  //发送超时消息, 1000ms keyguard还没有绘制完,就继续向下执行  
  17.             mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);  //调用keyguard中函数, 绘制完成后回调mKeyguardDrawnCallback  
  18.         } else {  
  19.             if (DEBUG_WAKEUP) Slog.d(TAG,  
  20.                     "null mKeyguardDelegate: setting mKeyguardDrawComplete.");  
  21.             finishKeyguardDrawn();   //如果没有keyguard就直接结束绘制  
  22.         }  
  23.     }  
  24. }  
当绘制完keyguard或者keyguard绘制超时都会调用finishKeyguardDrawn结束绘制.
[java]  view plain  copy
  1. final DrawnListener mKeyguardDrawnCallback = new DrawnListener() {  
  2.     @Override  
  3.     public void onDrawn() {   //绘制完成调用onDrawn函数  
  4.         if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onDrawn.");  
  5.         mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);  //发送MSG_KEYGUARD_DRAWN_COMPLETE消息  
  6.     }  
  7. };  
  8.   
  9. private class PolicyHandler extends Handler {  
  10.     @Override  
  11.     public void handleMessage(Message msg) {  
  12.         switch (msg.what) {  
  13.             case MSG_KEYGUARD_DRAWN_COMPLETE:      
  14.                 if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete");  
  15.                 finishKeyguardDrawn();   //keyguard绘制完成, 结束  
  16.                 break;  
  17.             case MSG_KEYGUARD_DRAWN_TIMEOUT:  
  18.                 Slog.w(TAG, "Keyguard drawn timeout. Setting mKeyguardDrawComplete");  
  19.                 finishKeyguardDrawn();   //keyguard绘制超时, 结束  
  20.                 break;  
  21.         }  
  22.     }  
  23. }  
keyguard绘制结束后调用WindowManagerService对Windows进行绘制, 绘制windows超时时间也为1000ms
[java]  view plain  copy
  1. /** Amount of time (in milliseconds) to wait for windows drawn before powering on. */  
  2. static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;      //windows绘制超时时间1000ms  
  3.   
  4. private void finishKeyguardDrawn() {  
  5.     synchronized (mLock) {  
  6.         if (!mScreenOnEarly || mKeyguardDrawComplete) {  
  7.             return// We are not awake yet or we have already informed of this event.  
  8.         }  
  9.   
  10.         mKeyguardDrawComplete = true;  
  11.         if (mKeyguardDelegate != null) {  
  12.             mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);  //移除keyguard超时的message  
  13.         }  
  14.         mWindowManagerDrawComplete = false;  
  15.     }  
  16.   
  17.     // ... eventually calls finishWindowsDrawn which will finalize our screen turn on  
  18.     // as well as enabling the orientation change logic/sensor.  
  19.     mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback,  
  20.             WAITING_FOR_DRAWN_TIMEOUT);    //windows绘制完成后回调mWindowManagerDrawCallback  
  21. }  
在WindowManagerService中获取需要绘制的windows将其加入mWaitingForDrawn中等待绘制,通过检查mWaitingForDrawn是否为空来判断,window是否绘制完成。此时screenTurningOn函数就执行完了,剩下的就是等待windows绘制完成.
[java]  view plain  copy
  1.     @Override  
  2.     public void waitForAllWindowsDrawn(Runnable callback, long timeout) {  
  3.         boolean allWindowsDrawn = false;  
  4.         synchronized (mWindowMap) {  
  5.             mWaitingForDrawnCallback = callback;  
  6.             final WindowList windows = getDefaultWindowListLocked();  //获取windows列表  
  7.             for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {  
  8.                 final WindowState win = windows.get(winNdx);  
  9.                 final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);  
  10.                 if (win.isVisibleLw()  
  11.                         && (win.mAppToken != null || isForceHiding)) {  
  12.                     win.mWinAnimator.mDrawState = DRAW_PENDING;  
  13.                     // Force add to mResizingWindows.  
  14.                     win.mLastContentInsets.set(-1, -1, -1, -1);  
  15.                     mWaitingForDrawn.add(win);   //将所要绘制的windows加入mWaitingForDrawn中  
  16.   
  17.                     // No need to wait for the windows below Keyguard.  
  18.                     if (isForceHiding) {  
  19.                         break;  
  20.                     }  
  21.                 }  
  22.             }  
  23.             mWindowPlacerLocked.requestTraversal();  
  24.             mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);   //先将绘制windows超时的message移除, 以便重新计时  
  25.             if (mWaitingForDrawn.isEmpty()) {  
  26.                 allWindowsDrawn = true;  
  27.             } else {  
  28.                 mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);  //发送绘制超时的延迟消息  
  29.                 checkDrawnWindowsLocked();   //检查windows是否绘制完成  
  30.             }  
  31.         }  
  32.         if (allWindowsDrawn) {  
  33.             callback.run();   //如果所有windows都绘制完成了就调用回调接口, 回到PhoneWindowManager中  
  34.         }  
  35.     }  
  36.   
  37. void checkDrawnWindowsLocked() {  
  38.     if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {  
  39.         return;   //如果没有要绘制的windows或者没有回调接口就直接返回了  
  40.     }  
  41.     for (int j = mWaitingForDrawn.size() - 1; j >= 0; j--) {  //遍历要绘制的windows  
  42.         WindowState win = mWaitingForDrawn.get(j);  
  43.         if (DEBUG_SCREEN_ON) Slog.i(TAG_WM, "Waiting for drawn " + win +  
  44.                 ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +  
  45.                 " mHasSurface=" + win.mHasSurface +  
  46.                 " drawState=" + win.mWinAnimator.mDrawState);  
  47.         if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) {  
  48.             // Window has been removed or hidden; no draw will now happen, so stop waiting.  
  49.             if (DEBUG_SCREEN_ON) Slog.w(TAG_WM, "Aborted waiting for drawn: " + win);  
  50.             mWaitingForDrawn.remove(win);   //如果window已经被移除或者隐藏 就不需要绘制了, 直接从等待列表中移除   
  51.         } else if (win.hasDrawnLw()) {  
  52.             // Window is now drawn (and shown).  
  53.             if (DEBUG_SCREEN_ON) Slog.d(TAG_WM, "Window drawn win=" + win);  
  54.             mWaitingForDrawn.remove(win);    //如果window已经绘制完成,也从等待列表中移除  
  55.         }  
  56.     }  
  57.     if (mWaitingForDrawn.isEmpty()) { //如果等待列表为空  
  58.         if (DEBUG_SCREEN_ON) Slog.d(TAG_WM, "All windows drawn!");  
  59.         mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);  //移除超时消息  
  60.         mH.sendEmptyMessage(H.ALL_WINDOWS_DRAWN);  //发送ALL_WINDOWS_DRAWN消息  
  61.     }  
  62. }  
这时只用等待所有的windows绘制了.

Windows的绘制在ViewRootImpl的draw函数中,其中ViewRoot的draw是通过vsync进行同步绘制。在绘制完成之后就会调用到WMS中DO_TRAVERSAL流程进行调用函数checkDrawnWindowsLocked判断每个window是否绘制完成。绘制完成后会发起ALL_WINDOWS_DRAWN的Message,回调回PhoneWindowManager。由于在调用函数performLayoutAndPlaceSurfacesLockedInner过程中需要与Power中的一些函数进行交互,而在Power中函数执行需要获得mLock锁,如果该锁被别的线程所持有就会发生阻塞,就不会及时返回发出window绘制完成的信号,在该流程中具体mLock还会被那些线程获取, 在讲解设置设置状态时讲解过可能被power中线程获取.
当window绘制完成后通过接口回调到PhoneWindowManager中,发送Window准备好的Message进行结束window绘制,将变量mWindowManagerDrawComplete置为true。最后回到DPC中。

[java]  view plain  copy
  1. case ALL_WINDOWS_DRAWN: {   //绘制windows完成  
  2.     Runnable callback;  
  3.     synchronized (mWindowMap) {  
  4.         callback = mWaitingForDrawnCallback;  
  5.         mWaitingForDrawnCallback = null;    
  6.     }  
  7.     if (callback != null) {  
  8.         callback.run();  //回调回PhoneWindowManager  
  9.     }  
  10. }  
  11.   
  12. case WAITING_FOR_DRAWN_TIMEOUT: {   //绘制Windows超时  
  13.     Runnable callback = null;  
  14.     synchronized (mWindowMap) {  
  15.         Slog.w(TAG_WM, "Timeout waiting for drawn: undrawn=" + mWaitingForDrawn);  //输出超时log信息  
  16.         mWaitingForDrawn.clear();   //将等待绘制列表清空  
  17.         callback = mWaitingForDrawnCallback;  
  18.         mWaitingForDrawnCallback = null;  
  19.     }  
  20.     if (callback != null) {  
  21.         callback.run(); //回调回PhoneWindowManager  
  22.     }  
  23.     break;  
  24. }  
回调会PhoneWindowManager后发送所有windows绘制完成的message
[java]  view plain  copy
  1. final Runnable mWindowManagerDrawCallback = new Runnable() {  
  2.     @Override  
  3.     public void run() {  
  4.         if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display!");  
  5.         mHandler.sendEmptyMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE); //发送Handler消息  
  6.     }  
  7. };  
  8. private class PolicyHandler extends Handler {  
  9.     @Override  
  10.     public void handleMessage(Message msg) {  
  11.         switch (msg.what) {  
  12.             //........  
  13.             case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:  
  14.                 if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mWindowManagerDrawComplete");  
  15.                 finishWindowsDrawn();    //结束windows绘制  
  16.                 break;  
  17.             //.......  
  18.         }  
  19.     }  
  20. }  
  21. private void finishWindowsDrawn() {  
  22.     synchronized (mLock) {  
  23.         if (!mScreenOnEarly || mWindowManagerDrawComplete) {  
  24.             return// Screen is not turned on or we did already handle this case earlier.  
  25.         }  
  26.   
  27.         mWindowManagerDrawComplete = true;   //设置windows绘制完成为true  
  28.     }  
  29.   
  30.     finishScreenTurningOn();   //调用screenTurningOn就要结束了  
  31. }  
  32.   
  33. private void finishScreenTurningOn() {  
  34.     synchronized (mLock) {  
  35.         // We have just finished drawing screen content. Since the orientation listener  
  36.         // gets only installed when all windows are drawn, we try to install it again.  
  37.         updateOrientationListenerLp();  //更新转向监听器  
  38.     }  
  39.     final ScreenOnListener listener;  
  40.     final boolean enableScreen;  
  41.     synchronized (mLock) {  
  42.         if (DEBUG_WAKEUP) Slog.d(TAG,  
  43.                 "finishScreenTurningOn: mAwake=" + mAwake  
  44.                         + ", mScreenOnEarly=" + mScreenOnEarly  
  45.                         + ", mScreenOnFully=" + mScreenOnFully  
  46.                         + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete  
  47.                         + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);  
  48.   
  49.         if (mScreenOnFully || !mScreenOnEarly || !mWindowManagerDrawComplete  
  50.                 || (mAwake && !mKeyguardDrawComplete)) {  
  51.             return// spurious or not ready yet  
  52.         }  
  53.   
  54.         if (DEBUG_WAKEUP) Slog.i(TAG, "Finished screen turning on...");  
  55.         listener = mScreenOnListener;     
  56.         mScreenOnListener = null;  
  57.         mScreenOnFully = true;  
  58.   
  59.         // Remember the first time we draw the keyguard so we know when we're done with  
  60.         // the main part of booting and can enable the screen and hide boot messages.  
  61.         if (!mKeyguardDrawnOnce && mAwake) {  
  62.             mKeyguardDrawnOnce = true;  
  63.             enableScreen = true;  
  64.             if (mBootMessageNeedsHiding) {  
  65.                 mBootMessageNeedsHiding = false;  
  66.                 hideBootMessages();  
  67.             }  
  68.         } else {  
  69.             enableScreen = false;  
  70.         }  
  71.     }  
  72.   
  73.     if (listener != null) {  
  74.         listener.onScreenOn();    //回调回DisplayPowerController的onScreenOn函数  
  75.     }  
  76.   
  77.     if (enableScreen) {  
  78.         try {  
  79.             mWindowManager.enableScreenIfNeeded();  
  80.         } catch (RemoteException unhandled) {  
  81.         }  
  82.     }  
  83. }  

Windows绘制完成回调回来之后,此时就不用block screen了,将mPendingScreenOnUnblocker置为空,并计算screen一共block了多少毫秒。之后进行更新power state,由于之前调用DisplayPS设置过状态为ON,现在状态没有发生改变,故不用进行设置直接返回,由于window已经绘制完成mPendingScreenOnUnblocker为空,故返回true。如果没有亮屏动画直接将ColorFade level设置为1.0,如果有亮屏动画的话会有ColorFade level会从0.0逐渐变为1.0。

[java]  view plain  copy
  1. private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {  
  2.     @Override  
  3.     public void onScreenOn() {  
  4.         Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);  
  5.         msg.setAsynchronous(true);  
  6.         mHandler.sendMessage(msg);   //发送unblock的消息  
  7.     }  
  8. }  
  9.   
  10. private final class DisplayControllerHandler extends Handler {  
  11.     public DisplayControllerHandler(Looper looper) {  
  12.         super(looper, nulltrue /*async*/);  
  13.     }  
  14.   
  15.     @Override  
  16.     public void handleMessage(Message msg) {  
  17.         switch (msg.what) {  
  18.             case MSG_UPDATE_POWER_STATE:  
  19.                 updatePowerState();  
  20.                 break;  
  21.   
  22.             case MSG_PROXIMITY_SENSOR_DEBOUNCED:  
  23.                 debounceProximitySensor();  
  24.                 break;  
  25.   
  26.             case MSG_SCREEN_ON_UNBLOCKED: //Handler收到unblock的消息后  
  27.                 if (mPendingScreenOnUnblocker == msg.obj) {  
  28.                     unblockScreenOn();    //将mPendingScreenOnUnblocker赋值NULL,并且输出unblock的时间, 之前有讲解  
  29.                     updatePowerState();   //重新更新power 状态. setPowerState返回true, 继续设置Color Fade Level为1.0f  
  30.                 }  
  31.                 break;  
  32.         }  
  33.     }  
  34. }  

之后就是通过animateScreenBrightness函数设置屏幕亮度.

进而点亮屏幕, 在后文中讲解.

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fu_kevin0606/article/details/54411816
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值