Android 8.1 DisplayPowerController(二) Proximity Sensor的亮灭屏
概述
P-Sensor亮屏和按Power键亮屏流程还是有些不同之处,如开始调用位置、PowerManagerService中的流程等,由于在平常遇到过许多PSensor亮屏相关Bug,因此这里独立地进行下分析。
流程
在DisplayPowerController中,实例化了一个SensorEventListener
对PSensor事件进行监听:
private final SensorEventListener mProximitySensorListener = new SensorEventListener() {@Overridepublic void onSensorChanged(SensorEvent event) {if (mProximitySensorEnabled) {//获取当前系统时间final long time = SystemClock.uptimeMillis();//获取distance值,表示距离final float distance = event.values[0];//如果distance>=0.0fboolean positive = distance >= 0.0f && distance < mProximityThreshold;//处理PSensor上报事件handleProximitySensorEvent(time, positive);}}
SensorEventListener是一个用于接收Sensor新数据的接口,只有两个方法:
onSensorChanged(SensorEvent event)
:当有新Sensor事件时进行回调;onAccuracyChanged(Sensor, int accuracy)
:Sensor注册的精度值发生改变时调用;
在DisplayPowerController中,重写了onSensorChanged()
方法,因此,在接收到PSensor新事件时,会回调该方法。在这个方法中可以看到,只有mProximitySensorEnabled这个全局变量为true时,才会进入该方法体,这个值表示PSensor是否可用,由屏幕状态和PMS中的请求参数useProximitySensor共同决定,只有满足useProximitySensor=true && state!=Display.STATE_OFF
时,该值才会为true,而useProximitySensor=true的条件是当前系统持有PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK
类型的锁。
现在进入方法体中,它记录了当前系统时间,并根据SensorEvent判断是否是靠近还是远离后,将time和distance作为参数传给了handleProximitySensorEvent()
方法,该方法如下:
private void handleProximitySensorEvent(long time, boolean positive) {//PSensor是否可用,由setProximitySensorEnabled()方法根据PMS中请求的参数
//useProximitySensor和STATE_OFF决定if (mProximitySensorEnabled) {//mPendingProximity表示"将要进行的PSensor状态",初始值为// PROXIMITY_UNKNOWN,PROXIMITY_NEGATIVE&&positive=false都表示远离,// 所以没有值变化,直接返回,下同if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {return; // no change}if (mPendingProximity == PROXIMITY_POSITIVE && positive) {return; // no change}//取消延迟处理Psensor事件的HandlermHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);//如果是靠近if (positive) {//设置mPendingProximity 值,PROXIMITY_POSITIVE表示靠近设备mPendingProximity = PROXIMITY_POSITIVE;//设置PSensor去抖动时间(或转换延迟时间),会申请一个Display锁// 靠近时立即灭屏setPendingProximityDebounceTime(time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); } else {mPendingProximity = PROXIMITY_NEGATIVE;//设置去抖动时间,远离时会稍等会再亮屏setPendingProximityDebounceTime(time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); }// Debounce the new sensor reading.//读取新Psensor数据debounceProximitySensor();}
}
在这个方法中,如果PSensor数据没有改变,则直接返回。如果上报值发生了改变,则首先设置mPendingProximity,表示“将要、打算进行的PSensor事件”,如果是靠近则设置该变量值为PROXIMITY_POSITIVE
,远离则设置该变量值为PROXIMITY_NEGATIVE
,同时调用setPendingProximityDebounceTime()
方法,该方法用来设置PSensor的转换延迟时间,并会通过DisplayPowerCallback向PMS中申请一个Display锁,如果是靠近,延迟时间为0,也即不会进行延迟;如果是远离,默认延迟时间为250。该方法如下:
private void setPendingProximityDebounceTime(long debounceTime) {if (mPendingProximityDebounceTime < 0) {//申请一个PowerManagerService.Display的SuspendBlocker锁mCallbacks.acquireSuspendBlocker(); // acquire wake lock}//PSensor转换延迟时间mPendingProximityDebounceTime = debounceTime;
}
当设置完毕mPendingProximity和mPendingProximityDebounceTime后,会调用debounceProximitySensor()
方法,读取新的PSensor数据,该方法如下:
private void debounceProximitySensor() {if (mProximitySensorEnabled&& mPendingProximity != PROXIMITY_UNKNOWN&& mPendingProximityDebounceTime >= 0) {//获取当前系统时间final long now = SystemClock.uptimeMillis();//如果mPendingProximityDebounceTime <= now,说明是靠近事件或者延迟事件到达,//直接进行下一步处理if (mPendingProximityDebounceTime <= now) {// Sensor reading accepted. Apply the change then release the wake lock.//mProximity表示反应给系统的PSensor事件值,这里进行赋值表示接受Sensor读取值mProximity = mPendingProximity;//更新状态的核心方法updatePowerState();//更新完成后,清除延迟时间、释放Display锁clearPendingProximityDebounceTime(); // release wake lock (must be last)} else {// Need to wait a little longer.// Debounce again later. We continue holding a wake lock while waiting.//如果mPendingProximityDebounceTime > now,说明有延迟,是远离事件,//通过Handler延迟处理事件,到达延迟时间后会重新调用该方法Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);}}
}
在这个方法中,首先会通过系统当前时间和mPendingProximityDebounceTime进行比较,这个值在setPendingProximityDebounceTime()
中设置,前面已经说过。如果当前时间大于等于后者,说明没有延迟,则直接调用下一步逻辑;反之则说明有延迟,通过Handler异步处理延迟,并在到达延迟后会重新调用该方法,进行下一步逻辑的调用,先看看延迟转换的处理:
@Override
public void handleMessage(Message msg) {switch (msg.what) {.......case MSG_PROXIMITY_SENSOR_DEBOUNCED:debounceProximitySensor();break;
因此,PSensor事件不管是延迟转换还是不进行延迟,都会进入该方法的if语句中执行相同的逻辑。
在进入if语句后,首先是将mPendingProximity值设置给mProximity,mProximity表示最终系统的PSensor事件状态值,所以可以理解为接受PSensor新的事件状态值。然后调用updatePowerState()
方法开始更新屏幕状态,最后当upatePowerState()方法执行完毕后,调用clearPendingProximityDebounceTime()
方法,该方法会重置PSensor转换延迟时间、释放Display锁,如下:
private void clearPendingProximityDebounceTime() {if (mPendingProximityDebounceTime >= 0) {//重置PSensor转换时间mPendingProximityDebounceTime = -1;//释放Display锁mCallbacks.releaseSuspendBlocker(); // release wake lock}
}
现在我们开机进入updatePowerState()方法中进行分析,这个方法在第一篇中已经分析了,更详细的分析请见第一篇内容.这里主要来看PSensor相关的部分:
private void updatePowerState() {//....................................// Apply the proximity sensor.if (mProximitySensor != null) {//如果mPowerRequest.useProximitySensor=true&&Display状态不等于灭屏状态if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {//设置Psensor可用setProximitySensorEnabled(true);if (!mScreenOffBecauseOfProximity&& mProximity == PROXIMITY_POSITIVE) {//该值表示是否由PSensor灭屏mScreenOffBecauseOfProximity = true;//通过DisplayPowerCallback回调PMSsendOnProximityPositiveWithWakelock();}//如果释放PSensor锁时带有PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY标记,则该值为true} else if (mWaitingForNegativeProximity&& mScreenOffBecauseOfProximity&& mProximity == PROXIMITY_POSITIVE&& state != Display.STATE_OFF) {setProximitySensorEnabled(true);} else {//设置PSensor不可用setProximitySensorEnabled(false);mWaitingForNegativeProximity = false;}//如果满足说明此时PSensor处理远离事件,重置mScreenOffBecauseOfProximity为falseif (mScreenOffBecauseOfProximity&& mProximity != PROXIMITY_POSITIVE) {mScreenOffBecauseOfProximity = false;sendOnProximityNegativeWithWakelock();}} else {mWaitingForNegativeProximity = false;}
//由PSensor灭屏为true,则将state置位DISPLAY.STATE_OFFif (mScreenOffBecauseOfProximity) {state = Display.STATE_OFF;}/*-------------PSensor设置 end--------------*///获取屏幕状态,此时还未设置新的屏幕状态,因此是”旧”的final int oldState = mPowerState.getScreenState();//在这个方法中会进行屏幕状态、亮度的设置和处理亮灭屏动画animateScreenStateChange(state, performScreenOffTransition);//获取屏幕状态,此时已经设置新的屏幕状态state = mPowerState.getScreenState();// ........................
}
现在来分析一下updatePowerState()方法的以上逻辑。对于以上逻辑,我们分别对PSensor灭屏和PSensor亮屏逻辑分开进行分析。
1.PSensor灭屏
先看看下PSensor灭屏的逻辑,如果PMS请求时携带的useProximitySensor 值为true,表示此时系统持有PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK
锁,同时如果当前屏幕状态不为灭屏状态(Display.STATE_OFF),则:
- 1.通过setProximitySensorEnabled()方法将PSensor设置为enable状态,即可用状态;
- 2.将mScreenOffBecauseOfProximity值设置为true,表示由PSensor灭屏,并将请求屏幕状态值设置为Display.STATE_OFF;
- 3.通过sendOnProximityPositiveWithWakelock()方法回调PMS中,通知PMS在DIC中进行了PSensor灭屏。
setProximitySensorEnabled()方法如下:
private void setProximitySensorEnabled(boolean enable) {if (enable) {if (!mProximitySensorEnabled) {// Register the listener.// Proximity sensor state already cleared initially.mProximitySensorEnabled = true;//将PSensor设置为可用状态//注册PSensor事件接收器mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,SensorManager.SENSOR_DELAY_FASTEST, mHandler);}} else {//设置PSensor不可用状态if (mProximitySensorEnabled) {// Unregister the listener.// Clear the proximity sensor state for next time.mProximitySensorEnabled = false;//将PSensor设置为不可用状态mProximity = PROXIMITY_UNKNOWN;//重置mProximity值为初始值mPendingProximity = PROXIMITY_UNKNOWN;//重置mPendingProximity值为初始值mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);//解除PSensor事件接收器mSensorManager.unregisterListener(mProximitySensorListener);clearPendingProximityDebounceTime(); // 清除去抖动时间,释放Display锁}}
}
在setProximitySensorEnabled()
中,根据传入参数决定PSensor是否可用,同时注册或解除PSensor事件接收器,这个接收器就是刚开始分析时监听PSensor上报值的Listener,实际上,这部分逻辑应该在接受PSensor上报值之前,因为只有注册了接受器,才能接收上报值,但便于流程分析,就放在这里分析了。
sendOnProximityPositiveWithWakelock()
方法如下:
private void sendOnProximityPositiveWithWakelock() {//回调到PMS中,申请Display锁mCallbacks.acquireSuspendBlocker();mHandler.post(mOnProximityPositiveRunnable);
}
private final Runnable mOnProximityPositiveRunnable = new Runnable() {@Overridepublic void run() {//将PMS中mProximityPositive值置为truemCallbacks.onProximityPositive();//释放Display锁mCallbacks.releaseSuspendBlocker();}
};
在以上方法中,mCallbacks对象就是PMS中mDisplayPowerCallbacks对象,因此这里会回调到PMS中去,在PMS中回调的三个方法如下:
@Override
public void acquireSuspendBlocker() {mDisplaySuspendBlocker.acquire();
}
@Override
public void releaseSuspendBlocker() {mDisplaySuspendBlocker.release();
}
@Override
public void onProximityPositive() {synchronized (mLock) {mProximityPositive = true;mDirty |= DIRTY_PROXIMITY_POSITIVE;updatePowerStateLocked();}
}
前两个方法用来申请、释放Display锁,onProximityPositive()方法中内容稍后分析。
回到DPC中,当执行完以上流程后,调用animateScreenStateChange()
设置屏幕状态、背光值,除一点外:
当执行进入到setScreenState()
后:
private boolean setScreenState(int state, boolean reportOnly) {//......final boolean isOff = (state == Display.STATE_OFF);if (isOff && !mScreenOffBecauseOfProximity) {setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);}//......}
也就是说,PSensor灭屏,不会设置mReportedScreenStateToPolicy
值,所以PSensor灭屏后,这个值依旧为亮屏时的REPORTED_TO_POLICY_SCREEN_ON(2)
,所以不会有blockScreenOff()
和unblockScreenOff()
的调用。
之后的流程就和Power键灭屏流程相同了,这里就不再分析,请参考DisplayPowerController第一篇分析。
PSensor灭屏时PMS中做了什么?
在上面我们分析到了PMS中的回调,现在继续看看回调onProximityPositive()时,更细节的内容。
onProximityPositive()
方法中,首先会将mProximityPositive置为true,然后置位mDirty,最后调用PMS核心updatePowerStateLocked()方法。需要注意的是,在调用updatePowerStateLocked()
方法时,由于mDirty值不满足条件,这里不会执行其中的几个updatexxx()方法,只会执行最后一个updateSuspendBlockerLocked()
方法,我们只看以下部分:
private boolean needDisplaySuspendBlockerLocked() {//请求Policy为亮屏if (mDisplayPowerRequest.isBrightOrDim()) {//以下条件任意一个为false,则表示需要Display锁,因此不会进入Suspend状态if (!mDisplayPowerRequest.useProximitySensor || !mProximityPositive|| !mSuspendWhenScreenOffDueToProximityConfig) {return true;}}return false;
}
以上方法逻辑很简单,其中三个条件任意一个为false,则该方法将返回true,不会释放Display锁。
mSuspendWhenScreenOffDueToProximityConfig
表示当由PSensor灭屏后,是否可以进入Suspend状态,该值从配置文件中获取:
mSuspendWhenScreenOffDueToProximityConfig = resources.getBoolean(com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity);
因此如果该值配置为true,则表示当PSensor灭屏后可以进入Suspend状态,实现原因就是这部分逻辑。
再来看看mProximityPositive这个值的用处,除了上面判断是否需要Display锁之外,还有一个地方:
private boolean isBeingKeptAwakeLocked() {return mStayOn|| mProximityPositive|| (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT| USER_ACTIVITY_SCREEN_DIM)) != 0|| mScreenBrightnessBoostInProgress;
}
这个方法在分析PMS时分析了,判断是否需要保持唤醒状态,只要任意一个条件为true,则系统就不能进入休眠中状态。
因此,当PSensor灭屏后,不会存在超时进入GoToSleep的逻辑。
2.PSensor亮屏
现在继续回到DPC中,来看看PSensor亮屏时的updatePowerState()
方法。在调用到updatePowerState()方法之前,PSensor事件接收器接收到远离事件上报值,将mProximity值置为PROXIMITY_NEGATIVE,然后调用到updatePowerState()后,根据条件判断,则:
- 1.将mScreenOffBecauseOfProximity值置为false;
- 2.通过sendOnProximityNegativeWithWakelock()方法回调PMS中,通知PMS在DIC中进行了PSensor亮屏。
此时PMS中请求的屏幕状态依旧为Display.STATE_ON
,接下来的步骤和原来的步骤相同,除一点外:
当执行进入到setScreenState()
后:
isOff = (state == Display.STATE_OFF) = false;
mReportedScreenStateToPolicy = REPORTED_TO_POLICY_SCREEN_ON;
所以不满足任何一个条件,因此,不会有blockScreenOn()
和blockScreenOn()
,也就是说,PSensor的亮屏不需要WindowManger中的相关回调。
剩下的流程和Power键等亮屏流程完全一样。
这里再来看下sendOnProximityNegativeWithWakelock()
方法:
private void sendOnProximityNegativeWithWakelock() {mCallbacks.acquireSuspendBlocker();mHandler.post(mOnProximityNegativeRunnable);
}
private final Runnable mOnProximityNegativeRunnable = new Runnable() {@Overridepublic void run() {mCallbacks.onProximityNegative();mCallbacks.releaseSuspendBlocker();}
};
这个方法中还是回调到PMS中的逻辑,其中释放锁和申请锁的已经分析过,再来看看onProximityNegative()
方法:
@Override
public void onProximityNegative() {synchronized (mLock) {//将mProximityPositive 置为falsemProximityPositive = false;mDirty |= DIRTY_PROXIMITY_POSITIVE;//更新用户活动时间userActivityNoUpdateLocked(SystemClock.uptimeMillis(),PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);updatePowerStateLocked();}
}
在这个方法中,将mProximityPositive
置为false,同时更新用户活动时间,然后执行updatePowerStateLocked()
方法。
总结
分析到这里,整个PSensor亮灭屏的流程就总结完毕,并且从整个分析来看,PSensor亮灭屏,仅仅是修改屏幕状态的背光值,不会像Power键灭屏一样,使系统进入休眠状态。
然而,正是这个原因,在PSensor灭屏后,如果按Power键,是不会亮屏的,这有时候会带来一些用户体验问题,现在市面上好多手机,都会这块有修改,即在PSensor灭屏后,可以通过Power键亮屏,即PSensor灭屏也走休眠流程,这样做的好处显而易见;但是这样一来,好像违反了PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK
锁的设计初衷了(该锁表示如果由PSensor导致的灭屏,那么是不会进入休眠状态的, 被视为是持续的用户活动。)
常见问题
Q:PSensor亮灭屏和其他亮灭屏区别?
- 1.PSensor亮灭屏不存在Wakeup和goToSleep流程;
- 2.PSensor亮灭屏不需要WindowManager中的回调(unblockScreenOn).
Q:PSensor灭屏后,到达设置休眠时间后会不会进入休眠?
不会,原因如下:
private boolean isBeingKeptAwakeLocked() {return mStayOn|| mProximityPositive //为true,该方法返回true|| (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT| USER_ACTIVITY_SCREEN_DIM)) != 0|| mScreenBrightnessBoostInProgress;
}
Q:PSensor灭屏后按Power键,为何不会亮屏?
通过对整个DisplayPowerController的流程分析,在通话过程中,对于PSensor灭屏,只会修改其屏幕状态和背光,因此,如果在PSensor灭屏后,第一次按Power键,此时PMS中向Display请求状态时,mWakefulness值为sleep,即要进行睡眠,系统执行goToSleep流程。由于屏幕状态已经为STATE_OFF,背光值为0,因此只会做setScreenState()之前的工作,如调用WindowManager.setScreenTurnedOff,setScreenTruningOff()
等,完成真正意义上的灭屏。
而当goToSleep时,PMS中会忽略掉PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK
类型的WakeLock锁:
if (mWakefulness == WAKEFULNESS_ASLEEP) {mWakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;}
因此,当进入DisplayPowerController后,mPowerRequest.useProximitySensor
将为false,结合updatePowerState()
中的判断条件,此时PSensor将会被解除注册,之后将收不到PSensor消息。
接着,我们第二次按Power键,此时系统将执行wakeUp流程,当进入DisplayPowerController后,mPowerRequest.useProximitySensor
将为true,结合updatePowerState()
中的判断条件,此时PSensor将会被重新注册,由于用户操作未远离设备,故将立即上报PSensor靠近时间,从而由将屏幕设置为灭屏状态,之后如果抬起PSensor,将才会亮屏。
此外,如果由PSensor灭屏后,当释放PSensor的WakeLock时,携带flag值PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY
,此时按Power键也不会立即唤醒,这种情况下DisplayPowerController中的参数mWaitingForNegativeProximity
为true,之后的执行逻辑和上面分析的差不多。
Q:如何修改使得在PSensor灭屏后,可以被Power键亮屏,在Power键灭屏后,可以被PSensor亮屏?
暂无
Android 8.1 DisplayPowerController(二) Proximity Sensor的亮灭屏相关推荐
- Android 系统(41)---Android7.0 PowerManagerService亮灭屏分析(二)
Android7.0 PowerManagerService亮灭屏分析(二) 3029 在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerReque ...
- Android 系统(40)--Android7.0 PowerManagerService亮灭屏分析(一)
Android7.0 PowerManagerService亮灭屏分析(一) 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现 ...
- Android 系统(42)---Android7.0 PowerManagerService亮灭屏分析(三)
Android7.0 PowerManagerService亮灭屏分析(三) 在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在D ...
- 结合源码探讨Android距离传感器亮灭屏机制
结合源码探讨Android距离传感器亮灭屏机制 本文的分析是基于Android 5.0.0 Google原生代码的. Android中的距离传感器,也就是P-sensors(Proximity Sen ...
- Android7.0 PowerManagerService亮灭屏分析(二)
在PowerManagerService中对各种状态进行判断后,将其数值封装进DisplayPowerRequest中传入DisplayPowerController中进一步处理.在亮屏过程中Disp ...
- Android 知识点 109 —— Android7.0 PowerManagerService 之亮灭屏
原文地址: https://www.cnblogs.com/dyufei/p/8017604.html 写的太好了,粘过来! 本篇从按下power按键后,按键事件从InputManagerServic ...
- Android7.0 PowerManagerService 之亮灭屏(二) PMS 电源状态管理updatePowerStateLocked()...
本篇注意接着上篇[Android7.0 PowerManagerService 之亮灭屏(一)]继续分析量灭屏的流程,这篇主要分析PMS的状态计算和更新流程,也是PMS中最为重要和复杂的一部分电源状态 ...
- Android 8.0 手机亮灭屏
本文主要跟踪分析通过按松power键来唤醒,熄灭屏幕的逻辑.下面是一些相关类的介绍 PowerManagerService.java:简称PMS,负责Andorid系统中电源管理方面的工作.作为系统核 ...
- android 亮灭屏流程
1. frameworks\base\core\java\android\hardware\display\DisplayManagerInternal.java 内部服务 frameworks\ba ...
最新文章
- 模型数据的保存和读取
- JavaScript改变 HTML 内容
- mac terminal常用命令接触
- socket编程 及select poll epoll示例
- linux 酷炫的命令行
- 204. Count Primes
- 高扫后督解决方案 力助银行内部核查
- Package vim is not available, but is referred to by another package.
- Zookeeper 安装部署
- ***PHP各种编码的汉字字符串截取
- The Windows Phone Emulator wasn't able to create the external network switches 解决方法
- 让你的Hybrid App听懂你的话(Android篇)
- 区块链 single共识
- HTML 打开新页面 关闭,javascript打开新窗口同时关闭旧窗口
- 为什么浏览器要阻止跨域
- 用asp.net写的一个购物网站
- [转载]项目风险管理七种武器-长生剑
- 页式存储中的逻辑地址与物理地址之间的解析过程
- 《仿大众点评仿美团做一个评价网站——Java SSM》项目研发阶段性总结
- 泰克示波器CVI开发|泰克示波器波形抓取数据控制软件NS-Scope
热门文章
- (原创)六度拓扑(www.6dtop.com)---超乎想像的人际关系网络
- linux如何更改密钥环密码,Linux系统教程:Ubuntu桌面上禁用默认的密钥环解锁提示...
- 关于销售订单高级定价的一点疑惑
- oracle安装后,电脑变得很卡,解决办法(安装的是oracle11g)
- 结合《穹顶之下》看中、美宽带提速
- SSR门户项目爬坑之路(一)
- (附源码)springBoot高校宿舍交电费系统 毕业设计 031552
- IDEA生成springboot项目的两种方式
- 西安恒智小寨java_长安反编译工具 java
- 电脑误删的文件怎么恢复?分享90%的人都会的这2招