结合源码探讨Android距离传感器亮灭屏机制

本文的分析是基于Android 5.0.0 Google原生代码的。

Android中的距离传感器,也就是P-sensors(Proximity Sensors),与G-sensor重力传感器所对应。最常见的应用场景在于,当使用安卓手机接听电话时,贴近屏幕时,屏幕会灭屏,以防止脸部触碰屏幕引起误操作。当我们的脸离开屏幕时,屏幕会自动亮屏,方便我们操作手机,打开键盘输入指令。

开启和关闭距离传感器的方法位于DisplayPowerController的updatePowerState()方法当中。

[java] view plaincopy
  1. private void setProximitySensorEnabled(boolean enable) {
  2. if (enable) {
  3. if (!mProximitySensorEnabled) {
  4. // Register the listener.
  5. // Proximity sensor state already cleared initially.
  6. mProximitySensorEnabled = true;
  7. mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
  8. SensorManager.SENSOR_DELAY_NORMAL, mHandler);
  9. }
  10. } else {
  11. if (mProximitySensorEnabled) {
  12. // Unregister the listener.
  13. // Clear the proximity sensor state for next time.
  14. mProximitySensorEnabled = false;
  15. mProximity = PROXIMITY_UNKNOWN;
  16. mPendingProximity = PROXIMITY_UNKNOWN;
  17. mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
  18. mSensorManager.unregisterListener(mProximitySensorListener);
  19. clearPendingProximityDebounceTime(); // release wake lock (must be last)
  20. }
  21. }

该方法位于文件/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController当中。

这个方法的逻辑非常清晰,enable参数是一个boolean类型变量,enable等于true代表启用距离传感器,enable等于false代表关闭或停用距离传感器。

mProximitySensorEnabled参数用于表示当前距离传感器是否正在启用。

1. 当enable等于true且当前距离传感器不处于启用的状态的情况下,我们就在SensorManager注册一个类型为mProximitySensorListener的监听器。

2. 当enable等于false且当前距离传感器已经启用的状态下,我们便取消监听这个Listener。

接下来看下mProximitySensorListener的定义。

[java] view plaincopy
  1. private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
  2. @Override
  3. public void onSensorChanged(SensorEvent event) {
  4. if (mProximitySensorEnabled) {
  5. final long time = SystemClock.uptimeMillis();
  6. final float distance = event.values[0];
  7. boolean positive = distance >= 0.0f && distance < mProximityThreshold;
  8. handleProximitySensorEvent(time, positive);
  9. }
  10. }
  11. @Override
  12. public void onAccuracyChanged(Sensor sensor, int accuracy) {
  13. // Not used.
  14. }
  15. };

这个监听器的声明位于同一文件/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController当中。

可以发现,mProximitySensorListener是一个传感器事件监听器,并且重写了onSensorChanged方法。

其执行的主要操作是对positive这个变量进行赋值。在接下来的流程当中,我们会看到positive和negative是一个相对的流程。

如果distance小于一定的范围(mProximityThreshold内),positive为true。如果大于此阀值,positive就为false,换而言之,也就是negative的意思。

[java] view plaincopy
  1. // The actual proximity sensor threshold value.
  2. private float mProximityThreshold;

mProximityThreshold的赋值位于DisplayPowerController类的构造函数当中。

[java] view plaincopy
  1. /**
  2. * Creates the display power controller.
  3. */
  4. public DisplayPowerController(Context context,
  5. DisplayPowerCallbacks callbacks, Handler handler,
  6. SensorManager sensorManager, DisplayBlanker blanker) {
  7. //......
  8. if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
  9. mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
  10. if (mProximitySensor != null) {
  11. //在这里定义
  12. mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
  13. TYPICAL_PROXIMITY_THRESHOLD);
  14. }
  15. }
  16. }

在对positive进行完赋值之后,调用handleProximitySensorEvent(time, positive)方法。

[java] view plaincopy
  1. private void handleProximitySensorEvent(long time, boolean positive) {
  2. if (mProximitySensorEnabled) {
  3. if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
  4. return; // no change
  5. }
  6. if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
  7. return; // no change
  8. }
  9. // Only accept a proximity sensor reading if it remains
  10. // stable for the entire debounce delay.  We hold a wake lock while
  11. // debouncing the sensor.
  12. mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
  13. if (positive) {
  14. mPendingProximity = PROXIMITY_POSITIVE;
  15. setPendingProximityDebounceTime(
  16. time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock
  17. } else {
  18. mPendingProximity = PROXIMITY_NEGATIVE;
  19. //这边会有一个额外的250ms防抖动延迟时间
  20. setPendingProximityDebounceTime(
  21. time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock
  22. }
  23. // Debounce the new sensor reading.
  24. debounceProximitySensor();
  25. }
  26. }

该方法位于文件/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController当中。

//里面出现了典型的Handler.removeMessage的延时操作。具体这是一个什么样的过程,我会在以后专门开辟一篇文章提及。

原生代码中对mPendingProximityDebounceTime的额外增加时间如下:

[java] view plaincopy
  1. // Proximity sensor debounce delay in milliseconds for positive or negative transitions.
  2. private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
  3. private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;

先设置好debounceTime。

[java] view plaincopy
  1. private void setPendingProximityDebounceTime(long debounceTime) {
  2. if (mPendingProximityDebounceTime < 0) {
  3. mCallbacks.acquireSuspendBlocker(); // acquire wake lock
  4. }
  5. mPendingProximityDebounceTime = debounceTime;
  6. }

该方法位于文件/frameworks/base/services/core/Java/com/android/server/display/DisplayPowerController当中。

然后更新Debounce处理后的mProximity值。

[java] view plaincopy
  1. private void debounceProximitySensor() {
  2. if (mProximitySensorEnabled
  3. && mPendingProximity != PROXIMITY_UNKNOWN
  4. && mPendingProximityDebounceTime >= 0) {
  5. final long now = SystemClock.uptimeMillis();
  6. if (mPendingProximityDebounceTime <= now) {
  7. // Sensor reading accepted.  Apply the change then release the wake lock.
  8. mProximity = mPendingProximity;  //非常重要,传感器触发的最后结果就是设置这个标志位
  9. updatePowerState(); //重新更新屏幕的显示Display状态
  10. clearPendingProximityDebounceTime(); // release wake lock (must be last)
  11. } else {
  12. // Need to wait a little longer.
  13. // Debounce again later.  We continue holding a wake lock while waiting.
  14. Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
  15. //如果时间没到会再次发送一次新的消息,在恰当的时间再调用debounceProximitySensor()方法
  16. msg.setAsynchronous(true);
  17. mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
  18. }
  19. }
  20. }

该方法位于文件/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController当中。

为什么会在监听到negative事件的加一个250ms的延迟处理呢,主要是为了防抖动。当用户拿在手上操作时,很多时候会使手机处于一种抖动颠簸状态。为了防止接下来频繁地刷新Display和Power状态,原生代码加入了这样的一个考虑。不少手机厂商会修改,适当增加会减小这个值以获得更好的用户体验和用户反馈。

[java] view plaincopy
  1. private void updatePowerState() {
  2. //......截录传感器处理的这一部分
  3. // Apply the proximity sensor.
  4. if (mProximitySensor != null) {
  5. //当距离传感器的锁被申请之后,且不是亮屏时,该条件满足
  6. if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
  7. setProximitySensorEnabled(true);
  8. //距离传感器被遮挡时,会调用回调函数
  9. if (!mScreenOffBecauseOfProximity
  10. && mProximity == PROXIMITY_POSITIVE) {
  11. mScreenOffBecauseOfProximity = true;
  12. sendOnProximityPositiveWithWakelock();
  13. }
  14. } else if (mWaitingForNegativeProximity
  15. && mScreenOffBecauseOfProximity
  16. && mProximity == PROXIMITY_POSITIVE
  17. && state != Display.STATE_OFF) {
  18. setProximitySensorEnabled(true);
  19. } else {
  20. //当没有距离传感器的锁被申请时,将距离传感器停用
  21. setProximitySensorEnabled(false);
  22. mWaitingForNegativeProximity = false;
  23. }
  24. //离开了屏幕一定范围
  25. if (mScreenOffBecauseOfProximity
  26. && mProximity != PROXIMITY_POSITIVE) {
  27. mScreenOffBecauseOfProximity = false;
  28. sendOnProximityNegativeWithWakelock();
  29. }
  30. } else {
  31. mWaitingForNegativeProximity = false;
  32. }
  33. if (mScreenOffBecauseOfProximity) {
  34. state = Display.STATE_OFF;
  35. }
  36. //......
  37. }

该方法位于文件/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController当中。

先对几个变量进行解释:

1. mScreenOffBecauseOfProximity 如果为true则代表屏幕是因为距离传感器而熄灭的。

[java] view plaincopy
  1. // True if the screen was turned off because of the proximity sensor.
  2. // When the screen turns on again, we report user activity to the power manager.
  3. private boolean mScreenOffBecauseOfProximity;

2. mProximity代表经过防抖处理后的传感器状态。

3. mWaitingForNegativeProximity

[java] view plaincopy
  1. // True if the device should wait for negative proximity sensor before
  2. // waking up the screen.  This is set to false as soon as a negative
  3. // proximity sensor measurement is observed or when the device is forced to
  4. // go to sleep by the user.  While true, the screen remains off.
  5. private boolean mWaitingForNegativeProximity;

注释写的非常精彩,我在这里就仅仅做一次大自然的搬运工:

mWaitingForNegativeProximity为true代表屏幕应当等到距离传感器检测到距离增大后才变亮。

有两种情况会将这个变量设置为false: ①是检测到一个negative的传感器监听器事件发生(距离大于阀值) ②用户按下power键强制使手机进入休眠

这个方法中最关键的最需要理解的部分,位于其多个if else-if else逻辑之间。

此方法调用到的sendOnProximityPositiveWithWakelock()、sendOnProximityNegativeWithWakelock()将回调PowerManagerService的方法。

[java] view plaincopy
  1. private void sendOnProximityPositiveWithWakelock() {
  2. mCallbacks.acquireSuspendBlocker();
  3. mHandler.post(mOnProximityPositiveRunnable);
  4. }
  5. private final Runnable mOnProximityPositiveRunnable = new Runnable() {
  6. @Override
  7. public void run() {
  8. mCallbacks.onProximityPositive();
  9. mCallbacks.releaseSuspendBlocker();
  10. }
  11. };
  12. //--------------------------------------------------------
  13. private void sendOnProximityNegativeWithWakelock() {
  14. mCallbacks.acquireSuspendBlocker();
  15. mHandler.post(mOnProximityNegativeRunnable);
  16. }
  17. private final Runnable mOnProximityNegativeRunnable = new Runnable() {
  18. @Override
  19. public void run() {
  20. mCallbacks.onProximityNegative();
  21. mCallbacks.releaseSuspendBlocker();
  22. }
  23. };

以sendOnProximityPositiveWithWakelock()为例,我们了解下,在DisplayPowerController.java是怎么调用到PowerMS中的回调方法了。

因为读者即便借助SourceInsight和Eclipse等代码阅读工具,如果不梳理清楚DisplayPowerController和PowerManagerService是如何建立联系的,也难以确认mCallbacks就是PowerManagerService中的回调。

mCallbacks在DisplayPowerController.java文件中的定义如下:

[java] view plaincopy
  1. // Asynchronous callbacks into the power manager service.
  2. // Only invoked from the handler thread while no locks are held.
  3. private final DisplayPowerCallbacks mCallbacks;

此处提供的信息是:mCallbacks是DisplayPowerCallbacks类型的的callbacks,而且是PowerManagerService中的异步回调。

同时在此文件中又能看到DispalyPowerController的构造函数中,有这么一句:

[java] view plaincopy
  1. /**
  2. * Creates the display power controller.
  3. */
  4. public DisplayPowerController(Context context,
  5. DisplayPowerCallbacks callbacks, Handler handler,
  6. SensorManager sensorManager, DisplayBlanker blanker) {
  7. mHandler = new DisplayControllerHandler(handler.getLooper());
  8. //
  9. mCallbacks = callbacks;
  10. //......
  11. }

系统关键服务启动的时候,往往会调用SystemReady()方法告知系统此服务已经就绪。

PowerManagerService的SystemReady():

[java] view plaincopy
  1. public void systemReady(IAppOpsService appOps) {
  2. synchronized (mLock) {
  3. //......
  4. //此处mDisplayManagerInternal初始化为DisplayManagerService.LocalService
  5. mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
  6. // Initialize display power management.
  7. mDisplayManagerInternal.initPowerManagement(
  8. mDisplayPowerCallbacks, mHandler, sensorManager);
  9. //......
  10. }
  11. }

此方法位于文件/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java中。

getLocalService是PowerManagerService的父类SystemService的私有方法,且PowerManagerService并没有对它进行重写。在SystemServer启动时,会启动DisplayManagerService,并调用DisplayManagerService的startService方法把此DisplayManagerService的LocalService加入到一个HASHMAP中,使其能够通过getLocalService方法获取出来。此处不做深入探讨。这一流程要做的就是实例化mDisplayManagerInternal这个变量,并调用它的initPowerManagerment方法。

[java] view plaincopy
  1. private final class LocalService extends DisplayManagerInternal {
  2. @Override
  3. public void initPowerManagement(final DisplayPowerCallbacks callbacks, Handler handler,
  4. SensorManager sensorManager) {
  5. synchronized (mSyncRoot) {
  6. DisplayBlanker blanker = new DisplayBlanker() {
  7. @Override
  8. public void requestDisplayState(int state) {
  9. // The order of operations is important for legacy reasons.
  10. if (state == Display.STATE_OFF) {
  11. requestGlobalDisplayStateInternal(state);
  12. }
  13. callbacks.onDisplayStateChange(state);
  14. if (state != Display.STATE_OFF) {
  15. requestGlobalDisplayStateInternal(state);
  16. }
  17. }
  18. };
  19. //一路传递下来,终于在此处将PowerManagerService的Callbacks传递到了DisplayPowerController当中
  20. mDisplayPowerController = new DisplayPowerController(
  21. mContext, callbacks, handler, sensorManager, blanker);
  22. }
  23. }
  24. @Override
  25. public boolean requestPowerState(DisplayPowerRequest request,
  26. boolean waitForNegativeProximity) {
  27. return mDisplayPowerController.requestPowerState(request,
  28. waitForNegativeProximity);
  29. }
  30. @Override
  31. public boolean isProximitySensorAvailable() {
  32. return mDisplayPowerController.isProximitySensorAvailable();
  33. }
  34. @Override
  35. public DisplayInfo getDisplayInfo(int displayId) {
  36. return getDisplayInfoInternal(displayId, Process.myUid());
  37. }
  38. @Override
  39. public void registerDisplayTransactionListener(DisplayTransactionListener listener) {
  40. if (listener == null) {
  41. throw new IllegalArgumentException("listener must not be null");
  42. }
  43. registerDisplayTransactionListenerInternal(listener);
  44. }
  45. @Override
  46. public void unregisterDisplayTransactionListener(DisplayTransactionListener listener) {
  47. if (listener == null) {
  48. throw new IllegalArgumentException("listener must not be null");
  49. }
  50. unregisterDisplayTransactionListenerInternal(listener);
  51. }
  52. @Override
  53. public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) {
  54. setDisplayInfoOverrideFromWindowManagerInternal(displayId, info);
  55. }
  56. @Override
  57. public void performTraversalInTransactionFromWindowManager() {
  58. performTraversalInTransactionFromWindowManagerInternal();
  59. }
  60. @Override
  61. public void setDisplayProperties(int displayId, boolean hasContent,
  62. float requestedRefreshRate, boolean inTraversal) {
  63. setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate, inTraversal);
  64. }
  65. }

此内部类定义位于文件/frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.java中。

在梳理清楚后,我们理一下思绪,回头重新PowerManagerService中的回调函数的"庐山真面目"吧。

[java] view plaincopy
  1. private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =
  2. new DisplayManagerInternal.DisplayPowerCallbacks() {
  3. private int mDisplayState = Display.STATE_UNKNOWN;
  4. @Override
  5. public void onStateChanged() {
  6. synchronized (mLock) {
  7. mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
  8. updatePowerStateLocked();
  9. }
  10. }
  11. @Override
  12. public void onProximityPositive() {
  13. synchronized (mLock) {
  14. mProximityPositive = true;
  15. mDirty |= DIRTY_PROXIMITY_POSITIVE;
  16. updatePowerStateLocked();
  17. }
  18. }
  19. @Override
  20. public void onProximityNegative() {
  21. synchronized (mLock) {
  22. mProximityPositive = false;
  23. mDirty |= DIRTY_PROXIMITY_POSITIVE;
  24. userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
  25. PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
  26. updatePowerStateLocked();
  27. }
  28. }
  29. @Override
  30. public void onDisplayStateChange(int state) {
  31. // This method is only needed to support legacy display blanking behavior
  32. // where the display's power state is coupled to suspend or to the power HAL.
  33. // The order of operations matters here.
  34. synchronized (mLock) {
  35. if (mDisplayState != state) {
  36. mDisplayState = state;
  37. if (state == Display.STATE_OFF) {
  38. if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
  39. setHalInteractiveModeLocked(false);
  40. }
  41. if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
  42. setHalAutoSuspendModeLocked(true);
  43. }
  44. } else {
  45. if (!mDecoupleHalAutoSuspendModeFromDisplayConfig) {
  46. setHalAutoSuspendModeLocked(false);
  47. }
  48. if (!mDecoupleHalInteractiveModeFromDisplayConfig) {
  49. setHalInteractiveModeLocked(true);
  50. }
  51. }
  52. }
  53. }
  54. }
  55. @Override
  56. public void acquireSuspendBlocker() {
  57. mDisplaySuspendBlocker.acquire();
  58. }
  59. @Override
  60. public void releaseSuspendBlocker() {
  61. mDisplaySuspendBlocker.release();
  62. }
  63. @Override
  64. public String toString() {
  65. synchronized (this) {
  66. return "state=" + Display.stateToString(mDisplayState);
  67. }
  68. }
  69. };

该方法位于文件/frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java当中。

继续以sendOnProximityPositiveWithWakelock()为例。为了让大家重拾思绪,此处将该方法相关代码再次粘贴出来。

[java] view plaincopy
  1. private void sendOnProximityPositiveWithWakelock() {
  2. mCallbacks.acquireSuspendBlocker();
  3. mHandler.post(mOnProximityPositiveRunnable);
  4. }
  5. private final Runnable mOnProximityPositiveRunnable = new Runnable() {
  6. @Override
  7. public void run() {
  8. mCallbacks.onProximityPositive();
  9. mCallbacks.releaseSuspendBlocker();
  10. }
  11. };

该方法位于文件/frameworks/base/services/core/java/com/android/server/display/DisplayPowerController当中。

此处先调用了mCallbacks.acquireSuspendBlocker()方法申请一个锁。 然后以post一个Runnable的方式调用mCallbacks.onProximityPositive()方法,将PowerManagerService中mDirty的Proximity相关位 置位。代表此处有变化,并调用updatePowerStateLocked方法,刷新改变power状态。最后释放掉mCallbacks自身所持有的锁。

结合源码探讨Android距离传感器亮灭屏机制相关推荐

  1. 结合源码探讨Android系统的启动流程

    结合源码探讨Android系统的启动流程 由于本人能力有限,所考虑或者疏忽错漏的地方或多或少应该存在.同时,Android从启动过程开始,实际上就涉及多个技术难点和多种通信机制的知识点. 基于上面两个 ...

  2. Android Keyguard 亮灭屏流程分析

    文章目录 概述 Keyguard 关键文件 Power 灭屏 超时灭屏 Power 亮屏 FAQ 亮屏慢 概述 Keyguard 锁屏流程: Keyguard 加载时机:一个是开机的时候:另一个是灭屏 ...

  3. android流程点击开机键熄屏,一种基于android系统的灭屏状态下指纹解锁加速亮屏方法与流程...

    本发明涉及android系统解锁显示方法,尤其涉及一种基于android系统的灭屏状态下指纹解锁加速亮屏方法. 背景技术: 目前,随着指纹技术越来越普及,很多android系统设备都带有指纹外设,特别 ...

  4. Android 系统(40)--Android7.0 PowerManagerService亮灭屏分析(一)

    Android7.0 PowerManagerService亮灭屏分析(一) 可以导致手机亮灭屏的因素有多种,而在本文中主要讲解按power键亮灭屏过程以及来电亮屏.在亮灭屏过程power中主要的实现 ...

  5. Android 8.0 手机亮灭屏

    本文主要跟踪分析通过按松power键来唤醒,熄灭屏幕的逻辑.下面是一些相关类的介绍 PowerManagerService.java:简称PMS,负责Andorid系统中电源管理方面的工作.作为系统核 ...

  6. Android 知识点 109 —— Android7.0 PowerManagerService 之亮灭屏

    原文地址: https://www.cnblogs.com/dyufei/p/8017604.html 写的太好了,粘过来! 本篇从按下power按键后,按键事件从InputManagerServic ...

  7. 【Android RTMP】RTMPDumb 源码导入 Android Studio ( 交叉编译 | 配置 CMakeList.txt 构建脚本 )

    文章目录 安卓直播推流专栏博客总结 一. RTMP 协议 二. RTMP 协议使用 三. RTMPDump 源码下载 四. RTMPDump 源码交叉编译 五. RTMPDump 源码导入 Andro ...

  8. 源码分析Android Handler是如何实现线程间通信的

    源码分析Android Handler是如何实现线程间通信的 Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的.开发者从一开始就被告知必须在主线程中进行UI操作.但H ...

  9. Android 系统(42)---Android7.0 PowerManagerService亮灭屏分析(三)

    Android7.0 PowerManagerService亮灭屏分析(三) 在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在D ...

最新文章

  1. vb打开服务器excel文件路径,咨询下VB如何打开EXCEL文件并将内容显示在listbox中
  2. 小马拉大车,无线网络优化
  3. 魅族用鸿蒙系统吗,魅族宣布接入鸿蒙是怎么回事?魅族手机可以刷鸿蒙系统吗?...
  4. 搜索不包含关键词_亚马逊listing关键词优化
  5. P4055 [JSOI2009]游戏
  6. 百度谷歌2013年母亲节 赏析中文搜索引擎庆祝涂鸦
  7. 虚拟主机金华php空间,金华虚拟主机_金华云虚机_金华主机申请_金华网站空间_爱名网(www.22.cn)...
  8. MySQL 指定各分区路径
  9. css3中的过度transition与动画animation以及字体@font-face
  10. 【UWB 定位】室内定位 三边定位算法
  11. matlab中画花瓣,matlab花瓣图的编程原理是什么,向天下大侠求解!!!!给力的? 爱问知识人...
  12. 如何解决移动端 Retina 屏(高清屏)1px 像素问题
  13. JAVA | MongoDB 去重、聚合函数、Criteria Query使用
  14. 苹果笔记本显卡性能测试软件,测试结果来了!新款Macbook Pro显卡性能怎样?
  15. 初学入门YOLOv5手势识别之制作并训练自己的数据集
  16. 采购货物和服务的有效步骤
  17. UIWebView使用app内自定义字体
  18. ABAP 调用webservice 错误
  19. Python 实战系列-微信或网页远程控制电脑
  20. 基于Ethernet KRL,上位机C#通过TCP/IP与KUKA库卡机械臂通讯Demo

热门文章

  1. spring 操作对象写入mongo去除_class列
  2. 支付宝接口调试经验总结
  3. 《高性能Linux服务器构建实战Ⅱ》一书纠错汇总(12月30日更新)
  4. 机器学习三个部分:输入、算法、输出 资料收集
  5. 深度学习 --- 受限玻尔兹曼机RBM(MCMC和Gibbs采样)
  6. 单目摄像头光学图像测距_挑战激光雷达,MAXIEYE要重新定义单目摄像头?
  7. python 空列表对象的布尔值_python – 从TensorFlow对象中检索数据 – 来自correct_prediction的布尔值列表...
  8. 反序列化对象列表发生异常_面试官:你知道Java对象的序列化与反序列化背后的原理吗?...
  9. 三星手机怎么把html,三星手机怎么连接电脑 只要四步轻松搞定【图文】
  10. linux虚拟化技术 教程,Linux上实现虚拟化技术的优势