Android 渲染机制
1 Android 渲染流程
一般情况下,一个布局写好以后,使用 Activity#setContentView 调用该布局,这个 View tree 就创建好了。Activity#setContentView 其实是通过 LayoutInflate 来把布局文件转化为 View tree 的(反射)。需要注意的是,LayoutInflate 虽然可以帮助创建 View tree,但到这里也仅是以单纯的对象数据存在,这个时候是无法正确的获取 View 的 GUI(Graphical User Interface 图形用户界面)的相关属性的,如大小、位置和渲染状态。
View tree 生成的最后一步就是把根节点送到 ViewRootImpl#setView 中,之后就会进入渲染流程,入口方法是 ViewRootImpl#requestLayout,之后是 ViewRootImpl#scheduleTraversals,最后调用的是 ViewRootImpl#performTraversals,View tree 的渲染流程全都在这里,也就是常说的 measure、layout、draw。View体系与自定义View(三)—— View的绘制流程
以下为 View 的绘制流程/视图添加到 Window 的过程:
总结:文本数据(xml)—> 实例数据(java) —> 图像数据 bitmap,bitmap 才是屏幕(硬件)所需的数据。
在 ViewRootImpl#drawSoftware 方法中会通过 Surface#lockCanvas 方法创建一个 Canvas(在英文中是“画布”的意思) 对象,然后进入 View#draw 流程,Canvas 才是实际制作图像的工具,比如如何画点,如何画线,如何画文字、图片等等。
// /frameworks/base/core/java/android/view/ViewRootImpl.java
public final Surface mSurface = new Surface();
private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {Surface surface = mSurface; // 1...if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,scalingRequired, dirty, surfaceInsets)) {return false;}}private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty, Rect surfaceInsets) {// Draw with software renderer.final Canvas canvas; // 2try {canvas = mSurface.lockCanvas(dirty); // 3canvas.setDensity(mDensity);} catch (Surface.OutOfResourcesException e) {handleOutOfResourcesException(e);return false;} catch (IllegalArgumentException e) {mLayoutRequested = true; // ask wm for a new surface next time.return false;}try {...mView.draw(canvas);...}finally {... }
}
一个 Canvas 对象从 ViewRootImpl 传给 View,View 的各个方法(draw、dispatchDraw 和 drawChild)都只接收 Canvas 对象,每个 View 都要把其想要展示的内容传递到 Canvas 对象中。
// /frameworks/base/core/java/android/view/View.java
public void draw(@NonNull Canvas canvas) {...
}protected void dispatchDraw(@NonNull Canvas canvas) { }// /frameworks/base/core/java/android/view/ViewGroup.java
protected boolean drawChild(@NonNull Canvas canvas, View child, long drawingTime) {return child.draw(canvas, this, drawingTime);
}
在 Canvas 中有一个 Bitmap 类型的对象,这个 Bitmap 才是真正的画布。
// /frameworks/base/graphics/java/android/graphics/Canvas.java
public class Canvas extends BaseCanvas {private Bitmap mBitmap;
}
那么,Surface 是什么呢?以下是 Surface 的部分源码:
/*** Handle onto a raw buffer that is being managed by the screen * 由屏幕管理的原始缓冲区*/
public class Surface implements Parcelable {private final Canvas mCanvas = new CompatibleCanvas();public Canvas lockCanvas(Rect inOutDirty)throws Surface.OutOfResourcesException, IllegalArgumentException {synchronized (mLock) {checkNotReleasedLocked();if (mLockedObject != 0) {throw new IllegalArgumentException("Surface was already locked");}mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);return mCanvas;}}
}
从注释上可以知道,Surface 是一块原始缓冲区。 在 Android 中,所有的 View 都由窗口管理,而每个窗口都会关联一个 Surface。在屏幕上绘制内容之前,都需要先获得 Surface,然后用 2D/3D 引擎(Skia/OpenGL)在这个缓冲区上绘制内容。 绘制完成之后,会通知 SurfaceFlinger 将绘制内容(Frame Buffer)渲染到屏幕上去。关于 SurfaceFlinger,之后会做详细解释。
屏幕渲染分为软件渲染和硬件渲染,Canvas 对象的来源也有两个:
- 一是走软件渲染时,在 ViewRootImpl 中创建,从源码上看,ViewRootImpl 本身就会创建一个 Surface 对象,然后用 Surface 获取出一个 Canvas 对象,再传递给 View,由 View 进行具体的绘制;
- 二是走硬件加速,会由 hwui 创建 Canvas 对象;
因此,draw 的触发逻辑也有两条:
- 没有硬件加速时,走的是 ViewRootImpl#performTraversals —> performDraw —> draw —> drawSoftware —> View#draw;
- 启动硬件加速时,走的是 ViewRootImpl#performTraversals —> performDraw —> draw —> ThreadedRenderer.java#draw
2 软件绘制和硬件绘制
Android 4.0 开始引入硬件加速机制,之前走的都是软件渲染。如果有一些 API 是不支持硬件加速的,需要进行手动关闭。
UI 渲染需要要依赖两个核心的硬件,CPU 和 GPU:
- CPU(Center Processing Unit 中央处理器),是计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元;
- GPU(Graphics Processing Unit 图形处理器),是一种专门用于图像运算的处理器,在加计算机系统中通常被称为“显卡”的核心部位就是 GPU;
在没有 GPU 的时代,UI 的绘制任务都是由 CPU 完成的,也就是说,CPU 除了负责逻辑运算、内存管理还要负责 UI 绘制,这就导致 CPU 的任务繁重,性能也会受到影响。
CPU 和 GPU 在结构设计上完全不同,如下所示:
- 黄色部分:Control 控制器,用于协调控制整个 CPU 的运行,包括读取指令、控制其他模块的运行等;
- 绿色部分:ALU(Arithmetic Logic Unit)是算数逻辑单元,用于进行数学、逻辑运算;
- 橙色部分:Cache 和 DRAM 分别为高速缓存和 RAM,用于存储信息;
从上图可以看出,CPU 的控制器较为复杂,而 ALU 数量较少,因此 CPU 更擅长各种复杂的逻辑运算,而不擅长数学尤其是浮点运算。而 GPU 的设计正是为了实现大量的数学运算。GPU 的控制器比较简单,但包含大量的 ALU,GPU 中的 ALU 使用了并行设计,且具有较多的浮点运算单元,可以帮助我们加快 Rasterization(栅格化)操作。
栅格化将 UI 组件拆分到显示器上的不同像素上进行显示。UI 组件在绘制到屏幕之前都要经过 Rasterization(栅格化)操作,是绘制 Button、Shape、Path、String、Bitmap 等显示组件最基础的操作。这是一个非常耗时的操作,GPU 的引入就是为了加快栅格化。
因此,硬件绘制的思想就是 CPU 将 XML 数据转换成实例对象,然后将 CPU 不擅长的图形计算交由 GPU 去处理,由 GPU 完成绘制任务,以便实现更好的性能(CPU 和 GPU 都是制图者)。
底层图像库有很多,Android 选择的是 Skia(2D) 和 OpenGL(3D) 来绘制图形,图形库可以直接控制 GPU 产生图形数据(Canvas.draw —> native —>Skia/OpenGL —> GPU)。
软件绘制使用的是 Skia 库,是一款能在低端设备,如手机上呈现高质量的 2D 跨平台图形框架,Chrome、Flutter 内部使用的都是 Skia 库。需要注意的是,软件绘制使用的是 Skia 库,但这并不代表 Skia 库不支持硬件加速,从 Android 8 开始,我们可以使用 Skia 进行硬件加速,Android 9 开始默认使用Skia 进行硬件加速。
在处理 3D 场景时,通常使用 OpenGL ES。在 Android 7.0 中添加了对 Vulkan 的支持。Vulkan 的设计目标是取代 OpenGL,Vulkan 是个相当低级别的 API,并且提供了并行的任务处理。除了较低的 CPU 的使用率,VulKan 还能够更好的在多个 CPU 内核之间分配工作。在功耗、多核优化提升会图调用上有非常明显的优势。
Skia、OpenGL、Vulkan 的区别:
- Skia:是 2D 图形渲染库。如果想完成 3D 效果需要 OpenGL、Vulkan、Metal 进行支持。Android 8 开始 Skia 支持硬件加速,Chrome、Flutter 都是用它来完成绘制的;
- OpenGL:是一种跨平台的 2D/3D 图形绘制规范接口,OpenGL ES 是针对嵌入式设备的,对手机做了优化;
- Vulkan:Vulkan 是用来替换 OpenGL 的,它同时支持 2D 和 3D 绘制,也更加轻量级;
3 Android 黄油计划(Project Butter)
虽然引入了硬件加速机制,加快了渲染的时间,但是对于 GUI(Graphical User Interface 图形用户界面)的流畅度、响应度,特别是动画这一块的流畅程度和其他平台(如 Apple)差距仍然是很大的。一个重要的原因就在于,GUI 整体的渲染缺少协同。 最大的问题在于动画,动画要求连续不断的重绘,如果仅靠客户端来触发,帧率不够,由此造成的流畅度也不好。
Google 在 2012 年的 I/O 大会上宣布了 Project Butter 黄油计划,并且在 Android 4.1 中正式开启这个机制。Project Butter 主要包含三个组成部分:
- VSync
- Choreographer
- TripBuffer
其中,VSync(Vertical Synchronization) 是理解 Project Butter 的核心。
3.1 VSync
帧率 vs 屏幕刷新频率:
- 帧率(Frame Rate):单位 fps,即 GPU 在一秒内生成的帧数(图片),帧率越高越好。例如电影界采用 24 帧的速度就可以画面非常流畅了,而 Android 系统则采用更高的 60fps,即每秒生成 60 帧的画面,也就是 1000/60 ≈ 16ms 生成一帧画面;
- 12 fps:由于人类眼睛的特殊生理结构,如果所看画面帧率高于 10~12 fps 的时候,就会认为是连贯的;
- 24fps:有声电影的拍摄及播放帧率均为 24fps,对一般人来说是可以接受的;
- 30fps:早起的高动态电子游戏,帧率小于 30fps 时就会显得不连贯,这是因为没有动态模糊使流畅度降低;
- 60fps:在与手机交互过程中,如果触摸和反馈在 60fps 以下是可以被人感觉出来的,会感到画面卡顿和迟滞现象;
- 屏幕刷新频率(Refresh Rate):单位是赫兹(Hz),表示屏幕在一秒内刷新画面的次数,刷新频率取决于硬件的固定参数,该值对于特定的设备来说是一个常量。如 60Hz、144 Hz 表示每秒刷新 60 次或 144 次。
对于一个特定的设备来说,帧率和屏幕刷新速率没有必然的关系。但是两者需要协同工作,才能正确的获取图像数据并进行绘制。比如 Android 手机的刷新频率是 60Hz,那么一帧数据需要在 16ms 内完成。
屏幕并不是一次性的显示画面的,而是从左到右(行刷新,水平刷新,Horizontal Scanning)、从上到下(屏幕刷新,垂直刷新,Vertiacl Scanning)逐行扫描显示,不过这一过程快到人眼无法察觉。以 60Hz 的刷新频率的屏幕为例,即 1000/60 ≈ 16ms,16ms 刷新一次。
如果上一帧的扫描没有结束,屏幕又开始扫描下一帧,就会出现扫描撕裂的情况:
因此,GPU 厂商开发出了一种防止屏幕撕裂的技术方案 —— Vertical Synchronization,即 VSync,垂直同步信号或时钟中断。VSync 是一个硬件信号,它和显示器的刷新频率相对应,每当屏幕完成一次垂直刷新,VSync 信号就会被发出,作为显示器和图形引擎之间时间同步的标准,其本质意义在于保证界面的流畅性和稳定性。
3.2 Choreographer
Choreographer(编舞者)根据 VSync 信号来对 CPU/GPU 进行绘制指导,协调整个渲染过程,对于输入事件响应、动画和渲染在时间上进行把控,以保证流畅的用户体验。
Choreographer 在 ViewRootImpl 中的使用:
// /frameworks/base/core/java/android/view/ViewRootImpl.java
final Choreographer mChoreographer;
void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
Choreographer 的作用:
- 布局请求:当视图需要进行布局操作时,Choreographer 发出布局请求并协调布局操作的执行。它确保将布局请求与其他动画和绘制操作同步,避免冲突和界面不一致;
- 绘制同步:Choreographer 负责将绘制操作与显示器的刷新同步。它通过监听系统的 VSync 信号,去定绘制操作的时机,避免图形撕裂和卡顿现象;
- 输入事件处理:Choreographer 管理和分发用户输入事件,确保它们在正确的时间点被处理,并与动画和渲染操作同步。这有助于提供更流畅和响应敏捷的用户交互体验;
- 动画调度:Choreographer 调度和管理应用程序中的动画效果,确保动画按照预定的帧率和时间表进行播放,并平滑地过渡到下一个动画阶段;
Choreographer 使用了以下几种机制来实现流畅的界面渲染:
- VSync(垂直同步信号):Choreographer 监听系统发出的 VSync 信号。每当收到 VSync 信号时,Choreographer 就知道屏幕即将进行一次刷新。这样,它可以根据 VSync 信号的时间点来安排渲染和动画操作的触发和执行;
- 时间戳(Timestamping):Choreographer 在收到 VSync 信号时,会获取一个时间戳,以记录每次 VSync 信号的时间点。这个时间戳可以用于计算渲染和动画的操作时间和持续时间,从而在合适的时机进行调度和执行;
- 界面刷新(Frame Refresh):Choreographer 使用 VSync 信号和时间戳来决定界面的刷新时机。它根据预定的逻辑和优先级,调度动画、布局和绘制操作,以确保它们在下一次 VSync 信号到来之前完成。这样可以避免界面的撕裂或卡顿现象,提供流畅的用户体验;
其实这个 Choreogarpher 这个类本身并不会很复杂,简单来说它就是负责定时回调,主要方法有 postFrameCallback 和 removeFrameCallback,FrameCallback 是个比较简单的接口:
// /frameworks/base/core/java/android/view/Choreographer.java
public interface FrameCallback {public void doFrame(long frameTimeNanos);
}
3.3 TripBuffer 三缓存
3.3.1 单缓存
在没有引入 Vsync 的时候,屏幕显示图像的工作流程是这样的:
如上图所示,CPU/GPU 将需要绘制的数据存放在图像缓冲区中,屏幕从图像缓冲区中获取数据,然后刷新显示,这是典型的生产者-消费者模型。
理想的情况是帧率(GPU)和刷新频率(屏幕)相等,每绘制一帧,屏幕就显示一帧。而实际情况是,二者之间没有必然的联系,如果没有锁来控制同步,很容易出现问题。
- 如果刷新频率大于帧率的时候,屏幕拿不到下一帧数据,就会重复绘制当前帧数据。
- 如当帧率大于刷新频率时,屏幕还没有刷新到 n-1 帧的时候,GPU 已经生成第 n 帧了,屏幕刷新的时候绘制的就是第 n 帧数据,这个时候屏幕上半部分显示的是第 n 帧数据,屏幕的下半部分显示的是第 n-1 帧之前的数据,这样显示的图像就会出现明显的偏差,也就是“tearing”,如下所示:
3.3.2 双缓存(Double Buffer)
这里的双缓存和计算机组成原理中的“二级缓存”不是一回事。
为了解决单缓存的 tearing 问题,双缓存和 VSync 应运而生。双缓存的模型如下所示:
两个缓存分别为 Back Buffer 和 Frame Buffer(帧缓冲区)。GPU 向 Back Buffer 中写数据,屏幕从 Frame Buffer 中读数据。VSync 信号负责调用 Back Buffer 到 Frame Buffer 的复制操作,可以认为该复制操作在瞬间完成。
在双缓冲模式下,工作流程是这样的:在某个时间点,一个屏幕刷新周期完成,进入短暂的刷新空白期。此时,VSync 信号产生,先完成复制操作,然后通知 CPU/GPU 绘制下一帧图像。复制操作完成后屏幕开始下一个刷新周期,即将刚复制到 Frame Buffer 的数据显示到屏幕上。
在双缓冲模型下,只有当 VSync 信号产生时,CPU/GPU 才会开始绘制。这样,当帧率大于刷新频率时,帧率就会被迫跟刷新频率保持同步,从而避免“tearing”现象。
需要注意的是,当 VSync 信号发出时,如果 CPU/GPU 正在生产帧数据,此时不会发生复制操作。当屏幕进入下一个刷新周期时,就会从 Frame Buffer 中取出“老”数据,而非正在产生的帧数据,即两个刷新周期显示的是同一帧数据,这就是“掉帧”现象(Dropped Frame,Skipped Frame,Jank)。因此,双缓存的缺陷在于,当 CPU/GPU 绘制一帧的时间超过 16ms 时,就会产生 Jank。
如下图所示,A、B 和 C 都是 Buffer。蓝色代表 CPU 生成的帧数据,绿色代表 GPU 执行生成帧数据,黄色代表生成帧完成:
CPU/GPU 处理数据的时间过长,超过了一帧绘制的时间,在第二个时间段内,由于 GPU 还在处理 B 帧数据,无法进行数据交换,导致 A 帧被重复绘制。而 B 帧数据在绘制完成后又缺乏 VSync 信号,只能等待下一次的 VSync 信号的来临。因此,在这一过程中,有一段时间是被浪费的。
3.3.3 三缓存(Triple Buffer)
于是有了三缓存:
工作原理同双缓冲类似,只是多了一个 Back Buffer。三缓冲机制有效的利用了等待 VSync 信号的时间,可以帮助我们减少 jank。
如果有第三个 Buffer 能让 CPU/GPU 在这个时候继续工作,那就完全可以避免第二个 Jank 产生了。
需要注意的是,第三个缓存并不是总存在的,只有当需要的时候才会创建。 之所以这样,是因此三缓存会显著增加用户输入到显示的延迟时间。如上图,帧 C 是在第 2 个刷新周期产生的,却是在第 4 个周期显示的。
4 Android 渲染的整体架构
以下是 Android 渲染的整体架构:
Android 渲染的整体架构可以分为以下几部分:
- 图像生产者(image stream producers):主要有 MediaPlayer、CameraPreview、NDK(Skia)、OpenGL ES。其中,MediaPlayer 和 Camera Preview 是通过直接读取图像源来生成图像数据。NDK(Skia)、OpenGL ES 是通过自身的绘制能力产生的图像数据。
- 图像缓冲区(BufferQueue):一般是三缓冲区。NDK(Skia)、OpenGL ES、Vulkan 将绘制的数据存放在图像缓冲区;
- 图像消费者(image stream consumers): SurfaceFlinger 从图像缓冲区将数据取出,通过硬件合成器 Hardware Composer 进行加工及合成 layer,最终交给 HAL 展示;
- HAL:硬件抽象层,把图形数据展示到设备屏幕;
整个图像渲染系统就是采用了生产者-消费者模式,屏幕渲染的核心,是对图像数据的生产和消费。 生产和消费的对象是 BufferQueue 中的 Buffer。
前面我们已经说过,Surface 是一块原始缓冲区,每个窗口都会管理一个 Surface,屏幕在绘制内容之前,先要获得 Surface,然后在再用 2D/3D 引擎(Skia/OpenGL)在这个缓冲区上进行绘制(Surface —> Canvas)。
SurfaceFlinger 是图像数据的消费者,它的作用主要是接收 Graphic Buffer,然后交给 HWComposer 合成,合成完的数据,最终交给了 Frame Buffer(帧缓冲区)。
软件渲染
再没有硬件加速之前主要是通过 Skia 这种软件方式渲染 UI,如下所示:
整个渲染流程看上去比较简单,但是正如前面所说,CPU 对于图形处理器并不是那么高效,这个过程完全没有利用 GPU 的高性能。
硬件渲染
Android 3.0,支持硬件加速,需要手动打开,Android 4.0 就默认开启硬件加速了,开启硬件加速流程如下:
硬件加速绘制最核心就是通过 GPU 完成 Graphic Buffer 的内容绘制。
RenderThread 线程
经过 Android 4.1 的 Projcet Butter 黄油计划之后,Android 的渲染性有了很大的改善。不过你有没有注意到这样一个问题,虽然利用了 GPU 的图形高性能运算,但是从计算到通过 GPU 绘制到 Frame Buffer,整个计算和绘制都在 UI 主线程中完成。UI 线程任务过于繁重。如果整个渲染过程比较耗时,可能造成无法响应用户的操作,进而出现卡顿的情况。GPU 对图形的渲染能力更胜一筹,如果使用 GPU 并在不同的线程绘制渲染图形,那么整个流程会更加顺畅。
在 Android 5.0 之通过引进 RenderThread(渲染线程),我们就可将 UI 渲染工作从 Main Thread 释放出来,交由 RenderThread 来处理,从而也使得 Main Thread 可以更专注高效地处理用户输入,这样使得在提高 UI 绘制效率的同时,也使得 UI 具有更高的响应。
相关文章:
Android 渲染机制
1 Android 渲染流程 一般情况下,一个布局写好以后,使用 Activity#setContentView 调用该布局,这个 View tree 就创建好了。Activity#setContentView 其实是通过 LayoutInflate 来把布局文件转化为 View tree 的(反射)…...
go语言Map与结构体
1. Map map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。 1.1. map定义 Go语言中 map的定义语法如下 map[KeyType]ValueType其中, KeyType:表示键的类型。ValueType:表示键对应的值的类型。map类型的…...
C#,打印漂亮杨辉三角形(帕斯卡三角形)的源代码
杨辉 Blaise Pascal 这是某些程序员看完会哭的代码。 杨辉三角形(Yanghui Triangle),是一种序列数值的三角形几何排列,最早出现于南宋数学家杨辉1261年所著的《详解九章算法》一书。 欧洲学者,最先由帕斯卡&#x…...
[SUCTF 2019]CheckIn1
黑名单过滤后缀’ph,并且白名单image类型要有对应文件头 对<?过滤,改用GIF89a<script languagephp>eval($_POST[cmd]);</script>,成功把getshell.gif上传上去了 尝试用.htaccess将上传的gif当作php解析,但是失败…...
C语言练习题110例(十)
91.杨辉三角 题目描述: KK知道什么叫杨辉三角之后对杨辉三角产生了浓厚的兴趣,他想知道杨辉三角的前n行,请编程帮他 解答。杨辉三角,本质上是二项式(ab)的n次方展开后各项的系数排成的三角形。其性质包括:每行的端点数为1&…...
前端学习-0125
<h>标签 含义:标题 级别:<h1> - <h6> 快捷键生成 :h$*[0,6] 属性:align"left|center|right" <p>标签 含义: 段落 <br>标签 含义:换行 <hr>标签 含义&…...
gin中使用validator做参数校验
在web开发中对请求参数进行校验,通常在代码中定义与请求参数相对应的模型(结构体),借助模型绑定快捷地解析请求中的参数,例如 gin 框架中的Bind和ShouldBind系列方法。 gin框架使用github.com/go-playground/validato…...
理想架构的Doherty功率放大器理论与仿真
Doherty理论—理想架构的Doherty功率放大器理论与仿真 参考: 三路Doherty设计 01 射频基础知识–基础概念 ADS仿真工程文件链接:理想架构的Doherty功率放大器理论与仿真 目录 Doherty理论---理想架构的Doherty功率放大器理论与仿真0、Doherty架构的作用…...
22. 离线MC强化学习算法(1)
文章目录 1. 理解离线MC强化学习的关键2. 什么是重要性采样3.重要性采样定理给我们的一般启示4.重要性采样定理给离线蒙特卡洛强化学习的启示 1. 理解离线MC强化学习的关键 离线强化学习的特点是采样策略 π ′ ≠ 待评估策略 π \pi\ne 待评估策略\pi π′待评估策略π&…...
如何阅读xml电子发票
xml电子发票是官方给出的电子存档的文件格式,本质是文本,所以文件很小,大量发票存储,能够更加凸显优势。 但是xml电子发票不方便阅读,因为里面是xml格式,对于财务人员来讲,看“代码”简直太难了…...
php实现多进程的几种方式
目录 一:使用pcntl扩展库 二:使用Swoole扩展 三:使用多进程模式PHP-FPM 在PHP中实现多进程主要有以下几种方式: 一:使用pcntl扩展库 pcntl扩展库提供了多线程相关的函数,如pcntl_fork()用于创建子进程…...
CmakeList教程
一、CmakeList介绍: cmake 是一个跨平台、开源的构建系统。它是一个集软件构建、测试、打包于一身的软件。它使用与平台和编译器独立的配置文件来对软件编译过程进行控制。它会通过写的语句自动生成一个MakeFile,从而实现高效编译 二、CmakeList的常用指令 1.指定…...
JavaWeb之JavaScript-Vue --黑马笔记
什么是JavaScript? JavaScript(简称:JS) 是一门跨平台、面向对象的脚本语言。是用来控制网页行为的,它能使网页可交互。 JavaScript 和 Java 是完全不同的语言,不论是概念还是设计。但是基础语法类似。 …...
pikachu_ssrf攻略
ssrf(curl): 打开pikachu靶场: http://127.0.0.1/pikachu-master/vul/ssrf/ssrf_curl.php?urlhttp://127.0.0.1/pikachu-master/vul/ssrf/ssrf_info/info1.php 发现URL地址最后面是info1.php 猜测一下有没有可能存在info2.php?…...
门面模式 Facade Pattern
门面模式 门面模式(Facade Pattern),也称之为外观模式,其核心为:外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。 在软件开发领域有这样一句话:计算机科学领域…...
Linux基础指令大汇总
Linux的指令比较多,在学习的过程中要学会总结和归纳,同时结合实践多多使用,就像学数学一样,不是背过公式就等于掌握的,而是要知道在什么时候用,怎么用才是关键。 这篇文章会列举一系列常用的指令࿰…...
Unity配置表xlsx/xls打包后读取错误问题
前言 代码如下: //文本解析private void ParseText(){//打开文本 读FileStream stream File.Open(Application.streamingAssetsPath excelname, FileMode.Open, FileAccess.Read, FileShare.Read);//读取文件流IExcelDataReader excelRead ExcelReaderFactory…...
CSS基本知识总结
目录 一、CSS语法 二、CSS选择器 三、CSS样式表 1.外部样式表 2.内部样式表 3.内联样式 四、CSS背景 1.背景颜色:background-color 2.背景图片:background-image 3.背景大小:background-size 4.背景图片是否重复:backg…...
3dmax效果图渲染出现曝光怎么解决?
在使用3ds Max完成效果图渲染工作时,有时会遇到曝光过度的问题,这会使得渲染的图像出现光斑或者过亮,损害了效果的真实感和美观度。那么解决解决3dmax曝光问题呢?一起看看吧! 3dmax效果图渲染出现曝光解决方法 1、相机…...
科技、文化与旅游的融合创新:智慧文旅的未来之路
在当今社会,科技、文化与旅游的融合已经成为文旅产业转型升级的重要趋势。这种融合不仅有助于提升文旅产业的核心竞争力,更有助于推动产业的数字化转型和可持续发展。 本文将深入探讨科技、文化与旅游的融合创新,以及智慧文旅场景的解决方案…...
【第四天】蓝桥杯备战
题 1、求和2、天数3、最大缝隙 1、求和 https://www.lanqiao.cn/problems/1442/learning/ 解法:字符串方法的应用 import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scann…...
计算机服务器中了halo勒索病毒怎么办,halo勒索病毒解密处理流程
计算机技术的发展与应用为企业的生产生活提供了坚实基础,但同时也为网络安全威胁制造了有利条件。近期,网络上的勒索病毒非常嚣张,给企业的计算机服务器带来严重威胁。近日,云天数据恢复中心接到山东某制造公司的求助,…...
基于LSTM的负荷预测,基于BILSTM的负荷预测,基于GRU的负荷预测,基于BIGRU的负荷预测,基于BP神经网络的负荷预测
目录 背影 摘要 代码和数据下载:基于LSTM的负荷预测,基于BILSTM的负荷预测,基于GRU的负荷预测,基于BIGRU的负荷预测,基于BP神经网络的负荷预测资源-CSDN文库 https://download.csdn.net/download/abc991835105/8876806…...
Linux命令-apachectl命令(Apache服务器前端控制工具)
apachectl命令 是Apache的Web服务器前端控制工具,用以启动、关闭和重新启动Web服务器进程。 语法 apachectl (参数)参数 configtest:检查设置文件中的语法是否正确;fullstatus:显示服务器完整的状态信息;graceful&a…...
5.【SpringBoot3】文件上传
1. 文件上传到本地 需求分析 在用户更换头像或发布文章时,需要携带一个图片的 url 地址,该 url 地址是当用户访问文件上传接口,将图片上传成功后,服务器返回的地址。所以,后台需要提供一个文件上传接口,用…...
网络安全态势感知平台概述
网络安全态势感知平台 文章目录 网络安全态势感知平台网络安全态势感知平台是什么一、网络安全态势感知平台是什么?二、网络安全态势感知很重要三、网络安全态势感知基础功能以某公司态势平台产品为例具体功能有以下: 网络安全态势感知平台是什么 网络安…...
PHP导出csv文件格式(最快捷的方式导出Excel文件)
php导出csv文件格式比起用PHPExcel插件导出excel文件速度快100倍! 以下是几种不同的PHP导出CSV文件的方法: 方法一(php://output方式用fputcsv函数格式化成csv数据): ----------------------------------------------…...
vue3常用代码
文章目录 监听路由vue3 警告Feature flag __VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.mitt、project/inject 无效解决方案 菜鸟做项目时发现很多 vue3 常用的代码,所以来总结一下! 监听路由 import { useRoute } from "…...
【技术】SpringBoot 接口怎么加密解密
1. 介绍 在我们日常的Java开发中,免不了和其他系统的业务交互,或者微服务之间的接口调用 如果我们想保证数据传输的安全,对接口出参加密,入参解密。 但是不想写重复代码,我们可以提供一个通用starter,提…...
SqlAlchemy使用教程(六) -- ORM 表间关系的定义与CRUD操作
SqlAlchemy使用教程(一) 原理与环境搭建SqlAlchemy使用教程(二) 入门示例及编程步骤SqlAlchemy使用教程(三) CoreAPI访问与操作数据库详解SqlAlchemy使用教程(四) MetaData 与 SQL Express Language 的使用SqlAlchemy使用教程(五) ORM API 编程入门 本章内容,稍微有…...
四川旅游攻略自由行攻略/优化大师下载
一:ClassLoader类加载器,主要的作用是将class文件加载到jvm虚拟机中。jvm启动的时候,并不是一次性加载所有的类,而是根据需要动态去加载类,主要分为隐式加载和显示加载。 隐式加载:程序代码中不通过调用Cla…...
商城建设公司/seo推广优化
欢迎提出更简单的语法~(文章中案例有两个福利哦,一个是养生,一个是人工智能[ 密码:fqif])先说下感觉,python的编程有点JavaScript的感觉(比如:和“有时候不区别),又感觉像外国版的易语言,整个过…...
网站建设 检查 通报/河源疫情最新通报
外观模式 为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 外观模式三要素(client-facade-subSystem) 外观角色(facade),是模式的…...
网站名称去哪里注册/杭州百度
请进入链接http://www.cnblogs.com/code-cd/p/4810408.html 转载于:https://www.cnblogs.com/xhc1263478959/p/4823950.html...
开放大学门户网站建设/互联网营销平台
请完成Java程序:本题是一个冒泡排序程序的实例。冒泡排序的含义是将相邻的两个数作比较,如果是升序排列的话,如果前边的数大,则将两个数交换。从第一个数开始两两比较一次,就可以将最大的数移动到最后。注意࿱…...
限制访问次数的网站/什么软件可以免费发广告
实用标准Oracle BIEE 12c 系统安装配置文档仇辉2015 年 12 月文案大全实用标准1 文档控制编制日期 作者 版本 变更参考2015-12-18 仇辉 V1.02015-12-18 V2.0审核职位发布位置 / 岗位项目指导委员会项目指导委员会项目指导委员会项目指导委员会文案大全实用标准目 录1 文档控制 …...