Android硬件渲染流程

news2024/11/18 21:51:48

Android硬件渲染流程

  • 一.渲染流程
    • 1.VSync信号的监听
    • 2.VSync信号触发绘制
  • 二.渲染原理
    • 1.画布的获取
      • 1.1 画布的创建
      • 1.2 渲染指令列表的创建
    • 2.绘制与渲染指令
      • 2.1 矩形的绘制
      • 2.2 硬件渲染指令
      • 2.3 节点的绘制
    • 3.绘制的提交
      • 3.1 绘制结果的保存
      • 3.2 绘制结果的获取
    • 4.层级的构建
      • 4.1 绘制结果的更新
      • 4.2 构建的分发
      • 4.3 构建数据的保存
    • 5.渲染与渲染管线
      • 5.1 节点的更新
      • 5.2 节点的渲染
  • 三.总结
    • 1.硬件渲染
    • 2.绘制流程
      • 2.1 渲染指令
      • 2.2 对应关系
    • 3.渲染流程
      • 3.1 渲染指令列表的获取
      • 3.2 渲染层级的构建
      • 3.3 渲染管线的渲染

一.渲染流程

1.VSync信号的监听

    在Android中,App的渲染流程是从ViewRootImpl开始的。在回调Activity的onResume方法后,会调用ViewRootImpl的requestLayout方法触发页面中View的测量与绘制。

    在requestLayout方法中,首先会调用checkThread方法检查当前线程是否为UI线程,如果不是,则抛出异常。接下来会调用scheduleTraversals方法。
0
    在ViewRootImpl的scheduleTraversals方法中,主要做了两件事:

1)向主线程的消息队列发送一个同步信息屏障。

2)提交callbackType类型为CALLBACK_TRAVERSAL的TraversalRunnable。
1

2.VSync信号触发绘制

    当VSync信号到来时,会执行TraversalRunnable的run方法,该方法内部会调用ViewRootImpl的doTraversal方法。
2
    在ViewRootImpl的doTraversal方法中,主要做了两件事:

1)移除主线程消息队列的同步信息屏障。

2)调用performTraversals方法。
3
    在ViewRootImpl的performDraw方法中,会调用draw方法。在ViewRootImpl的draw方法中,如果开启了硬件渲染,就会从mAttachInfo的mThreadedRenderer中获取ThreadedRenderer。并调用ThreadedRenderer的draw方法。
4
    在ThreadedRenderer的draw方法中主要做了两件事:

1)从DecorView开始递归构建DisplayList。

2)唤醒Render线程对DisplayList进行渲染。
5
    在ThreadedRenderer的updateRootDisplayList中主要做了四件事:

1)从DecorView开始向下分发draw方法,递归构建DisplayListOp。

2)获取最顶层的RecordingCanvas。

3)通过DecorView获取最终的RenderNode并绘制到RecordingCanvas上。

4)将DisplayListOp填充到Native层的RootRenderNode中。
6

二.渲染原理

1.画布的获取

    在硬件渲染中,每个View都有一个RenderNode。当调用RenderNode的beginRecording方法时,内部会调用RecordingCanvas的静态方法obtain获取RenderNode。
7    在RecordingCanvas的静态方法obtain中,会创建RecordingCanvas。
8
    在RecordingCanvas的构造方法中,主要做了两件事:

1)创建Native层Canvas并返回对应的地址。

2)调用父类的构造方法对返回的地址进行保存。
9

1.1 画布的创建

    RecordingCanvas的nCreateDisplayListCanvas方法对应的Native实现为android_graphics_DisplayListCanvas的android_view_DisplayListCanvas_createDisplayListCanvas函数。

    在android_view_DisplayListCanvas_createDisplayListCanvas函数中,主要做了三件事:

1)获取Native层的RenderNode。

2)创建Canvas。

3)返回Canvas对应的地址。
10
    在Canvas::create_recording_canvas方法中,会创建SkiaRecordingCanvas。
11
    在SkiaRecordingCanvas的构造方法中,会调用initDisplayList方法,初始化DisplayList。
12

1.2 渲染指令列表的创建

    在SkiaRecordingCanvas的initDisplayList方法中,主要做了三件事:

1)清除上一帧的SkiaDisplayList,如果SkiaDisplayList为空,则再创建一个SkiaDisplayList。

2)绑定SkiaDisplayList与RecordingCanvas。

3)将RecordingCanvas保存到SkiaCanvas中。
13
    在SkiaDisplayList的attachRecorder方法中,会将SkiaDisplayList中的SkiaDisplayData与RecordingCanvas绑定。
14

2.绘制与渲染指令

2.1 矩形的绘制

    在硬件绘制过程中,当调用Canvas的drawRect方法时,在Canvas的drawRect方法中,会调用nDrawRect方法。
15
    Canvas的nDrawRect方法对应的Native实现为android_graphics_Canvas的drawRect函数。在drawRect函数中,主要做了两件事:

1)获取Native层Canvas。

2)通过Canvas绘制矩形。
16
    SkiaRecordingCanvas继承自SkiaCanvas。这里的drawRect方法在SkiaRecordingCanvas的父类SkiaCanvas中实现。

    在SkiaCanvas的drawRect方法中,会调用SkCanvas的drawRect方法。
17
    在SkCanvas的drawRect方法中,会调用子类RecordingCanvas的onDrawRect方法。
18
    在RecordingCanvas的onDrawRect方法中,会调用之前保存的DisplayListData的drawRect方法。在DisplayListData的drawRect方法中,会创建一个DrawRect指令并保存。
19

2.2 硬件渲染指令

    在DisplayListData中,所有的绘制指令都存储在一块连续的内存中。

    在Android中,所有的绘制指令都继承了Op。Op是一个结构体,在Op中有两个字段,type表示指令的类型,skip表示指令的长度。

struct Op {
    uint32_t type : 8;
    uint32_t skip : 24;
  };

    在DisplayListData的push方法中,主要做了四件事:

1)计算当前指令的长度。

2)判断所有的绘制指令是否超过内存最大容量,如果超过最大容量,则进行扩容,重新分配内存,每次扩容增加4096个字节。

3)计算当前绘制指令在内存中的位置,并在指定位置处创建当前指令的实例对象。

4)为当前绘制指令的type和skip赋值。
20

2.3 节点的绘制

    在ThreadedRenderer的updateViewTreeDisplayList方法中,会调用View的updateDisplayListIfDirty方法。
21    在View的updateDisplayListIfDirty方法中,主要做了三件事:

1)从自身的RenderNode中获取RecordingCanvas。

2)通过Flag判断,如果是ViewGroup且自身不用绘制,则分发子View去绘制,否则直接绘制。

3)结束绘制。
22
    在View的draw方法中,首先会绘制自身。然后对子View进行绘制。dispatchDraw方法在View中为空实现,如果一个View不是ViewGroup,那么dispatchDraw方法不会对任何View进行绘制分发。
23
    View的dispatchDraw方法在ViewGroup中被重写。如果一个View是ViewGroup,那么dispatchDraw方法会对子View进行绘制分发。在ViewGroup的dispatchDraw方法中,会调用drawChild方法。
24
    在ViewGroup的drawChild方法中,会调用View的draw方法。这里的draw方法是View中一个重载的draw方法,只在ViewGroup中调用。
25
    在View重载的draw方法中,主要做了三件事:

1)判断是否开启硬件渲染。

2)如果开启硬件渲染,则对当前View构建DisplayList,保存到当前View的RenderNode并返回。

3)如果当前View的DisplayList不为空,则将当前View的RenderNode绘制到父View的Canvas上。
26
    在RecordingCanvas的drawRenderNode方法中,会调用nDrawRenderNode方法。
27
    RecordingCanvas的nDrawRenderNode方法对应的Native层实现为android_graphics_DisplayListCanvas的android_view_DisplayListCanvas_drawRenderNode函数。

    在android_view_DisplayListCanvas_drawRenderNode函数中,主要做了三件事:

1)根据地址获取Native层Canvas。

2)根据地址获取View对应的Native层的RenderNode。

3)将RenderNode绘制到Canvas上。
28
    根据硬件渲染中Canvas的创建过程可以知道,这里的Canvas实际上是SkiaRecordingCanvas。

    在SkiaRecordingCanvas的drawRenderNode方法中,主要做了两件事:

1)将RenderNode封装成RenderNodeDrawable,保存到SkiaDisplayList中用于记录。

2)对封装好的RenderNodeDrawable进行绘制。
29
    SkiaRecordingCanvas继承自SkiaCanvas。这里的drawDrawable方法在SkiaRecordingCanvas的父类SkiaCanvas中实现。

    在SkiaCanvas的drawDrawable方法中,会调用SkCanvas的drawDrawable方法。
30
    在SkCanvas的drawDrawable方法中,会调用子类RecordingCanvas的onDrawDrawable方法。
31
    在RecordingCanvas的onDrawDrawable方法中,会调用之前保存的DisplayListData的drawDrawable方法。在DisplayListData的drawDrawable方法中,会创建一个DrawDrawable指令并保存。
32

3.绘制的提交

3.1 绘制结果的保存

    在RenderNode的endRecording方法中,主要做了两件事:

1)停止记录,标记DisplayList可用。

2)释放RecordingCanvas。
33
    在RecordingCanvas的finishRecording方法中,会调用nFinishRecording方法。
34
    RecordingCanvas的nFinishRecording方法对应的Native实现为android_graphics_DisplayListCanvas的android_view_DisplayListCanvas_finishRecording函数。

    在android_view_DisplayListCanvas_finishRecording函数中,主要做了三件事:

1)获取Canvas,实际获取的是SkiaRecordingCanvas。

2)获取RenderNode。

3)调用SkiaRecordingCanvas的finishRecording结束绘制。
35
    在SkiaRecordingCanvas的finishRecording方法中,主要做了三件事:

1)暂停标记并获取SkiaDisplayList。

2)将SkiaDisplayList封装成DisplayList,DisplayList是对SkiaDisplayListWrapper的重命名,SkiaDisplayListWrapper会保存SkiaDisplayList。

3)将DisplayList保存到RenderNode中。
36

3.2 绘制结果的获取

    在DisplayList构建完成后, 会调用ThreadedRenderer的syncAndDrawFrame方法唤醒Render线程进行渲染。在ThreadedRenderer的syncAndDrawFrame中,会调用nSyncAndDrawFrame方法。
37
    ThreadedRenderer的nSyncAndDrawFrame方法对应的Native实现为android_graphics_HardwareRenderer的android_view_ThreadedRenderer_syncAndDrawFrame函数。

    在android_view_ThreadedRenderer_syncAndDrawFrame函数中,主要做了两件事:

1)获取RenderProxy。

2)调用RenderProxy的syncAndDrawFrame方法。
38
    在RenderProxy的syncAndDrawFrame方法中,会调用DrawFrameTask的drawFrame方法。在DrawFrameTask的drawFrame方法中,会调用postAndWait方法。
39
    在DrawFrameTask的postAndWait方法中,主要做了三件事:

1)对DrawFrameTask的run方法进行封装。

2)将封装后的对象添加到RenderThread的队列中。

3)UI线程进入阻塞状态。
40
    当RenderTread执行任务时,会调用DrawFrameTask的run方法。在DrawFrameTask的run方法中,主要做了三件事:

1)获取UI线程构建的DisplayList。

2)唤醒UI线程。

3)根据DisplayList进行绘制。
41

4.层级的构建

    在DrawFrameTask的syncFrameState方法中,主要做了两件事:

1)处理硬件加速层,如TextureView的绘制。

2)构建TreeInfo。
42
    在CanvasContext的prepareTree方法中,主要做了两件事:

1)保存LayerUpdateQueue到TreeInfo中,为后续后构建TreeInfo做准备。LayerUpdateQueue用于保存待绘制的RenderNode。

2)遍历RenderNode构建TreeInfo。
43
    在RenderNode的prepareTreeImpl方法中,主要做了三件事:

1)获取DisplayList。

2)通过DisplayList分发子节点构建TreeInfo。

3)将完成构建的当前RenderNode保存到LayerUpdateQueue中。
44

4.1 绘制结果的更新

    在RenderNode的pushStagingDisplayListChanges方法中,会调用syncDisplayList方法,对DisplayList进行锁定保存。
45
    在RenderNode的syncDisplayList方法中,主要做了三件事:

1)遍历mStagingDisplayList中保存的RenderNode,对RednerNode的引用加1。mStagingDisplayList是用于暂存本次构建好的DisplayList的变量。

2)遍历mDisplayList中保存的RenderNode,对RednerNode的引用减1,并清空mDisplayList中对SkiaDisplayList的引用。mDisplayList是用于保存下次待渲染的DisplayList的变量。

3)将本次构建好的DisplayList保存到下次待渲染的DisplayList。
46
    DisplayList是对SkiaDisplayListWrapper的重命名。在SkiaDisplayListWrapper的updateChildren方法中,会调用SkiaDisplayList的updateChildren方法。
47
    在SkiaDisplayList的updateChildren方法中,主要做了三件事:

1)遍历获取RenderNodeDrawable。

2)从RenderNodeDrawable中获取RenderNode。

3)将RenderNode作为参数,执行参数中传入的function方法。
48

4.2 构建的分发

    DisplayList是对SkiaDisplayListWrapper的重命名。在SkiaDisplayListWrapper的prepareListAndChildren方法中,会调用SkiaDisplayList的prepareListAndChildren方法。
49
    在SkiaDisplayList的prepareListAndChildren方法中,主要做了四件事:

1)遍历获取RenderNodeDrawable。

2)从RenderNodeDrawable中获取RenderNode。

3)对TreeInfo中的属性进行更新。

4)将RenderNode和TreeInfo作为参数,执行参数中传入的function方法。
50
    这里传入的function是RenderNode的prepareTreeImpl方法,这样就实现了子RenderNode构建TreeInfo。

4.3 构建数据的保存

    在RenderNode的pushLayerUpdate方法中,会对当前的RenderNode进行保存。
51

5.渲染与渲染管线

    在Android中,渲染管线有两种:SkiaOpenGLPipeline和SkiaVulkanPipeline,底层实现分别对应着OpenGL和Vulkan。下面所有的IRenderPipeline以SkiaOpenGLPipeline为例。

    在CanvasContext的draw方法中,主要做了三件事:

1)获取可绘制的缓存。

2)使用GPU按照绘制指令绘制界面。

3)将绘制好的图形缓冲通过Binder交给SurfaceFlinger进行合成与显示,即上帧。
52
    在SkiaOpenGLPipeline的draw方法中,主要做了两件事:

1)创建SkSurface指针并初始化。

2)开始渲染。
53

    SkiaOpenGLPipeline的renderFrame方法在SkiaOpenGLPipeline的父类SkiaPipeline中实现。

    在SkiaPipeline的renderFrame方法中,主要做了四件事:

1)获取SkCanvas。

2)处理发生变化的Layer,更新对应RenderNode。

3)对所有的renderNode进行绘制。

4)释放SkCanvas。
54

5.1 节点的更新

    在SkiaPipeline的renderLayersImpl方法中,主要做了三件事:

1)遍历LayerUpdateQueue,获取Entry,并从Entry中获取RenderNode.

2)从RenderNode中获取SkCanvas。

3)将RenderNode分装成RenderNodeDrawable,并绘制到SkCanvas上。
55
    RenderNodeDrawable继承自SkDrawable。在RenderNodeDrawable的draw方法中,会调用onDraw方法,onDraw方法在RenderNodeDrawable被重写。在RenderNodeDrawable的onDraw方法中,会调用forceDraw方法。在RenderNodeDrawable的forceDraw方法中,会调用drawContent方法。
56
    在RenderNodeDrawable的drawContent方法中,主要做了两件事:

1)从RenderNode中获取DisplayList。

2)绘制DisplayList。
57
    在SkiaDisplayList的draw方法中,会调用DisplayListData的draw方法。
58
    在DisplayListData的draw方法中,会调用map方法。在DisplayListData的map方法中,主要做了五件事:

1)计算绘制指令内存中绘制指令的终止区域。

2)从绘制指令内存的起始位置进行遍历。

3)获取绘制指令的类型和长度。

4)根据指令的类型,调用指令的绘制方法。

5)根据指令的长度,计算下一个绘制指令的位置。
59
    Array<Fn>是模版生成的代码,其中的模版参数Fn代表不同类型的绘制指令的draw方法的调用。以DrawRect指令为例,当ptr指针指向DrawRect指令时,会根据指令类型从Array<Fn>取出对应DrawRect的draw方法调用的Fn,当调用Fn时,会触发DrawRect的draw方法的执行。

    在DrawRect的draw方法中,会通过SkCanvas的draw方法完成绘制。
60

5.2 节点的渲染

    在SkiaPipeline的renderFrameImpl方法中,主要做了两件事:

1)对RenderNode进行遍历,将RenderNode封装成RenderNodeDrawable。

2)对RenderNodeDrawable进行渲染。
61
    与renderLayerImpl不同,renderFrameImpl绘制到缓存对应的SkCanvas上,而不是自身的SkCanvas上。

三.总结

1.硬件渲染

    Android硬件渲染分成两个部分:渲染指令列表的构建和渲染指令列表的渲染,分别对应着ThreadedRenderer的updateRootDisplayList方法和syncAndDrawFrame方法。即绘制过程和渲染过程是分开的。

    绘制过程发生在UI线程,渲染过程发生在Render线程。

2.绘制流程

    在硬件渲染中,每个View对应着一个RenderNode。每个RenderNode中保存着对应的SkiaDisplayList。

    硬件的绘制过程,本质上是将待绘制的数据封装成对应的硬件渲染指令,保存到SkiaDisplayList中。子View绘制到完成后,父View会对子View的RenderNode进行绘制,将子View的RenderNode绘制指令保存到SkiaDisplayList中,形成绘制的层级结构。

    画布的获取本质上是对SkiaDisplayList的创建与初始化。

    绘制的提交本质上是将构建好的SkiaDisplayList保存到RenderNode中。

2.1 渲染指令

    在Android中,所有的绘制指令都继承了Op。Op是一个结构体,在Op中有两个字段,type表示指令的类型,skip表示指令的长度。

struct Op {
    uint32_t type : 8;
    uint32_t skip : 24;
  };

    所有的渲染指令都保存到DisplayListData中,DisplayListData本质上是一块连续的内存。

2.2 对应关系

    SkiaRecordingCanvas继承自SkiaCanvas,RecordingCanvas继承自SkCanvas。

    RecordingCanvas负责管理硬件渲染指令和DisplayListData、操作硬件渲染指令在DisplayListData上的填充。SkiaRecordingCanvas是对RecordingCanvas的封装,负责DisplayListData的大小分配与初始化、SkiaDisplayList的管理和子View的RenderNode更新保存。

    RecordingCanvas直接操作DisplayListData,SkiaRecordingCanvas直接操作SkiaDisplayList。SkiaDisplayList是对DisplayListData的屏蔽封装。

3.渲染流程

    硬件的渲染过程分成三部分:渲染指令列表(SkiaDisplayList)的获取、渲染层级(TreeInfo)的构建、渲染管线(IRenderPipeline)的渲染。

3.1 渲染指令列表的获取

    渲染指令列表的获取过程会阻塞UI线程,在获取完成后会唤醒UI线程。

3.2 渲染层级的构建

    渲染层级的构建过程本质上是从DecorView的RootRenderNode开始向下判断哪些子View的RenderNode发生了变化,并把变化的RenderNode保存到TreeInfo中。

3.3 渲染管线的渲染

    在Android中,渲染管线有两种:SkiaOpenGLPipeline和SkiaVulkanPipeline,底层实现分别对应着OpenGL和Vulkan。

    渲染管线的渲染过程分成两部分:RenderNode的更新渲染和RenderNode的最终渲染。

    渲染管线在渲染过程中会将RenderNode封装成RenderNodeDrawable。RenderNode的更新渲染本质上就是在RenderNode自身的SkCanvas上绘制。RenderNode的最终渲染本质上就是在生产消费模型对应的SkCanvas上绘制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1699648.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FFmpeg的流程

文章目录 前序代码结构FFmpeg.cffmpeg_opt.c 小结 前序 之前看过FFmpeg的各种命令&#xff0c;然后不是很理解。相信很多人都不是很理解&#xff0c;毕竟&#xff0c;单纯的去记住那些命令行本身就需要很大的内存&#xff0c;我们的大脑内存又有限&#xff0c;所以&#xff0c…

spring cloud alibaba sentinel 配置过程 流控 降级热点 授权

目录 1.基础理论 2.配置 3.加入依赖和配置文件 4.流控 1.基础理论 Sentinel是阿里开源的项目&#xff0c;提供了流量控制、熔断降级、系统负载保护等多个维度来保障服务之间的稳定性。 丰富的应用场景 &#xff1a;Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心…

锁相环的一些学习笔记--(1)

下图两组1.2.3可以对应起来&#xff1b; 一些分析&#xff1a; 1.根据这个可知最后vco_voltage停在0.5v 参考资料&#xff1a; 1. Matlab https://www.bilibili.com/video/BV1bR4y1Z7Xg/?spm_id_from333.1296.top_right_bar_window_history.content.click&vd_source555…

windows 搭建 go开发环境

go语言&#xff08;或 Golang&#xff09;是Google开发的开源编程语言&#xff0c;诞生于2006年1月2日下午15点4分5秒&#xff0c;于2009年11月开源&#xff0c;2012年发布go稳定版。Go语言在多核并发上拥有原生的设计优势&#xff0c;Go语言从底层原生支持并发&#xff0c;无须…

Google Play 提示 “您的设备与此版本不兼容“ 解决方案

一、 问题概述Google Play提示“您的设备与此版本不兼容”&#xff0c;无法安装应用。 遇到问题的设备为Xiaomi Mi A3&#xff0c;查了下这台手机的基本信息&#xff0c;Android One系统&#xff0c;版本分为9.0、10.0、11.0。 二、 问题分析Google Play的过滤器 通常有以下5种…

2024-5-10-从0到1手写配置中心Config之Spring Value热更新

定义SpringValueProcessor处理类 实现BeanPostProcessor后置处理器接口&#xff0c;扫描所有的Spring value&#xff0c;保存起来。实现ApplicationListener接口&#xff0c;在配置变更时&#xff0c;更新所有的spring value 实现BeanPostProcessor后置处理器接口 实现postPr…

Value-Based Reinforcement Learning(1)

Action-Value Functions Discounted Return&#xff08;未来的reward&#xff0c;由于未来存在不确定性&#xff0c;所以未来的reward 要乘以进行打折&#xff09; 这里的依赖actions &#xff0c;和states 这里 Policy Function : &#xff0c;表达了action的随机性 S…

HILL密码

一&#xff1a;简介 Hill密码又称希尔密码是运用基本矩阵论原理的替换密码&#xff0c;属于多表代换密码的一种&#xff0c;由L e s t e r S . H i l l Lester S. HillLesterS.Hill在1929年发明。 二&#xff1a;原理 1.对于每一个字母&#xff0c;我们将其转化为对应的数字&am…

RS8751XF功能和参数介绍及PDF资料

以下是关于RS8751XF的功能和参数的介绍&#xff1a; 功能描述: 高速、宽带单通道运算放大器 轨到轨输入和输出&#xff0c;确保较大的动态范围 极高的增益带宽乘积&#xff08;GBW&#xff09;&#xff1a;250 MHz&#xff0c;适合高频应用 极高的压摆率&#xff08;SR&#xf…

SparkStreaming架构原理(详解)

Spark概述 SparkStreaming架构原理 Spark Streaming的架构主要由以下几个关键部分组成。 1.数据源接收器&#xff08;Receiver&#xff09; 执行流程开始于数据源接收阶段&#xff0c;其中接收器&#xff08;Receiver&#xff09;负责从外部数据源获取数据流。 接收器可以连…

SpringBoot+Vue开发记录(六)-- 后端配置mybatis

原型图什么的就先不管&#xff0c;后面再写。 本篇文章的主要内容就是springboot通过mybatis操作数据库实现增删改查。 重点是mybatis配置与相关文件数据&#xff0c;以后开新项目忘记了怎么配置的话可以再照着这个搞。 这算是最基础的部分了吧。 文章目录 一&#xff0c;配置…

安全分析[2]之计算机系统安全分析

文章目录 计算机系统结构计算机系统安全风险软件安全漏洞 计算机系统结构 计算机系统安全风险 计算机硬件安全&#xff1a;因设计不当、器件的质量及使用寿命的限制、外界因素等导致的计算机系统硬件出现故障&#xff0c;进而影响到整个系统的安全。特别是如果是CPU出现安全漏…

Docker容器网络互联

默认情况下&#xff1a;所有容器都是以bridge方式链接到Docker的一个虚拟网桥上。 命令 docker network create 创建一个网络 docker network ls 查看所有网络 docker network rm 删除指定网络 docker network prune 清除未使用的网络 docker network connect 使容器加入某个网…

JUC框架(CAS、ATOMIC、AQS)

文章目录 JUC之CASJUC之ATOMICJUC之AQSAQS简介AQS原理 更多相关内容可查看 JUC之CAS **CAS&#xff08;compareAndSwap&#xff09;**也叫比较交换&#xff0c;是一种无锁原子算法&#xff0c;其作用是让**CPU**将内存值更新为新值&#xff0c;但是有个条件&#xff0c;内存值…

Android Compose 八:常用组件 Switch

Switch 切换按钮 val isChecked remember { mutableStateOf(true) }Switch(checked isChecked.value,onCheckedChange {Log.i("text_compose","onCheckedChange>>"it)isChecked.value it})效果 默认颜色 应该对应 主题色 1.1 thumbContent 按钮…

表查询基础【mysql】【表内容 增,删,改,查询】

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;Linux_花果山~程序猿的博客-CSDN博客MySQL之旅_花果山~程序猿的博客-CSDN博客Linux_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我…

DDR5—新手入门学习(一)【1-5】

目录 1、DDR背景 &#xff08;1&#xff09;SDR SDRAM时代 &#xff1a; &#xff08;2&#xff09;DDR SDRAM的创新 &#xff1a; &#xff08;3&#xff09;DDR技术的演进 &#xff1a; &#xff08;4&#xff09;需求推动&#xff1a; 2、了解内存 &#xff08;1&…

领导VS管理:技术团队掌舵者的双重角色解析

文章目录 引言一、管理1.1 管理的定义1.2 管理的特点1.3 管理的作用 二、领导2.1 领导的定义2.2 领导的特点2.3 领导的作用 三、管理与领导的互补性3.1 互补策略 总结 引言 在科技飞速发展的今天&#xff0c;技术团队作为推动企业创新的核心力量&#xff0c;其管理显得尤为关键…

python command乱码怎么解决

python command乱码怎么解决&#xff1f;具体方法如下&#xff1a; 先引入import sys 再加一句&#xff1a;typesys.getfilesystemencoding() 然后在输出乱码的数据的后面加上“.decode(utf-8).encode(type)”。 比如输入“ss”乱码。 就写成print ss.decode(utf-8).encode(typ…

【Go专家编程——协程】

1 协程的概念 1.1 基本概念 进程是应用程序的启动实例&#xff0c;每个进程都有独立的内存空间&#xff0c;不同进程通过进程间的通信方式来通信线程从属于进程&#xff0c;每个进程至少包含一个线程&#xff0c;线程是CPU调度的基本单元&#xff0c;多个线程之间可以共享进程…