目录

1 PowerManagerService介绍

1.1 初始化流程

1.2 PowerManagerService构造函数


作者:ProgramAndroid
链接:https://www.jianshu.com/p/7751c7d740eb
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1 PowerManagerService介绍

PowerManagerService 提供Android系统的电源管理服务,主要功能是控制系统待机状态,屏幕显示,亮度调节,光线/距离传感器的控制等。

相关代码在以下文件中:

frameworks/base/services/java/com/android/server/SystemServer.java
frameworks/base/core/java/android/os/PowerManager.java
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
frameworks/base/core/java/android/os/PowerManagerInternal.java
frameworks/base/services/core/java/com/android/server/power/Notifier.java
device/qcom/common/power/power.c
system/core/libsuspend/autosuspend.c
hardware/libhardware_legacy/power/power.c

1.1 初始化流程

跟其他系统服务一样,PowerManagerService也是继承于SystemService并通过SystemServer启动。
SystemServer
frameworks/base/services/java/com/android/server/SystemServer.java

private void startBootstrapServices() {......mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);......
}

1.2 PowerManagerService构造函数

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

关于Watchdog.Monitor 接口,实现了就是一个空实现啊,感觉没什么用。

public final class PowerManagerService extends SystemServiceimplements Watchdog.Monitor {......
}

在SystemServer的startBootstrapServices中,通过SystemServiceManager.startService启动了PowerManagerService,下面首先来看PowerManagerService构造方法。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

public PowerManagerService(Context context) {super(context);// mContext赋值为SystemContextmContext = context;// 创建消息处理线程并启动,创建关联消息处理线程的handler对象mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);mHandlerThread.start();mHandler = new PowerManagerHandler(mHandlerThread.getLooper());qcNsrmPowExt = new QCNsrmPowerExtension(this);synchronized (mLock) {// 创建"PowerManagerService.WakeLocks"的SuspendBlockermWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");// 创建"PowerManagerService.Display"的SuspendBlockermDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");// 请求DisplaySuspendBlocker,禁止系统进入休眠mDisplaySuspendBlocker.acquire();mHoldingDisplaySuspendBlocker = true;mHalAutoSuspendModeEnabled = false;mHalInteractiveModeEnabled = true;// 设置mWakefulness为唤醒状态mWakefulness = WAKEFULNESS_AWAKE;// 进入到native层初始化nativeInit();nativeSetAutoSuspend(false);nativeSetInteractive(true);nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);}
}

PowerManagerService构造函数中首先创建了处理消息的进程及对应的handler对象以进行消息处理,然后创建SuspendBlocker对象,用于WakeLocks与Display,并设置mWakefulness的初始状态为WAKEFULNESS_AWAKE,最后进入到native层初始化。下面先看一下关于mWakefulness的定义。

PowerManagerService构造函数中首先创建了处理消息的进程及对应的handler对象以进行消息处理,然后创建SuspendBlocker对象,用于WakeLocks与Display,并设置mWakefulness的初始状态为WAKEFULNESS_AWAKE,最后进入到native层初始化。下面先看一下关于mWakefulness的定义。

>>> frameworks/base/core/java/android/os/PowerManagerInternal.java/*** 设备处于休眠状态,只能被wakeUp()唤醒.*/
public static final int WAKEFULNESS_ASLEEP = 0;/*** 设备处于正常工作(fully awake)状态.*/
public static final int WAKEFULNESS_AWAKE = 1;/*** 设备处于播放屏保状态.*/
public static final int WAKEFULNESS_DREAMING = 2;/*** 设备处于doze状态,只有低耗电的屏保可以运行,其他应用被挂起.*/
public static final int WAKEFULNESS_DOZING = 3;

继续回到PowerManagerService构造函数的native初始化中,首先来看nativeInit的实现。
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static const JNINativeMethod gPowerManagerServiceMethods[] = {/* name, signature, funcPtr */{ "nativeInit", "()V",(void*) nativeInit },{ "nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V",(void*) nativeAcquireSuspendBlocker },{ "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",(void*) nativeReleaseSuspendBlocker },{ "nativeSetInteractive", "(Z)V",(void*) nativeSetInteractive },{ "nativeSetAutoSuspend", "(Z)V",(void*) nativeSetAutoSuspend },{ "nativeSendPowerHint", "(II)V",(void*) nativeSendPowerHint },{ "nativeSetFeature", "(II)V",(void*) nativeSetFeature },
};

PowerManagerService中的native方法定义如上,nativeInit即调用nativeInit()。
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static void nativeInit(JNIEnv* env, jobject obj) {// 创建一个全局对象,引用PMSgPowerManagerServiceObj = env->NewGlobalRef(obj);// 利用hw_get_module加载power模块status_t err = hw_get_module(POWER_HARDWARE_MODULE_ID,(hw_module_t const**)&gPowerModule);if (!err) {gPowerModule->init(gPowerModule);} else {ALOGE("Couldn't load %s module (%s)", POWER_HARDWARE_MODULE_ID, strerror(-err));}
}

nativeInit的主要任务时装载power模块,该模块由厂商实现,以高通为例,如下。
device/qcom/common/power/power.c

tatic struct hw_module_methods_t power_module_methods = {.open = NULL,
};struct power_module HAL_MODULE_INFO_SYM = {.common = {.tag = HARDWARE_MODULE_TAG,.module_api_version = POWER_MODULE_API_VERSION_0_2,.hal_api_version = HARDWARE_HAL_API_VERSION,.id = POWER_HARDWARE_MODULE_ID,.name = "QCOM Power HAL",.author = "Qualcomm",.methods = &power_module_methods,},.init = power_init,.powerHint = power_hint,.setInteractive = set_interactive,
};

power_module中实现了init,powerHint,setInteractive,nativeInit最终调用到HAL power模块的power_init具体实现中。接着看native初始化nativeSetAutoSuspend的实现。

frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {if (enable) {ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off");autosuspend_enable();} else {ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on");autosuspend_disable();}
}

system/core/libsuspend/autosuspend.c

int autosuspend_disable(void)
{int ret;ret = autosuspend_init();if (ret) {return ret;}ALOGV("autosuspend_disable\n");if (!autosuspend_enabled) {return 0;}ret = autosuspend_ops->disable();if (ret) {return ret;}autosuspend_enabled = false;return 0;
}

nativeSetAutoSuspend最终调用到libsuspend(参考Android电源管理系列之libsuspend)的autosuspend_disable禁止系统休眠。继续看native初始化nativeSetInteractive,nativeSetFeature的实现
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp

static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {if (gPowerModule) {if (enable) {ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");gPowerModule->setInteractive(gPowerModule, true);} else {ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");gPowerModule->setInteractive(gPowerModule, false);}}
}static void nativeSetFeature(JNIEnv *env, jclass clazz, jint featureId, jint data) {int data_param = data;if (gPowerModule && gPowerModule->setFeature) {gPowerModule->setFeature(gPowerModule, (feature_t)featureId, data_param);}
}

同nativeInit一样,最终都是调用到HAL power模块的具体实现中。以上是构造函数的分析流程,下面继续看PowerManagerService在系统启动过程中回调onStart(),onBootPhase(),systemReady()的实现。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java


public void onStart() {publishBinderService(Context.POWER_SERVICE, new BinderService());publishLocalService(PowerManagerInternal.class, new LocalService());Watchdog.getInstance().addMonitor(this);Watchdog.getInstance().addThread(mHandler);
}private final class BinderService extends IPowerManager.Stub {......
}private final class LocalService extends PowerManagerInternal {......
}

onStart()中发布了BinderService,LocalService分别供其他进程,进程内其他服务调用,并将PowerManagerService加入到Watchdog监控中。

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

public void onBootPhase(int phase) {synchronized (mLock) {if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {......} else if (phase == PHASE_BOOT_COMPLETED) {final long now = SystemClock.uptimeMillis();// 设置mBootCompleted状态mBootCompleted = true;mDirty |= DIRTY_BOOT_COMPLETED;// 更新userActivity及PowerState,后面分析userActivityNoUpdateLocked(now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);updatePowerStateLocked();// 执行mBootCompletedRunnables中的runnable方法if (!ArrayUtils.isEmpty(mBootCompletedRunnables)) {Slog.d(TAG, "Posting " + mBootCompletedRunnables.length + " delayed runnables");for (Runnable r : mBootCompletedRunnables) {BackgroundThread.getHandler().post(r);}}mBootCompletedRunnables = null;}}
}

onBootPhase中主要设置mBootCompleted状态,更新PowerState状态,并执行mBootCompletedRunnables中的runnables方法(低电量模式会设置)。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

public void systemReady(IAppOpsService appOps) {synchronized (mLock) {mSystemReady = true;// 获取AppOpsServicemAppOps = appOps;// 获取DreamManagermDreamManager = getLocalService(DreamManagerInternal.class);// 获取DisplayManagerServicemDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);mPolicy = getLocalService(WindowManagerPolicy.class);// 获取mBatteryServicemBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);// 获取屏幕默认,最大,最小亮度mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();// 获取SensorManagerSensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());mBatteryStats = BatteryStatsService.getService();// 创建Notifier对象,用于广播power state的变化mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),mPolicy);// 无线充电检测mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),mHandler);// 监听设置的变化mSettingsObserver = new SettingsObserver(mHandler);mLightsManager = getLocalService(LightsManager.class);mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);// Initialize display power management.mDisplayManagerInternal.initPowerManagement(mDisplayPowerCallbacks, mHandler, sensorManager);// Register for settings changes.final ContentResolver resolver = mContext.getContentResolver();resolver.registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.SCREENSAVER_ENABLED),......IVrManager vrManager =(IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);try {vrManager.registerListener(mVrStateCallbacks);} catch (RemoteException e) {Slog.e(TAG, "Failed to register VR mode state listener: " + e);}// 读取配置readConfigurationLocked();updateSettingsLocked();mDirty |= DIRTY_BATTERY_STATE;updatePowerStateLocked();}// Register for broadcasts from other components of the system.IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_BATTERY_CHANGED);filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);filter = new IntentFilter();filter.addAction(Intent.ACTION_DREAMING_STARTED);filter.addAction(Intent.ACTION_DREAMING_STOPPED);mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);filter = new IntentFilter();filter.addAction(Intent.ACTION_USER_SWITCHED);mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);filter = new IntentFilter();filter.addAction(Intent.ACTION_DOCK_EVENT);mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
}

userActivity
userActivity是定义在PowerManager中的SystemApi,用户向PowerManagerService报告用户活动,以更新PowerManagerService内部时间/状态值,推迟系统休眠的时间。下面首先来看userActivity的定义。

frameworks/base/core/java/android/os/PowerManager.java

/*** User activity event type: Unspecified event type.*/
public static final int USER_ACTIVITY_EVENT_OTHER = 0;/*** User activity event type: Button or key pressed or released.*/
public static final int USER_ACTIVITY_EVENT_BUTTON = 1;/*** User activity event type: Touch down, move or up.*/
public static final int USER_ACTIVITY_EVENT_TOUCH = 2;/*** User activity event type: Accessibility taking action on behalf of user.*/
public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3;@SystemApi
public void userActivity(long when, int event, int flags) {try {mService.userActivity(when, event, flags);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private final class BinderService extends IPowerManager.Stub {......public void userActivity(long eventTime, int event, int flags) {final long now = SystemClock.uptimeMillis();......if (eventTime > now) {throw new IllegalArgumentException("event time must not be in the future");}final int uid = Binder.getCallingUid();final long ident = Binder.clearCallingIdentity();try {userActivityInternal(eventTime, event, flags, uid);} finally {Binder.restoreCallingIdentity(ident);}}......
}

PowerManager中userActivity请求调用服务端PowerManagerService BinderService的userActivity,即调用内部方法userActivityInternal。

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void userActivityInternal(long eventTime, int event, int flags, int uid) {synchronized (mLock) {if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {updatePowerStateLocked();}}
}

userActivityInternal中首先调用userActivityNoUpdateLocked更新相关数据及状态(***NoUpdateLocked仅仅更新内部状态并不采取任何操作),然后调用updatePowerStateLocked更新所有PowerState,下面分析userActivityNoUpdateLocked的实现,updatePowerStateLocked是PowerManagerService的核心方法,在最后进行分析。

frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private boolean userActivityNoUpdateLocked(long eventTime, int event, int flags, int uid) {// 如果发生时间是上一次休眠或唤醒前,或当前没有开机完成到systemReady,不采取操作直接返回if (eventTime < mLastSleepTime || eventTime < mLastWakeTime|| !mBootCompleted || !mSystemReady) {return false;}try {// 更新mLastInteractivePowerHintTime时间if (eventTime > mLastInteractivePowerHintTime) {powerHintInternal(POWER_HINT_INTERACTION, 0);mLastInteractivePowerHintTime = eventTime;}// 通过mNotifier通知BatteryStats UserActivity事件mNotifier.onUserActivity(event, uid);if (mUserInactiveOverrideFromWindowManager) {mUserInactiveOverrideFromWindowManager = false;mOverriddenTimeout = -1;}// 如果系统处于休眠状态,不进行处理if (mWakefulness == WAKEFULNESS_ASLEEP|| mWakefulness == WAKEFULNESS_DOZING|| (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {return false;}// 根据flag是否在已变暗的情况下是否重启活动超时更新mLastUserActivityTimeNoChangeLights或mLastUserActivityTime// 并且设置mDirty DIRTY_USER_ACTIVITYif ((flags & PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS) != 0) {if (eventTime > mLastUserActivityTimeNoChangeLights&& eventTime > mLastUserActivityTime) {mLastUserActivityTimeNoChangeLights = eventTime;mDirty |= DIRTY_USER_ACTIVITY;return true;}} else {if (eventTime > mLastUserActivityTime) {mLastUserActivityTime = eventTime;mDirty |= DIRTY_USER_ACTIVITY;return true;}}} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}return false;
}

gotoSleep
gotoSleep在PowerManager中的定义如下:
frameworks/base/core/java/android/os/PowerManager.java

public void goToSleep(long time) {goToSleep(time, GO_TO_SLEEP_REASON_APPLICATION, 0);
}public void goToSleep(long time, int reason, int flags) {try {mService.goToSleep(time, reason, flags);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

与userActivity一样,gotoSleep最终将调用到goToSleepInternal。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private final class BinderService extends IPowerManager.Stub {......public void goToSleep(long eventTime, int reason, int flags) {if (eventTime > SystemClock.uptimeMillis()) {throw new IllegalArgumentException("event time must not be in the future");}mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);final int uid = Binder.getCallingUid();final long ident = Binder.clearCallingIdentity();try {goToSleepInternal(eventTime, reason, flags, uid);} finally {Binder.restoreCallingIdentity(ident);}}......
}private void goToSleepInternal(long eventTime, int reason, int flags, int uid) {synchronized (mLock) {if (goToSleepNoUpdateLocked(eventTime, reason, flags, uid)) {updatePowerStateLocked();}}
}

goToSleepInternal中将执行goToSleepNoUpdateLocked更新内部状态,同样在updatePowerStateLocked中更新PowerState的操作。
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) {// 当不处于awake状态或未开机systemReady,不处理if (eventTime < mLastWakeTime|| mWakefulness == WAKEFULNESS_ASLEEP|| mWakefulness == WAKEFULNESS_DOZING|| !mBootCompleted || !mSystemReady) {return false;}try {......// 更新mLastSleepTime时间,设置DIRTY_WAKEFULNESS标志位mLastSleepTime = eventTime;mSandmanSummoned = true;setWakefulnessLocked(WAKEFULNESS_DOZING, reason);// Report the number of wake locks that will be cleared by going to sleep.int numWakeLocksCleared = 0;final int numWakeLocks = mWakeLocks.size();for (int i = 0; i < numWakeLocks; i++) {final WakeLock wakeLock = mWakeLocks.get(i);switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {case PowerManager.FULL_WAKE_LOCK:case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:case PowerManager.SCREEN_DIM_WAKE_LOCK:numWakeLocksCleared += 1;break;}}// Skip dozing if requested.if ((flags & PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE) != 0) {reallyGoToSleepNoUpdateLocked(eventTime, uid);}} finally {Trace.traceEnd(Trace.TRACE_TAG_POWER);}return true;
}

goToSleepNoUpdateLocked中更新mLastSleepTime,mWakefulness,mDirty状态。

Android 知识点 108 —— PowerManagerService相关推荐

  1. Android知识点汇总以及常见面试题

    Android知识点汇总以及常见面试题 1. 链表和数组的区别 2. List Hash 数组的区别 3. 用过哪些三方SDK 4. Android四大组件 5. 堆和栈的区别 6. Activity ...

  2. Android知识点 200 —— framework/base/cmds 常见的am命令,input,pm命令

    文章原文:http://www.360doc.com/content/11/0510/00/4154133_115595135.shtml 返回知识列表:Android知识点list /framewo ...

  3. Android 知识点——Method put in org.json.JSONObject not mocked

    先扯两句 老头子我没有什么别的优点,就剩下为了作死而作死了.总想玩的新鲜的东西,也就导致了新的问题,这篇存在的原因就是作死的报应啊! 下面给大家展示个神器,叫Android知识点--目录,好了,闲言少 ...

  4. Android知识点 431 -- recovery 强制执行恢复出厂设置(Rescue Party)

    转载原文:https://www.cnblogs.com/codeking100/p/10339258.html 返回知识列表:Android知识点list 1 Incremented rescue ...

  5. Android系统学习(37)---Android知识点及资料汇总

    Android知识点及资料汇总 废话不多说,直接上总结: Android入门方法和经验之谈 如何自学 Android 编程? 如何零基础学习安卓开发? 我是如何自学,资料分享 一张Android学习的 ...

  6. Android知识点大扫描

    Android知识点大扫描 什么是 3G 3G,全称为3rd Generation,中文含义就是指第三代数字通信. 所谓3G,是指将无线通信与国际互联网等多媒体通信结合的新一代移动通信系统. 3G只是 ...

  7. Android知识点 400 -- /data/tombstones

    原文地址 https://www.cnblogs.com/CoderTian/p/5980426.html 返回知识列表 Android知识点list tombstone一般是由Dalvik错误.状态 ...

  8. Android知识点复习(持续更新中)

    1 总览 作为开发者,基础知识非常重要的,尤其一些大厂非常注重基础,基础是一切的根本,在面试时候,如果没有事先准备,很容易被淘汰.笔者整理这套知识点,大大小小的一共几百多条,方便大家查漏补缺. 参考答 ...

  9. Android 8.1 PowerManagerService分析(三)——WakeLock机制

    欢迎大家关注我的掘金帐号 我会在那里定期更新最新版本的Android Framework源码模块分析~~ 在Android 8.1 PowerManagerService分析(一)中,主要分析了PMS ...

最新文章

  1. 亿级PV请求的三种负载均衡技术
  2. C++实现CString和string的互相转换(转)
  3. mysql world.sql.zip_安装mysql数据库zip版|mysqlzip
  4. python上机实验报告读取文件_Python程序设计实验报告八 : 文件
  5. Nginx配置pathinfo
  6. Jupyter notebook Ipython 魔法函数 Magic 计算代码(函数)耗时 Timing(%%time %time %timeit)
  7. SpringBoot FK-关联表查询(三)
  8. 查看Oracle表中的指定记录在数据文件中的位置
  9. jasperReport Studio java报表设计(详细)
  10. java单例模式的7种实现方式
  11. 一键搞定JavaEE应用,JTM1.0(JRE+Tomcat+MySQL绿色运行环境)
  12. oracle常用函数详解
  13. mysql增删改查语法
  14. python -PyPDF2对pdf 进行拆分
  15. STM8在STVD下开发所需的中断向量表模版
  16. 信息安全等级保护分为几个级别呢?
  17. Anaconda中pkgs文件夹详解
  18. 苏格拉底和柏拉图甩手的故事
  19. vue中引用高德地图根据经纬度计算两地距离
  20. 第3章 数据分析工具Pandas

热门文章

  1. JAVA系统之间通信方式总结
  2. 一文读懂寒武纪:AI芯片拓荒者的乘风破浪
  3. excel怎么统计相同名字的数量
  4. 三星android平板,三星也不忘Android平板,或推带虹膜识别的Galaxy Tab S4
  5. 大数据风控必看,挖掘学历数据中暗藏的还款意愿及还款能力
  6. Navicat绘制数据库物理模型
  7. 超酷震撼 HTML5/CSS3动画应用及源码
  8. 哥大计算机专业 世界排名,2020年哥伦比亚大学计算机系统排名
  9. Python画玫瑰花
  10. GOOGLE HACKS巧妙使用网络搜索的技巧和工具(第二版)已经出版