android5.1 自动亮度调节简析

android亮度调节在"设置"的"显示"中,分为手动调节和自动调节。其中手动调节就是通过拖动"亮度调节"的拖动条来直接设置亮度值,自动调节则比较复杂,设置自动亮度模式后,再拖动"亮度调节"的拖动条,可以小范围改变亮度值(前提是光照条件不剧烈变化),但这时不是直接设置的亮度值,而是需要通过一系列转变转成亮度值。

现在遇到问题是:在自动亮度调节模式下,调节亮度拖动条到最小,屏幕变得很黑。


设置中的亮度设置是在DisplaySettings.java(/Settings/src/com/android/settings/DisplaySettings.java)中,但这里面没有拖动条的相关操作,只有设置手动调节和自动调节模式的操作。其实弹出拖动条的操作在BrightnessPreference.java(/Settings/src/com/android/settings/BrightnessPreference.java)中,它实现了Preference的onClick方法:

 protected void onClick() {
        Log.i("BrightnessPreference", "BrightnessPreference:brightness dialog open");
        getContext().startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
                UserHandle.CURRENT_OR_SELF);
    }

通过查找这个action,发现其就是android.intent.action.SHOW_BRIGHTNESS_DIALOG,在 SystemUI(frameworks\base\packages\SystemUI)的AndroidManifest.xml里也出现了:

<activity
    android:name=".settings.BrightnessDialog"
      android:label="@string/quick_settings_brightness_dialog_title"
            android:theme="@android:style/Theme.DeviceDefault.Dialog"
            android:finishOnCloseSystemDialogs="true"
            android:launchMode="singleInstance"
            android:excludeFromRecents="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

这里就是通过action启动BrightnessDialog.java(/UsbStorageActivity/src/com/android/systemui/settings/BrightnessDialog.java),发现这个Activity就是亮度调节的弹出框,至此就没有Settings什么事了。

  • BrightnessDialog

    在BrightnessDialog中初始化拖动条等相关控件,并实例化了BrightnessController.java(/UsbStorageActivity/src/com/android/systemui/settings/BrightnessController.java)

    setContentView(R.layout.quick_settings_brightness_dialog);
    final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
    final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
    mBrightnessController = new BrightnessController(this, icon, slider);

    并注册和注销BrightnessController的回调方法:

    @Override
      protected void onStart() {
          super.onStart();
          mBrightnessController.registerCallbacks();
      }
    
      @Override
      protected void onStop() {
          super.onStop();
          mBrightnessController.unregisterCallbacks();
      }
  • BrightnessController

    BrightnessController的回调方法主要是在亮度调节模式改变时更新系统保存的模式,在亮度调节时更新拖动条(此拖动条和下拉栏的亮度条保持一致)。这里主要看亮度设置时的操作。
    因为BrightnessController实现了对ToggleSlider的监听,在拖动条改变时设置亮度:

    @Override
      public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
          updateIcon(mAutomatic);
          if (mExternalChange) return;
    
          if (!mAutomatic) {
              final int val = value + mMinimumBacklight;
              Log.i(TAG, "brightness mode:"+mAutomatic+"\nvalue:"+value+"value set:"+val);
              setBrightness(val);
              if (!tracking) {
                  AsyncTask.execute(new Runnable() {
                          public void run() {
                              Settings.System.putIntForUser(mContext.getContentResolver(),
                                      Settings.System.SCREEN_BRIGHTNESS, val,
                                      UserHandle.USER_CURRENT);
                          }
                      });
              }
          } else {
              final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1;   //original code
              setBrightnessAdj(adj);
              if (!tracking) {
                  AsyncTask.execute(new Runnable() {
                      public void run() {
                          Settings.System.putFloatForUser(mContext.getContentResolver(),
                                  Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adj,
                                  UserHandle.USER_CURRENT);
                      }
                  });
              }
          }
    
          for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
              cb.onBrightnessLevelChanged();
          }
      }

    value就是拖动条的值,在初始化的时候,亮度条的范围和值都被设置:

    private void updateSlider() {
          if (mAutomatic) {
              float value = Settings.System.getFloatForUser(mContext.getContentResolver(),Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0,
                      UserHandle.USER_CURRENT);
              mControl.setMax((int) BRIGHTNESS_ADJ_RESOLUTION);
              mControl.setValue((int) ((value + 1) * BRIGHTNESS_ADJ_RESOLUTION / 2f));
    
          } else {
              int value;
              value = Settings.System.getIntForUser(mContext.getContentResolver(),
                      Settings.System.SCREEN_BRIGHTNESS, mMaximumBacklight,
                      UserHandle.USER_CURRENT);
              mControl.setMax(mMaximumBacklight - mMinimumBacklight);
              mControl.setValue(value - mMinimumBacklight);
          }
      }

    可以看出自动模式下亮度条最大值为BRIGHTNESS_ADJ_RESOLUTION,该值为100,而手动模式下亮度条最大值为mMaximumBacklight - mMinimumBacklight,其中mMaximumBacklight为255,mMinimumBacklight为10。
    在拖动条的值改变时,onChange()方法被调用,若为手动模式,则:

    final int val = value + mMinimumBacklight;
    setBrightness(val);

    可以看出,手动模式直接在从拖动条获取的基数value上加了个亮度的最小值,然后调用setBrightness(val),再来看 setBrightness(val):

    private void setBrightness(int brightness) {
          try {
              mPower.setTemporaryScreenBrightnessSettingOverride(brightness);
          } catch (RemoteException ex) {
          }
      }

    mPower是IPowerManager的对象,这里先放在一边。
    再来看若为自动模式时拖动条改变,

    final float adj = value / (BRIGHTNESS_ADJ_RESOLUTION / 2f) - 1;   
    setBrightnessAdj(adj);

    其中BRIGHTNESS_ADJ_RESOLUTION为100。可以计算出adj的范围随着value的改变始终保持在[-1,1],暂且称之为调节参数。然后调用setBrightnessAdj(adj)

    private void setBrightnessAdj(float adj) {
          try {
              mPower.setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(adj);
          } catch (RemoteException ex) {
          }
      }

    可以看出,两种模式都最终交给IPowerManager处理了,然后基本就是由framework层来进行处理。
    3.IPowerManager
    IPowerManager开头就有注释:

    /*
    * This file is auto-generated.  DO NOT MODIFY.
    * Original file: frameworks/base/core/java/android/os/IPowerManager.aidl
    */

    所以这个文件是aidl自动生成的。
    两个方法:

    @Override 
    public void setTemporaryScreenBrightnessSettingOverride(int brightness) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeInt(brightness);
    mRemote.transact(Stub.TRANSACTION_setTemporaryScreenBrightnessSettingOverride, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }
    @Override 
    public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeFloat(adj);
    mRemote.transact(Stub.TRANSACTION_setTemporaryScreenAutoBrightnessAdjustmentSettingOverride, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    }

    然后搜索发现在PowerManagerService.java(com.android.server.power.PowerManagerService.class)中的BinderService类implements了IPowerManager的Stub类,并且实现了上述两个方法:

    \/**
           * Used by the settings application and brightness control widgets to
           * temporarily override the current screen brightness setting so that the
           * user can observe the effect of an intended settings change without applying
           * it immediately.
           *
           * The override will be canceled when the setting value is next updated.
           *
           * @param brightness The overridden brightness.
           *
           * @see android.provider.Settings.System#SCREEN_BRIGHTNESS
           */
          @Override // Binder call
          public void setTemporaryScreenBrightnessSettingOverride(int brightness) {
              mContext.enforceCallingOrSelfPermission(
                      android.Manifest.permission.DEVICE_POWER, null);
    
              final long ident = Binder.clearCallingIdentity();
              try {
                  setTemporaryScreenBrightnessSettingOverrideInternal(brightness);
              } finally {
                  Binder.restoreCallingIdentity(ident);
              }
          }
    
          /**
           * Used by the settings application and brightness control widgets to
           * temporarily override the current screen auto-brightness adjustment setting so that the
           * user can observe the effect of an intended settings change without applying
           * it immediately.
           *
           * The override will be canceled when the setting value is next updated.
           *
           * @param adj The overridden brightness, or Float.NaN to disable the override.
           *
           * @see android.provider.Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ
           */
          @Override // Binder call
          public void setTemporaryScreenAutoBrightnessAdjustmentSettingOverride(float adj) {
              mContext.enforceCallingOrSelfPermission(
                      android.Manifest.permission.DEVICE_POWER, null);
    
              final long ident = Binder.clearCallingIdentity();
              try {
                  setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(adj);
              } finally {
                  Binder.restoreCallingIdentity(ident);
              }
          }

    注释里说明,这两个方法是Settings和亮度调节插件使用的,再来看值传进来后调用的两个方法:

    private void setTemporaryScreenBrightnessSettingOverrideInternal(int brightness) {
          synchronized (mLock) {
              if (mTemporaryScreenBrightnessSettingOverride != brightness) {
                  mTemporaryScreenBrightnessSettingOverride = brightness;
                  mDirty |= DIRTY_SETTINGS;
                  updatePowerStateLocked();
              }
          }
      }
    
      private void setTemporaryScreenAutoBrightnessAdjustmentSettingOverrideInternal(float adj) {
          synchronized (mLock) {
              // Note: This condition handles NaN because NaN is not equal to any other
              // value, including itself.
              if (mTemporaryScreenAutoBrightnessAdjustmentSettingOverride != adj) {
                  mTemporaryScreenAutoBrightnessAdjustmentSettingOverride = adj;
                  mDirty |= DIRTY_SETTINGS;
                  updatePowerStateLocked();
              }
          }
      }

    拿到值后赋值给一个全局变量,然后马上调用updatePowerStateLocked(),再来看此方法对传入的值做了哪些操作:

    /**
       * Updates the display power state asynchronously.
       * When the update is finished, mDisplayReady will be set to true.  The display
       * controller posts a message to tell us when the actual display power state
       * has been updated so we come back here to double-check and finish up.
       *
       * This function recalculates the display power state each time.
       *
       * @return True if the display became ready.
       */
      private boolean updateDisplayPowerStateLocked(int dirty) {
          final boolean oldDisplayReady = mDisplayReady;
          if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS
                  | DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
                  | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST)) != 0) {
              mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
    
              // Determine appropriate screen brightness and auto-brightness adjustments.
              int screenBrightness = mScreenBrightnessSettingDefault;
              float screenAutoBrightnessAdjustment = 0.0f;
              boolean autoBrightness = (mScreenBrightnessModeSetting ==
                      Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
              if (isValidBrightness(mScreenBrightnessOverrideFromWindowManager)) {
                  screenBrightness = mScreenBrightnessOverrideFromWindowManager;
                  autoBrightness = false;
              } else if (isValidBrightness(mTemporaryScreenBrightnessSettingOverride)) {
                  screenBrightness = mTemporaryScreenBrightnessSettingOverride;
              } else if (isValidBrightness(mScreenBrightnessSetting)) {
                  screenBrightness = mScreenBrightnessSetting;
              }
              if (autoBrightness) {
                  screenBrightness = mScreenBrightnessSettingDefault;
                  if (isValidAutoBrightnessAdjustment(
                          mTemporaryScreenAutoBrightnessAdjustmentSettingOverride)) {
                      screenAutoBrightnessAdjustment =
                              mTemporaryScreenAutoBrightnessAdjustmentSettingOverride;
                  } else if (isValidAutoBrightnessAdjustment(
                          mScreenAutoBrightnessAdjustmentSetting)) {
                      screenAutoBrightnessAdjustment = mScreenAutoBrightnessAdjustmentSetting;
                  }
              }
              screenBrightness = Math.max(Math.min(screenBrightness,
                      mScreenBrightnessSettingMaximum), mScreenBrightnessSettingMinimum);
              screenAutoBrightnessAdjustment = Math.max(Math.min(
                      screenAutoBrightnessAdjustment, 1.0f), -1.0f);
    
              // Update display power request.
              mDisplayPowerRequest.screenBrightness = screenBrightness;
              mDisplayPowerRequest.screenAutoBrightnessAdjustment =
                      screenAutoBrightnessAdjustment;
              mDisplayPowerRequest.useAutoBrightness = autoBrightness;
              mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked();
              mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
              mDisplayPowerRequest.boostScreenBrightness = mScreenBrightnessBoostInProgress;
    
              if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) {
                  mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager;
                  mDisplayPowerRequest.dozeScreenBrightness =
                          mDozeScreenBrightnessOverrideFromDreamManager;
              } else {
                  mDisplayPowerRequest.dozeScreenState = Display.STATE_UNKNOWN;
                  mDisplayPowerRequest.dozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
              }
    
              mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                      mRequestWaitForNegativeProximity);
              mRequestWaitForNegativeProximity = false;
    
              if (DEBUG_SPEW) {
                  Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady
                          + ", policy=" + mDisplayPowerRequest.policy
                          + ", mWakefulness=" + mWakefulness
                          + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary)
                          + ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
                          + ", mBootCompleted=" + mBootCompleted
                          + ", mScreenBrightnessBoostInProgress="
                                  + mScreenBrightnessBoostInProgress);
              }
          }
          return mDisplayReady && !oldDisplayReady;
      }

    发现最终值都被赋给mDisplayPowerRequest的属性,然后调用mDisplayManagerInternal.requestPowerState将值传出返回结果。mDisplayManagerInternalDisplayManagerInternal(android.hardware.display.DisplayManagerInternal.class)的对象,再来看看DisplayManagerInternal:
    4.DisplayManagerInternal

    public abstract boolean requestPowerState(DisplayPowerRequest request,
              boolean waitForNegativeProximity);

    发现并没有具体的实现方法。通过搜索发现DisplayManageService(com.android.server.display.DisplayManagerService.class)中的LocalService类继承了DisplayManagerInternal并实现了requestPowerState()方法:

    @Override
          public boolean requestPowerState(DisplayPowerRequest request,
                  boolean waitForNegativeProximity) {
              return mDisplayPowerController.requestPowerState(request,
                      waitForNegativeProximity);
          }

    发现调用了DisplayPowerController(com.android.server.display.DisplayPowerController.class)requestPowerState()方法作为返回结果。
    5.DisplayPowerController

    public boolean requestPowerState(DisplayPowerRequest request,
              boolean waitForNegativeProximity) {
          if (DEBUG) {
              Slog.d(TAG, "requestPowerState: "
                      + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
          }
    
          synchronized (mLock) {
              boolean changed = false;
    
              if (waitForNegativeProximity
                      && !mPendingWaitForNegativeProximityLocked) {
                  mPendingWaitForNegativeProximityLocked = true;
                  changed = true;
              }
    
              if (mPendingRequestLocked == null) {
                  mPendingRequestLocked = new DisplayPowerRequest(request);
                  changed = true;
              } else if (!mPendingRequestLocked.equals(request)) {
                  mPendingRequestLocked.copyFrom(request);
                  changed = true;
              }
    
              if (changed) {
                  mDisplayReadyLocked = false;
              }
    
              if (changed && !mPendingRequestChangedLocked) {
                  mPendingRequestChangedLocked = true;
                  sendUpdatePowerStateLocked();
              }
    
              return mDisplayReadyLocked;
          }
      }

    可以看出,先是将request对象赋给本地,然后调用sendUpdatePowerStateLocked()

    private void sendUpdatePowerStateLocked() {
          if (!mPendingUpdatePowerStateLocked) {
              mPendingUpdatePowerStateLocked = true;
              Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
              msg.setAsynchronous(true);
              mHandler.sendMessage(msg);
          }
      }

    交给handler处理:

    case MSG_UPDATE_POWER_STATE:
                      updatePowerState();
                      break;

    调用updatePowerState()方法,其中有这样一段代码:

    // Configure auto-brightness.
          boolean autoBrightnessEnabled = false;
          if (mAutomaticBrightnessController != null) {
              final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig
                      && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND);
              autoBrightnessEnabled = mPowerRequest.useAutoBrightness
                      && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
                      && brightness < 0;
              mAutomaticBrightnessController.configure(autoBrightnessEnabled,
                      mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON);
          }

    mAutomaticBrightnessControllerAutomaticBrightnessController(com.android.server.display.AutomaticBrightnessController.class)对象,下面来看AutomaticBrightnessController.
    6.AutomaticBrightnessController

    public void configure(boolean enable, float adjustment, boolean dozing) {
          // While dozing, the application processor may be suspended which will prevent us from
          // receiving new information from the light sensor. On some devices, we may be able to
          // switch to a wake-up light sensor instead but for now we will simply disable the sensor
          // and hold onto the last computed screen auto brightness.  We save the dozing flag for
          // debugging purposes.
          mDozing = dozing;
          boolean changed = setLightSensorEnabled(enable && !dozing);
          changed |= setScreenAutoBrightnessAdjustment(adjustment);
          if (changed) {
              updateAutoBrightness(false /*sendUpdate*/);
          }
      }
    private boolean setScreenAutoBrightnessAdjustment(float adjustment) {
          if (adjustment != mScreenAutoBrightnessAdjustment) {
              mScreenAutoBrightnessAdjustment = adjustment;
              return true;
          }
          return false;
      }

    拿到亮度调节参数后:

    private void updateAutoBrightness(boolean sendUpdate) {
          if (!mAmbientLuxValid) {
              return;
          }
    
          float value = mScreenAutoBrightnessSpline.interpolate(mAmbientLux);
          float gamma = 1.0f;
    
          if (USE_SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT
                  && mScreenAutoBrightnessAdjustment != 0.0f) {
              final float adjGamma = MathUtils.pow(SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA,
                      Math.min(1.0f, Math.max(-1.0f, -mScreenAutoBrightnessAdjustment)));
              gamma *= adjGamma;
              if (DEBUG) {
                  Slog.d(TAG, "updateAutoBrightness: adjGamma=" + adjGamma);
              }
          }
    
          if (USE_TWILIGHT_ADJUSTMENT) {
              TwilightState state = mTwilight.getCurrentState();
              if (state != null && state.isNight()) {
                  final long now = System.currentTimeMillis();
                  final float earlyGamma =
                          getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise());
                  final float lateGamma =
                          getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise());
                  gamma *= earlyGamma * lateGamma;
                  if (DEBUG) {
                      Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma
                              + ", lateGamma=" + lateGamma);
                  }
              }
          }
    
          if (gamma != 1.0f) {
              final float in = value;
              value = MathUtils.pow(value, gamma);
              if (DEBUG) {
                  Slog.d(TAG, "updateAutoBrightness: gamma=" + gamma
                          + ", in=" + in + ", out=" + value);
              }
          }
    
          int newScreenAutoBrightness =
              clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
          if (mScreenAutoBrightness != newScreenAutoBrightness) {
              if (DEBUG) {
                  Slog.d(TAG, "updateAutoBrightness: mScreenAutoBrightness="
                          + mScreenAutoBrightness + ", newScreenAutoBrightness="
                          + newScreenAutoBrightness);
              }
    
              mScreenAutoBrightness = newScreenAutoBrightness;
              mLastScreenAutoBrightnessGamma = gamma;
              if (sendUpdate) {
                  mCallbacks.updateBrightness();
              }
          }
      }

    这里是一大段针对自动亮度调节,将亮度调节系数和一段时间内的光线感应器获取的光强的加权平均通过一系列运算转化为合适的屏幕背光亮度,具体的转化模型这里不深究。

在这里来看一下,每次转化好亮度后都会做如下处理:

private int clampScreenBrightness(int value) {
        return MathUtils.constrain(value,
                mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
    }

mScreenBrightnessRangeMinimum就是最小值,由构造函数传过来,发现是DisplayPowerController实例化AutomaticBrightnessController时传入的:

mAutomaticBrightnessController = new AutomaticBrightnessController(this,
                        handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
                        lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
                        mScreenBrightnessRangeMaximum, dozeScaleFactor);

DisplayPowerController的构造函数中:

int screenBrightnessRangeMinimum = Math.min(Math.min(
                screenBrightnessSettingMinimum, mScreenBrightnessDimConfig),
                mScreenBrightnessDarkConfig);

这三个值均取自config.xml(frameworks\base\core\res\res\values\config.xml)分别为:

screenBrightnessSettingMinimum:config_screenBrightnessSettingMinimum
mScreenBrightnessDimConfig:config_screenBrightnessDim
mScreenBrightnessDarkConfig:config_screenBrightnessDark

发现config.xml文件中三个值最小的为1,改为10之后问题解决。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值