http://blog.csdn.net/kc58236582/article/details/54616756

上一篇博客我们主要分析了在setScreenState中调用PhoneWindowManager的一些流程,在setScreenState中先是调用了DisplayPowerState的setScreenState函数。上篇博客我们没有分析,这篇博客我们先从这个函数开始分析,主要分析下亮度的设置流程。

[cpp] view plain copy
  1. public void setScreenState(int state) {
  2. if (mScreenState != state) {
  3. if (DEBUG) {
  4. Slog.d(TAG, "setScreenState: state=" + state);
  5. }
  6. mScreenState = state;
  7. mScreenReady = false;
  8. scheduleScreenUpdate();
  9. }
  10. }



scheduleScreenUpdate主要通过消息方式,最后调用到下面函数。当我们屏幕刚要点亮,这个时候mScreenBrightness为0,所以这个时候调用mPhotonicModulator.setState设置state是点亮,但是brightness是0的。

[cpp] view plain copy
  1. private final Runnable mScreenUpdateRunnable = new Runnable() {
  2. @Override
  3. public void run() {
  4. mScreenUpdatePending = false;
  5. int brightness = mScreenState != Display.STATE_OFF
  6. && mColorFadeLevel > 0f ? mScreenBrightness : 0;
  7. if (mPhotonicModulator.setState(mScreenState, brightness)) {
  8. if (DEBUG) {
  9. Slog.d(TAG, "Screen ready");
  10. }
  11. mScreenReady = true;
  12. invokeCleanListenerIfNeeded();
  13. } else {
  14. if (DEBUG) {
  15. Slog.d(TAG, "Screen not ready");
  16. }
  17. }
  18. }
  19. };

DisplayPowerState的设置亮度状态逻辑分析

mPhotonicModulator.setState应该要PhotonicModulator的run函数结合一起看。

[cpp] view plain copy
  1. public boolean setState(int state, int backlight) {
  2. synchronized (mLock) {
  3. boolean stateChanged = state != mPendingState;
  4. boolean backlightChanged = backlight != mPendingBacklight;
  5. if (stateChanged || backlightChanged) {
  6. if (DEBUG) {
  7. Slog.d(TAG, "Requesting new screen state: state="
  8. + Display.stateToString(state) + ", backlight=" + backlight);
  9. }
  10. mPendingState = state;
  11. mPendingBacklight = backlight;
  12. boolean changeInProgress = mStateChangeInProgress || mBacklightChangeInProgress;
  13. mStateChangeInProgress = stateChanged;
  14. mBacklightChangeInProgress = backlightChanged;
  15. if (!changeInProgress) {
  16. Slog.d(TAG,"notify set backlight thread run");
  17. mLock.notifyAll();
  18. }
  19. }
  20. return !mStateChangeInProgress;
  21. }
  22. }

两者结合看先setState设置了状态,只有状态改变时,我们才能重新设置状态(设置到mpendingState和mPendingBacklight)。而在run函数中,当设置的状态mPendingState、mPendingBacklight和mActualState、mActualBacklight(真正设置到背光的状态、亮度)不一样时,才会调用mBlanker.requestDisplayState设置亮度。否则状态没有改变,就会把mStateChangeInProgress 和mBacklightChangeInProgress 设置为false,然后线程就wait住。

而此时setState重新设置下来的话,这个时候把亮度和状态设置到mPendingState 和mPendingBacklight 。然后这时mStateChangeInProgress 和 mBacklightChangeInProgress都是false。这样就可以调用mLock的notifyAll函数重新唤醒线程,这样就把把前面setState设置下来的mPendingState和mPendingBacklight再通过mBlanker.requestDisplayState设置到背光设备中去。

[cpp] view plain copy
  1. @Override
  2. public void run() {
  3. for (;;) {
  4. // Get pending change.
  5. final int state;
  6. final boolean stateChanged;
  7. final int backlight;
  8. final boolean backlightChanged;
  9. synchronized (mLock) {
  10. state = mPendingState;
  11. stateChanged = (state != mActualState);
  12. backlight = mPendingBacklight;
  13. backlightChanged = (backlight != mActualBacklight);
  14. if (!stateChanged) {
  15. // State changed applied, notify outer class.
  16. postScreenUpdateThreadSafe();
  17. mStateChangeInProgress = false;
  18. }
  19. if (!backlightChanged) {
  20. mBacklightChangeInProgress = false;
  21. }
  22. if (!stateChanged && !backlightChanged) {
  23. try {
  24. mLock.wait();
  25. } catch (InterruptedException ex) { }
  26. continue;
  27. }
  28. mActualState = state;
  29. mActualBacklight = backlight;
  30. }
  31. // Apply pending change.
  32. if (true) {
  33. Slog.d(TAG, "Updating screen state: state="
  34. + Display.stateToString(state) + ", backlight=" + backlight);
  35. }
  36. mBlanker.requestDisplayState(state, backlight);
  37. Slog.d(TAG, "kangchen Updating screen state: state=");
  38. }
  39. }

设置亮度、状态到背光设备

DisplayBlanker的requestDisplayState如下,主要调用requestGlobalDisplayStateInternal函数。

[cpp] 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. callbacks.onDisplayStateChange(state);
  9. if (state != Display.STATE_OFF) {
  10. requestGlobalDisplayStateInternal(state, brightness);
  11. }
  12. }
  13. };

requestGlobalDisplayStateInternal函数先是对state和brightness的处理,然后把这个两个变量放在mGlobalDisplayState 和mGlobalDisplayBrightness成员变量中。紧接着调用applyGlobalDisplayStateLocked函数mTempDisplayStateWorkQueue作为参数。最后再调用mTempDisplayStateWorkQueue各个成员的run函数(这里返回的是Runnable接口,这里就会设置状态和亮度到设备中去)。

[cpp] view plain copy
  1. private void requestGlobalDisplayStateInternal(int state, int brightness) {
  2. if (state == Display.STATE_UNKNOWN) {
  3. state = Display.STATE_ON;
  4. }
  5. if (state == Display.STATE_OFF) {
  6. brightness = PowerManager.BRIGHTNESS_OFF;
  7. } else if (brightness < 0) {
  8. brightness = PowerManager.BRIGHTNESS_DEFAULT;
  9. } else if (brightness > PowerManager.BRIGHTNESS_ON) {
  10. brightness = PowerManager.BRIGHTNESS_ON;
  11. }
  12. synchronized (mTempDisplayStateWorkQueue) {
  13. try {
  14. synchronized (mSyncRoot) {
  15. if (mGlobalDisplayState == state
  16. && mGlobalDisplayBrightness == brightness) {
  17. return; // no change
  18. }
  19. mGlobalDisplayState = state;
  20. mGlobalDisplayBrightness = brightness;
  21. applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue);
  22. }
  23. // Setting the display power state can take hundreds of milliseconds
  24. // to complete so we defer the most expensive part of the work until
  25. // after we have exited the critical section to avoid blocking other
  26. // threads for a long time.
  27. for (int i = 0; i < mTempDisplayStateWorkQueue.size(); i++) {
  28. mTempDisplayStateWorkQueue.get(i).run();//设置亮度、状态到设备
  29. }
  30. } finally {
  31. mTempDisplayStateWorkQueue.clear();
  32. }
  33. }
  34. }

applyGlobalDisplayStateLocked函数会遍历各个显示设备(多显示),然后调用updateDisplayStateLocked函数返回一个Runnable,最后把这个Runnable放入之前传入的mTempDisplayStateWorkQueue队列中。

[cpp] view plain copy
  1. private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
  2. final int count = mDisplayDevices.size();
  3. for (int i = 0; i < count; i++) {
  4. DisplayDevice device = mDisplayDevices.get(i);
  5. Runnable runnable = updateDisplayStateLocked(device);
  6. if (runnable != null) {
  7. workQueue.add(runnable);
  8. }
  9. }
  10. }

那下面我们看下updateDisplayStateLocked函数,主要是调用了DisplayDevice的requestDisplayStateLocked函数,当然mGlobalDisplayState和mGlobalDisplayBrightness作为参数。

[cpp] view plain copy
  1. private Runnable updateDisplayStateLocked(DisplayDevice device) {
  2. // Blank or unblank the display immediately to match the state requested
  3. // by the display power controller (if known).
  4. DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
  5. if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
  6. return device.requestDisplayStateLocked(mGlobalDisplayState, mGlobalDisplayBrightness);
  7. }
  8. return null;
  9. }

这里的DisplayDevice的requestDisplayStateLocked函数,是在LocalDisplayAdapter中实现的,这里吧state和brightness保存在mState和mBrightness中,然后返回Runnable接口,最后在Runnable接口中设置亮度和状态。

[cpp] view plain copy
  1. public Runnable requestDisplayStateLocked(final int state, final int brightness) {
  2. // Assume that the brightness is off if the display is being turned off.
  3. assert state != Display.STATE_OFF || brightness == PowerManager.BRIGHTNESS_OFF;
  4. final boolean stateChanged = (mState != state);
  5. final boolean brightnessChanged = (mBrightness != brightness) && mBacklight != null;
  6. if (stateChanged || brightnessChanged) {
  7. final int displayId = mBuiltInDisplayId;
  8. final IBinder token = getDisplayTokenLocked();
  9. final int oldState = mState;
  10. if (stateChanged) {
  11. mState = state;//保存state
  12. updateDeviceInfoLocked();
  13. }
  14. if (brightnessChanged) {
  15. mBrightness = brightness;//保存brightness
  16. }
  17. // Defer actually setting the display state until after we have exited
  18. // the critical section since it can take hundreds of milliseconds
  19. // to complete.
  20. return new Runnable() {//返回Runnable
  21. @Override
  22. public void run() {
  23. // Exit a suspended state before making any changes.
  24. int currentState = oldState;
  25. if (Display.isSuspendedState(oldState)
  26. || oldState == Display.STATE_UNKNOWN) {
  27. if (!Display.isSuspendedState(state)) {
  28. setDisplayState(state);
  29. currentState = state;
  30. } else if (state == Display.STATE_DOZE_SUSPEND
  31. || oldState == Display.STATE_DOZE_SUSPEND) {
  32. setDisplayState(Display.STATE_DOZE);
  33. currentState = Display.STATE_DOZE;
  34. } else {
  35. return; // old state and new state is off
  36. }
  37. }
  38. // Apply brightness changes given that we are in a non-suspended state.
  39. if (brightnessChanged) {
  40. Slog.d(TAG, "setDisplayBrightnessbrightness1=" + brightness);
  41. setDisplayBrightness(brightness);
  42. Slog.d(TAG, "setDisplayBrightnessbrightness2=" + brightness);
  43. }
  44. // Enter the final desired state, possibly suspended.
  45. if (state != currentState) {
  46. setDisplayState(state);
  47. }
  48. }
  49. private void setDisplayState(int state) {
  50. if (DEBUG) {
  51. Slog.d(TAG, "setDisplayState("
  52. + "id=" + displayId
  53. + ", state=" + Display.stateToString(state) + ")");
  54. }
  55. try {
  56. final int mode = getPowerModeForState(state);
  57. SurfaceControl.setDisplayPowerMode(token, mode);//到SurfaceControl设置状态
  58. } finally {
  59. Trace.traceEnd(Trace.TRACE_TAG_POWER);
  60. }
  61. }
  62. private void setDisplayBrightness(int brightness) {
  63. try {
  64. mBacklight.setBrightness(brightness);//设置亮度
  65. } finally {
  66. Trace.traceEnd(Trace.TRACE_TAG_POWER);
  67. }
  68. }
  69. };
  70. }
  71. return null;
  72. }

DisplayPowerControl设置亮度逻辑(根据VSync信号将亮度慢慢变亮)

上面在DisplayPowerState中仅仅是设置状态,比如刚点亮屏幕这个时候其实设置的brightness为0,我们继续分析DisplayPowerState的updatePowerState函数。在updatePowerState函数中,当设置亮度时会调用如下代码:

[cpp] view plain copy
  1. if (!mPendingScreenOff) {
  2. if (state == Display.STATE_ON || state == Display.STATE_DOZE) {
  3. animateScreenBrightness(brightness,
  4. slowChange ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
  5. } else {
  6. animateScreenBrightness(brightness, 0);
  7. }
  8. }

我们注意到这里有一个BRIGHTNESS_RAMP_RATE_SLOW 和BRIGHTNESS_RAMP_RATE_FAST(这里涉及到亮度显示原理我们后面分析),先看animateScreenBrightness函数。这里函数主要调用了mScreenBrightnessRampAnimator.animateTo函数。

[cpp] view plain copy
  1. private void animateScreenBrightness(int target, int rate) {
  2. if (mScreenBrightnessRampAnimator.animateTo(target, rate)) {
  3. try {
  4. mBatteryStats.noteScreenBrightness(target);
  5. } catch (RemoteException ex) {
  6. // same process
  7. }
  8. }
  9. }

我们再来看mScreenBrightnessRampAnimator 对象的创建

[cpp] view plain copy
  1. mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
  2. mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);

我们注意一个参数是DisplayPowerState对象mPowerState,另一个参数是DisplayPowerState.SCREEN_BRIGHTNESS

[cpp] view plain copy
  1. public static final IntProperty<DisplayPowerState> SCREEN_BRIGHTNESS =
  2. new IntProperty<DisplayPowerState>("screenBrightness") {
  3. @Override
  4. public void setValue(DisplayPowerState object, int value) {
  5. object.setScreenBrightness(value);
  6. }
  7. @Override
  8. public Integer get(DisplayPowerState object) {
  9. return object.getScreenBrightness();
  10. }
  11. };

RampAnimator的构造函数。

[cpp] view plain copy
  1. public RampAnimator(T object, IntProperty<T> property) {
  2. mObject = object;
  3. mProperty = property;
  4. mChoreographer = Choreographer.getInstance();
  5. }

我们结合RampAnimator的构造函数,再来分析RampAnimator的animateTo函数。

1. 当rate<=0时,这个时候,我们直接调用mProperty.setValue。就是调用DisplayPowerState的setScreenBrightness函数。这个setScreenBrightness函数我们后面分析。

2. 当rate>0时,这个时候会调用postAnimationCallback函数(这个函数根据VSync信号过来,把亮度慢慢上升的一个过程),而且mAnimating置为true。

[cpp] view plain copy
  1. public boolean animateTo(int target, int rate) {
  2. // Immediately jump to the target the first time.
  3. if (mFirstTime || rate <= 0) {
  4. if (mFirstTime || target != mCurrentValue) {
  5. mFirstTime = false;
  6. mRate = 0;
  7. mTargetValue = target;
  8. mCurrentValue = target;
  9. mProperty.setValue(mObject, target);//设置值
  10. if (mAnimating) {
  11. mAnimating = false;
  12. cancelAnimationCallback();
  13. }
  14. if (mListener != null) {
  15. mListener.onAnimationEnd();
  16. }
  17. return true;
  18. }
  19. return false;
  20. }
  21. // Adjust the rate based on the closest target.
  22. // If a faster rate is specified, then use the new rate so that we converge
  23. // more rapidly based on the new request.
  24. // If a slower rate is specified, then use the new rate only if the current
  25. // value is somewhere in between the new and the old target meaning that
  26. // we will be ramping in a different direction to get there.
  27. // Otherwise, continue at the previous rate.
  28. if (!mAnimating
  29. || rate > mRate
  30. || (target <= mCurrentValue && mCurrentValue <= mTargetValue)
  31. || (mTargetValue <= mCurrentValue && mCurrentValue <= target)) {
  32. mRate = rate;
  33. }
  34. final boolean changed = (mTargetValue != target);
  35. mTargetValue = target;
  36. // Start animating.
  37. if (!mAnimating && target != mCurrentValue) {
  38. mAnimating = true;
  39. mAnimatedValue = mCurrentValue;
  40. mLastFrameTimeNanos = System.nanoTime();
  41. postAnimationCallback();
  42. }
  43. return changed;
  44. }

下面我们先分析postAnimationCallback函数,这个和之前分析WMS的VSync信号类似,当VSync信号过来时,会调用mAnimationCallback函数。(可以看之前博客http://blog.csdn.net/kc58236582/article/details/53835998)

[cpp] view plain copy
  1. private void postAnimationCallback() {
  2. mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
  3. }

那我们继续看mAnimationCallback 的run函数,这个函数当当前值和上一次值不一样,我们就调用DisplayPowerState的setScreenBrightness来设置亮度。而且当前值不是目标值,我们就继续调用postAnimationCallback函数,来设置VSync回调。最后当亮度变成目标值后,将mAnimating 置为false,代表亮度变化的动画结束了。

[cpp] view plain copy
  1. private final Runnable mAnimationCallback = new Runnable() {
  2. @Override // Choreographer callback
  3. public void run() {
  4. final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
  5. final float timeDelta = (frameTimeNanos - mLastFrameTimeNanos)
  6. * 0.000000001f;
  7. mLastFrameTimeNanos = frameTimeNanos;
  8. final float scale = ValueAnimator.getDurationScale();
  9. if (scale == 0) {
  10. // Animation off.
  11. mAnimatedValue = mTargetValue;
  12. } else {
  13. final float amount = timeDelta * mRate / scale;
  14. if (mTargetValue > mCurrentValue) {
  15. mAnimatedValue = Math.min(mAnimatedValue + amount, mTargetValue);
  16. } else {
  17. mAnimatedValue = Math.max(mAnimatedValue - amount, mTargetValue);
  18. }
  19. }
  20. final int oldCurrentValue = mCurrentValue;
  21. mCurrentValue = Math.round(mAnimatedValue);
  22. if (oldCurrentValue != mCurrentValue) {
  23. mProperty.setValue(mObject, mCurrentValue);//设置到DisplayPowerState的setScreenBrightness函数中
  24. }
  25. if (mTargetValue != mCurrentValue) {//当前值还不是目标值,继续设置VSync回调
  26. postAnimationCallback();
  27. } else {
  28. mAnimating = false;//否则动画标志置为false
  29. if (mListener != null) {
  30. mListener.onAnimationEnd();
  31. }
  32. }
  33. }
  34. }

最后我们再看下DisplayPowerState的SetScreenBrightness函数,将亮度设置到mScreenBrightness 中,当屏幕状态不为off时,调用scheduleScreenUpdate函数(所以肯定先要调用setScreenState来设置屏幕状态为on,这样才能设置屏幕亮度)。

[cpp] view plain copy
  1. public void setScreenBrightness(int brightness) {
  2. if (mScreenBrightness != brightness) {
  3. mScreenBrightness = brightness;
  4. if (mScreenState != Display.STATE_OFF) {
  5. mScreenReady = false;
  6. scheduleScreenUpdate();
  7. }
  8. }
  9. }

scheduleScreenUpdate最后通过发消息会调用如下代码,这里状态已经是ON了,就会把刚刚设置的mScreenBrightness作为参数设置到mPhotonicModulator.setState中。流程和设置state一样了,只是一开始亮屏时,设置state时,亮度为0而已。

[cpp] view plain copy
  1. private final Runnable mScreenUpdateRunnable = new Runnable() {
  2. @Override
  3. public void run() {
  4. mScreenUpdatePending = false;
  5. int brightness = mScreenState != Display.STATE_OFF
  6. && mColorFadeLevel > 0f ? mScreenBrightness : 0;
  7. if (mPhotonicModulator.setState(mScreenState, brightness)) {
  8. if (DEBUG) {
  9. Slog.d(TAG, "Screen ready");
  10. }
  11. mScreenReady = true;
  12. invokeCleanListenerIfNeeded();
  13. } else {
  14. if (DEBUG) {
  15. Slog.d(TAG, "Screen not ready");
  16. }
  17. }
  18. }
  19. };

Android6.0 亮屏灭屏流程(DisplayPowerControler、WMS)(二)亮度设置相关推荐

  1. (原创)Android6.0亮屏流程分析

    1.概述 Android的亮屏流程从android系统结构层次来分可以分为三个流程,App应用唤醒源:Framework层Power结合Display,Light服务做亮屏绘制准备工作:底层驱动点亮背 ...

  2. (原创)Android6.0亮屏流程之Keyguard Window绘制

    亮灭屏问题一直是Android模块最常见的问题之一. 由于问题出现问题的地方涉及到公司代码,我这里仅仅只作原生代码模块的分析 其实在看过另外一篇关于android亮屏流程的文章就会发现,影响亮屏快慢的 ...

  3. 安卓锁屏灭屏加载流程

    时间2022-01-10 相关的类 首先需要了解以下这几个类 KeyguardViewMeditor:整个锁屏的调度类,相当于锁屏的大脑,控制着整个锁屏. KeyguardService:负责锁屏模块 ...

  4. android6.0 Bluetooth蓝牙源码流程笔记

    注:基于mtk平台的android6.0,由于我个人水平有限,代码细节不能详细说明,抱歉 参考文章: http://blog.csdn.net/shichaog/article/details/527 ...

  5. android开启热点softap模式,[RK3288][Android6.0] Wifi开启热点(SoftAP)流程小结

    Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 在Settings中选择要打开wifi热点功能: 调用流程如下: onPreferenceChan ...

  6. [RK3288][Android6.0] 调试笔记 --- 相机曝光模式以及等级的获取和设置

    Platform: RK3288 OS: Android 6.0 Kernel: 3.10.92 曝光模式种类: V4L2_EXPOSURE_AUTO Automatic exposure time, ...

  7. Android7.0 PowerManagerService亮灭屏分析(三)

    在前面两部分已经对绘制windows与设置设备状态进行了详细讲解. 之后接着就该对亮度值进行设置, 实现亮屏动作了. 在DisplayPowerController中的animateScreenBri ...

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

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

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

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

最新文章

  1. DevOps笔记-05:IT行业中BA、SM、PO、PM、PD、Dev、Ops、QA都是什么角色
  2. Jquery性能优化(转自蓝色理想)
  3. Android通过WebView在线打开PDF文件(文中提供源码下载)
  4. 学习C语言指针,这一篇案例教程就够够的了
  5. docker集群运行在calico网络上
  6. 二叉树经典题之二叉树的非递归遍历
  7. 响应式多终端挖掘机机械设备展示类网站模板
  8. JMeter接口测试入门
  9. 自动布局神器 -- ZXPAutoLayout框架的使用
  10. 【文章导读】什么是旁道攻击?Meltdown Redux英特尔漏洞(MDS攻击);KAISER:从用户空间隐藏内核(KAISER);Meltdown/Spectre分析
  11. Java单元测试实践-15.Stub、Replace、Suppress Spring的方法
  12. 声声不息,新“声”报到
  13. java arraylist.add(),Java ArrayList add()方法与示例
  14. 统计学中数据分析方法大全
  15. pytorch model.to(device) 加载模型特别慢
  16. 详解深度学习之经典网络:AlexNet(2012) 并利用该网络架构实现人脸识别
  17. 打开虚拟机时显示不是有效的虚拟机配置文件是什么原因啊
  18. linux通过Squid代理上网
  19. java cs系统_Java课程设计——基于CS模式的用户管理系统
  20. Clinet/Server在工作线程中刷新页面数据的方法

热门文章

  1. 《C陷阱与缺陷》第一章【词法“陷阱”】
  2. 数组的使用示例(老师的任务)
  3. qq里面cap字符_特殊符号大全
  4. HTML+CSS期末大作业:动漫篮球网站设计—— 樱木花道(3页) 学生DW网页设计作业成品 篮球网页设计作业 动漫网页设计作业
  5. 径向Kohn-Sham方程的谱有限元方法
  6. 软件的基于生命周期开发方法
  7. 我的智能物联网硬件之路--电子价签
  8. 【摘苹果】scratch蓝桥杯集训题目
  9. uniapp配置百度语音识别转文字(含demo)
  10. 用电梯服务器怎样解电梯显示E34,成为电梯高手之三菱-菱云系列电梯故障代码表...