UGUI中点击判断的原理

news2024/11/18 21:29:18

在这里插入图片描述
首选需要理解 EventSystem 中的代码结构,EventSystem 目录下包含4个子文件夹,分别是 EventData、InputModules,Raycasters 和 UIElements,UIElements 下是 UI Toolkit 相关代码,这里不做研究,主要关注其他三部分内容

EventData

在这里插入图片描述
EventData存储事件信息,PointerEventData存储点击,拖动等信息,AxisEventData存储轴事件数据,如手柄摇杆

InputModules

在这里插入图片描述
BaseInputModule(抽象类)引发事件并将其发送给 GameObjects 进行处理

Input是输入系统接口,可以获取输入信息如鼠标位置,但是这部分代码没有开源,而 BaseInput 是对 Input 类的封装

BaseInputModule 有个 BaseInput 类型的变量 m_DefaultInput,子类通过它获取一些输入信息

PointerInputModule(抽象类)用于处理点击类型的输入

StandaloneInputModule 是PC、Mac&Linux上的具体实现,而 TouchInputModule 是IOS、Android等移动平台上的具体实现

在这里插入图片描述
EventSystem 上面挂着 StandaloneInputModule 组件,并且运行时会添加 BaseInput 脚本

Raycasters

在这里插入图片描述
RaycasterManager 中有个List管理所有的Raycaster

BaseRaycaster(抽象类)负责对场景元素进行射线检测以确定光标是否在它们上面,sortOrderPriority和renderOrderPriority这两个字段用来对结果进行排序,在OnEnable中注册到RaycasterManager,在OnDisable移除,Raycast是抽象方法,在子类中实现

PhysicsRaycaster 是对3D组件进行射线检测,结果根据距离进行排序

Physics2DRaycaster 是对2D组件进行射线检测

GraphicRaycaster 对Graphic元素进行射线检测,所有UI元素(Image,RawImage等)的基类都是Graphic,这里最重要的是2个Raycast重载函数,这里略去不重要的代码

public class GraphicRaycaster : BaseRaycaster
{
    [NonSerialized] private List<Graphic> m_RaycastResults = new List<Graphic>();

    /// <summary>
    /// 对Canvas相关的Graphic元素进行射线检测,结果存入resultAppendList
    /// </summary>
    public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList)
    {
        if (canvas == null)
            return;

		// 获取Canvas上所有可点击,即raycastTarget = true的Graphic元素
        var canvasGraphics = GraphicRegistry.GetRaycastableGraphicsForCanvas(canvas);
        if (canvasGraphics == null || canvasGraphics.Count == 0)
            return;

        int displayIndex;
        // Canvas为ScreenSpaceOverlay模式下默认为null
        var currentEventCamera = eventCamera;

        if (canvas.renderMode == RenderMode.ScreenSpaceOverlay || currentEventCamera == null)
            displayIndex = canvas.targetDisplay;
        else
            displayIndex = currentEventCamera.targetDisplay;

		// 获取鼠标位置
        var eventPosition = Display.RelativeMouseAt(eventData.position);
        if (eventPosition != Vector3.zero)
        {
            int eventDisplayIndex = (int)eventPosition.z;
            if (eventDisplayIndex != displayIndex)
                return;
        }
        else
        {
            eventPosition = eventData.position;
        }

        // 把eventPosition转换到相机空间
        Vector2 pos;
        if (currentEventCamera == null)
        {
            float w = Screen.width;
            float h = Screen.height;
            if (displayIndex > 0 && displayIndex < Display.displays.Length)
            {
                w = Display.displays[displayIndex].systemWidth;
                h = Display.displays[displayIndex].systemHeight;
            }
            pos = new Vector2(eventPosition.x / w, eventPosition.y / h);
        }
        else
            pos = currentEventCamera.ScreenToViewportPoint(eventPosition);

        // 不在相机视口内
        if (pos.x < 0f || pos.x > 1f || pos.y < 0f || pos.y > 1f)
            return;

        float hitDistance = float.MaxValue;

        Ray ray = new Ray();
		// currentEventCamera不为空,摄像机发射射线
        if (currentEventCamera != null)
            ray = currentEventCamera.ScreenPointToRay(eventPosition);

        if (canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None)
        {
			// 这里主要检测2D/3D物体对UI的遮挡的,略
        }

        m_RaycastResults.Clear();

        Raycast(canvas, currentEventCamera, eventPosition, canvasGraphics, m_RaycastResults);

        int totalCount = m_RaycastResults.Count;
        for (var index = 0; index < totalCount; index++)
        {
            var go = m_RaycastResults[index].gameObject;
            bool appendGraphic = true;

            if (ignoreReversedGraphics)
            {
                if (currentEventCamera == null)
                {
                    // 点乘判断元素是否面向前方
                    var dir = go.transform.rotation * Vector3.forward;
                    appendGraphic = Vector3.Dot(Vector3.forward, dir) > 0;
                }
                else
                {
                    // 与摄像头的前方进行比较
                    var cameraForward = currentEventCamera.transform.rotation * Vector3.forward * currentEventCamera.nearClipPlane;
                    appendGraphic = Vector3.Dot(go.transform.position - currentEventCamera.transform.position - cameraForward, go.transform.forward) >= 0;
                }
            }

            if (appendGraphic)
            {
                float distance = 0;
                Transform trans = go.transform;
                Vector3 transForward = trans.forward;

                if (currentEventCamera == null || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
                    distance = 0;
                else
                {
                    distance = (Vector3.Dot(transForward, trans.position - ray.origin) / Vector3.Dot(transForward, ray.direction));

                    // 是否在相机后面
                    if (distance < 0)
                        continue;
                }

                if (distance >= hitDistance)
                    continue;

                var castResult = new RaycastResult
                {
                    gameObject = go,
                    module = this,
                    distance = distance,
                    screenPosition = eventPosition,
                    displayIndex = displayIndex,
                    index = resultAppendList.Count,
                    depth = m_RaycastResults[index].depth,
                    sortingLayer = canvas.sortingLayerID,
                    sortingOrder = canvas.sortingOrder,
                    worldPosition = ray.origin + ray.direction * distance,
                    worldNormal = -transForward
                };
                resultAppendList.Add(castResult);
            }
        }
    }

    /// <summary>
    /// 重载函数,收集点击位置处的Graphic元素
    /// </summary>
    [NonSerialized] static readonly List<Graphic> s_SortedGraphics = new List<Graphic>();
    private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
    {
		// foundGraphics是Canvas上所有可点击的Graphic元素
        int totalCount = foundGraphics.Count;
        for (int i = 0; i < totalCount; ++i)
        {
            Graphic graphic = foundGraphics[i];

            // -1表示Canvas尚未对其进行处理,也就是说它实际上并未绘制
            if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
                continue;

			// 检测点击位置是否在RectTransform形成的矩形内
            if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera, graphic.raycastPadding))
                continue;

			// 是否超出相机远裁剪面
            if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
                continue;

			// 进行后续的检测
            if (graphic.Raycast(pointerPosition, eventCamera))
            {
                s_SortedGraphics.Add(graphic);
            }
        }

		// 根据深度进行排序
        s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
        totalCount = s_SortedGraphics.Count;
        for (int i = 0; i < totalCount; ++i)
            results.Add(s_SortedGraphics[i]);

        s_SortedGraphics.Clear();
    }
}

跳转到 graphic.Raycast 方法查看后续的检测,这里获取 graphic 上的所有组件转换为接口 ICanvasRaycastFilter,然后调用接口的 IsRaycastLocationValid 方法进行最后的检测

public abstract class Graphic: UIBehaviour,ICanvasElement
{
    /// <summary>
    /// 确定点击的位置是否合法,sp是Screen point,返回True表示合法
    /// </summary>
    public virtual bool Raycast(Vector2 sp, Camera eventCamera)
    {
        if (!isActiveAndEnabled)
            return false;

        var t = transform;
        var components = ListPool<Component>.Get();

        bool ignoreParentGroups = false;
        bool continueTraversal = true;

        while (t != null)
        {
            t.GetComponents(components);
            for (var i = 0; i < components.Count; i++)
            {
                var canvas = components[i] as Canvas;
                if (canvas != null && canvas.overrideSorting)
                    continueTraversal = false;

                var filter = components[i] as ICanvasRaycastFilter;

                if (filter == null)
                    continue;

                var raycastValid = true;

                var group = components[i] as CanvasGroup;
                if (group != null)
                {
                    if (ignoreParentGroups == false && group.ignoreParentGroups)
                    {
                        ignoreParentGroups = true;
                        raycastValid = filter.IsRaycastLocationValid(sp, eventCamera);
                    }
                    else if (!ignoreParentGroups)
                        raycastValid = filter.IsRaycastLocationValid(sp, eventCamera);
                }
                else
                {
                    raycastValid = filter.IsRaycastLocationValid(sp, eventCamera);
                }

                if (!raycastValid)
                {
                    ListPool<Component>.Release(components);
                    return false;
                }
            }
            t = continueTraversal ? t.parent : null;
        }
        ListPool<Component>.Release(components);
        return true;
    }
}

在这里插入图片描述
Image、Mask以及CanvasGroup都实现了ICanvasRaycastFilter接口,查看Image的具体实现

public class Image : MaskableGraphic, ISerializationCallbackReceiver, ILayoutElement, ICanvasRaycastFilter
{
	/// <summary>
    /// 计算位置是否为有效的命中位置,根据图片的Alpha值是否大于阈值
    /// </summary>
	public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
    {
        if (alphaHitTestMinimumThreshold <= 0)
            return true;

        if (alphaHitTestMinimumThreshold > 1)
            return false;

        if (activeSprite == null)
            return true;

        Vector2 local;
        if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local))
            return false;

        Rect rect = GetPixelAdjustedRect();

        // Convert to have lower left corner as reference point.
        local.x += rectTransform.pivot.x * rect.width;
        local.y += rectTransform.pivot.y * rect.height;

        local = MapCoordinate(local, rect);

        // Convert local coordinates to texture space.
        Rect spriteRect = activeSprite.textureRect;
        float x = (spriteRect.x + local.x) / activeSprite.texture.width;
        float y = (spriteRect.y + local.y) / activeSprite.texture.height;

        try
        {
            return activeSprite.texture.GetPixelBilinear(x, y).a >= alphaHitTestMinimumThreshold;
        }
        catch (UnityException e)
        {
            Debug.LogError("Using alphaHitTestMinimumThreshold greater than 0 on Image whose sprite texture cannot be read. " + e.Message + " Also make sure to disable sprite packing for this sprite.", this);
            return true;
        }
    }
}

注意:所有勾选 RaycastTarget 的UI元素都会进行射线检测,参与一些坐标,排序计算,数量太多会影响效率,因此不需要交互的元素不要勾选 RaycastTarget

EventSystem

EventSystem 处理输入、射线检测和发送事件,在 EventSystem 中有个列表 m_SystemInputModules 保存所有的输入模块,在Update中更新当前输入模块m_CurrentInputModule,并调用CurrentInputModule.Process方法进行输入处理

在输入模块如 PointerInputModule 中会调用EventSystem.RaycastAll,进行射线检测并将结果保存到列表中,然后调用EventSystem.RaycastComparer对结果进行排序

public class EventSystem : UIBehaviour
{
	// 所有的输入模块
    private List<BaseInputModule> m_SystemInputModules = new List<BaseInputModule>();
	// 当前输入模块,PC上就是StandaloneInputModule
    private BaseInputModule m_CurrentInputModule;

    protected virtual void Update()
    {
    	// 更新m_SystemInputModules中所有的输入模块
        TickModules();

		// 更新当前输入模块
        bool changedModule = false;
        var systemInputModulesCount = m_SystemInputModules.Count;
        for (var i = 0; i < systemInputModulesCount; i++)
        {
            var module = m_SystemInputModules[i];
            if (module.IsModuleSupported() && module.ShouldActivateModule())
            {
                if (m_CurrentInputModule != module)
                {
                    ChangeEventModule(module);
                    changedModule = true;
                }
                break;
            }
        }

        // 当前输入模块为空,选择第一个有效的,略

		// Process方法中处理输入
        if (!changedModule && m_CurrentInputModule != null)
            m_CurrentInputModule.Process();
    }

    /// <summary>
    /// 进行射线检测并将结果添加到raycastResults
    /// </summary>
    public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
    {
        raycastResults.Clear();
        // 获取所有注册的BaseRaycaster对象,如GraphicRaycaster,PhysicsRaycaster,Physics2DRaycaster
        var modules = RaycasterManager.GetRaycasters();
        var modulesCount = modules.Count;
        for (int i = 0; i < modulesCount; ++i)
        {
            var module = modules[i];
            if (module == null || !module.IsActive())
                continue;

			// 进行射线检测,并将结果添加到raycastResults
            module.Raycast(eventData, raycastResults);
        }

		// 对结果进行排序,保证UI优先处理
        raycastResults.Sort(s_RaycastComparer);
    }

	private static readonly Comparison<RaycastResult> s_RaycastComparer = RaycastComparer;

	/// <summary>
    /// 对射线检测的结果进行比较
    /// </summary>
    private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
    {
        if (lhs.module != rhs.module)
        {
            var lhsEventCamera = lhs.module.eventCamera;
            var rhsEventCamera = rhs.module.eventCamera;
            if (lhsEventCamera != null && rhsEventCamera != null && lhsEventCamera.depth != rhsEventCamera.depth)
            {
                // 比较camera深度
                if (lhsEventCamera.depth < rhsEventCamera.depth)
                    return 1;
                if (lhsEventCamera.depth == rhsEventCamera.depth)
                    return 0;

                return -1;
            }
			
			// 比较排序优先级
            if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
                return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);

			// 比较渲染优先级
            if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
                return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
        }

		// 比较sortingLayer
        if (lhs.sortingLayer != rhs.sortingLayer)
        {
            var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
            var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
            return rid.CompareTo(lid);
        }

		// 比较sortingOrder 
        if (lhs.sortingOrder != rhs.sortingOrder)
            return rhs.sortingOrder.CompareTo(lhs.sortingOrder);

        // 比较深度
        if (lhs.depth != rhs.depth && lhs.module.rootRaycaster == rhs.module.rootRaycaster)
            return rhs.depth.CompareTo(lhs.depth);

		// 比较距离
        if (lhs.distance != rhs.distance)
            return lhs.distance.CompareTo(rhs.distance);

		// 比较index
        return lhs.index.CompareTo(rhs.index);
    }
}

查看 StandaloneInputModule 中鼠标事件相关方法,这里为了简洁只关注点击方法

public class StandaloneInputModule : PointerInputModule
{
    public override void Process()
    {
        if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
            return;

        bool usedEvent = SendUpdateEventToSelectedObject();

        // 优先处理触摸事件,如果有鼠标则处理鼠标事件
        if (!ProcessTouchEvents() && input.mousePresent)
            ProcessMouseEvent();
    }

	protected void ProcessMouseEvent()
    {
        ProcessMouseEvent(0);
    }

	/// <summary>
    /// 处理所有的鼠标事件
    /// </summary>
    protected void ProcessMouseEvent(int id)
    {
        var mouseData = GetMousePointerEventData(id);
        var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;

        m_CurrentFocusedGameObject = leftButtonData.buttonData.pointerCurrentRaycast.gameObject;

        // 优先处理鼠标左键
        ProcessMousePress(leftButtonData);
        // 处理右键和中键,按下和拖动事件,略。。。
    }

    /// <summary>
    /// 计算和处理任何鼠标按钮的状态变化
    /// </summary>
    protected void ProcessMousePress(MouseButtonEventData data)
    {
        var pointerEvent = data.buttonData;
        var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;

        // 按下
        if (data.PressedThisFrame())
        {
        	// 给pointerEvent填充数据,略
        }

        // 抬起
        if (data.ReleasedThisFrame())
        {
            ReleaseMouse(pointerEvent, currentOverGo);
        }
    }

    private void ReleaseMouse(PointerEventData pointerEvent, GameObject currentOverGo)
    {
    	// 触发抬起事件
        ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
        var pointerClickHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);

        if (pointerEvent.pointerClick == pointerClickHandler && pointerEvent.eligibleForClick)
        {
        	// 触发鼠标点击事件
            ExecuteEvents.Execute(pointerEvent.pointerClick, pointerEvent, ExecuteEvents.pointerClickHandler);
        }
        // 处理拖动相关事件,略
    }
}

查看 ExecuteEvents 的具体实现,简单说就是将组件转为IEventSystemHandler,然后执行委托方法

public static class ExecuteEvents
{
    public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor) where T : IEventSystemHandler
    {
        var internalHandlers = ListPool<IEventSystemHandler>.Get();
        GetEventList<T>(target, internalHandlers);

        var internalHandlersCount = internalHandlers.Count;
        for (var i = 0; i < internalHandlersCount; i++)
        {
            T arg;
            try
            {
            	// 转换成目标类型,如IPointerClickHandler
                arg = (T)internalHandlers[i];
            }
            catch (Exception e)
            {
                var temp = internalHandlers[i];
                Debug.LogException(new Exception(string.Format("Type {0} expected {1} received.", typeof(T).Name, temp.GetType().Name), e));
                continue;
            }

            try
            {
            	// 执行委托
                functor(arg, eventData);
            }
            catch (Exception e)
            {
                Debug.LogException(e);
            }
        }

        var handlerCount = internalHandlers.Count;
        ListPool<IEventSystemHandler>.Release(internalHandlers);
        return handlerCount > 0;
    }

	/// <summary>
    /// 将组件转换成IEventSystemHandler,并放入列表中
    /// </summary>
    private static void GetEventList<T>(GameObject go, IList<IEventSystemHandler> results) where T : IEventSystemHandler
    {
        if (go == null || !go.activeInHierarchy)
            return;

        var components = ListPool<Component>.Get();
        go.GetComponents(components);

        var componentsCount = components.Count;
        for (var i = 0; i < componentsCount; i++)
        {
            if (!ShouldSendToComponent<T>(components[i]))
                continue;
            results.Add(components[i] as IEventSystemHandler);
        }
        ListPool<Component>.Release(components);
    }
}

在这里插入图片描述
上述代码中的 IEventSystemHandler 是很多其他接口的父接口,Button实现了IPointerClickHandler
所以点击Button,会通过 IPointerClickHandler 接口调用它的 OnPointerClick 方法

参考

关于UGUI中点击判断的原理探究

[UGUI源码一]6千字带你入门UGUI源码

UGUI的点击事件机制

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

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

相关文章

linux文件及文件内容查找命令总结

在linux环境下&#xff0c;我们经常要查找一个文件或者文件的内容&#xff0c;但搜索的命令有很多&#xff0c;这些命令都有什么区别&#xff0c;应该怎么选择和使用呢&#xff1f; 下面总结了一些常见的文件查找、内容查找的命令&#xff0c;收藏起来备用吧。 文件查找 where…

每日学术速递4.25

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Long-Term Photometric Consistent Novel View Synthesis with Diffusion Models 标题&#xff1a;具有扩散模型的长期光度一致的新视图合成 作者&#xff1a;Jason J. Yu, Feresh…

Python入门教程+项目实战-11.3节: 元组的操作方法

目录 11.3.1 元组的常用操作方法 11.3.2 元组的查找 11.3.3 知识要点 11.3.4 系统学习python 11.3.1 元组的常用操作方法 元组类型是一种抽象数据类型&#xff0c;抽象数据类型定义了数据类型的操作方法&#xff0c;在本节的内容中&#xff0c;着重介绍元组类型的操作方法…

hive udf, tried to access method org.bouncycastle.math.ec.ECPoint$AbstractFp

在hive中添加加密udf,测试报错&#xff1a; select encrypt_sm2("aa","04AD9356466C7A505B3B2E18F2484E1F096108FA19C0F61C707A808EDF7C132BC3CE33E63D2CC6D77FB0A172004F8F5282CEADE22ED9628A02FE8FD85AF1EFE8B3"); Error: Error while compiling statem…

从0搭建Vue3组件库(九):VitePress 搭建部署组件库文档

VitePress 搭建组件库文档 当我们组件库完成的时候,一个详细的使用文档是必不可少的。本篇文章将介绍如何使用 VitePress 快速搭建一个组件库文档站点并部署到GitHub上 安装 首先新建 site 文件夹,并执行pnpm init,然后安装vitepress和vue pnpm install -D vitepress vue安…

什么是分库分表?为什么需要分表?什么时候分库分表

不急于上手实战 ShardingSphere 框架&#xff0c;先来复习下分库分表的基础概念&#xff0c;技术名词大多晦涩难懂&#xff0c;不要死记硬背理解最重要&#xff0c;当你捅破那层窗户纸&#xff0c;发现其实它也就那么回事。 什么是分库分表 分库分表是在海量数据下&#xff0…

“星河杯”隐私计算大赛新闻发布会在京召开

4月24日下午&#xff0c;“星河杯”隐私计算大赛新闻发布会在京召开。本次大赛由中国信通院、中国通信学会、隐私计算联盟共同主办&#xff0c;中移动信息技术有限公司、联通数字科技有限公司、天翼电子商务有限公司、中国通信标准化协会大数据技术标准推进委员会联合协办&…

微信小程序 | 基于高德地图+ChatGPT实现旅游规划小程序

&#x1f388;&#x1f388;效果预览&#x1f388;&#x1f388; ❤ 路劲规划 ❤ 功能总览 ❤ ChatGPT交互 一、需求背景 五一假期即即将到来&#xff0c;在大家都阳过之后&#xff0c;截止到目前这应该是最安全的一个假期。所以出去旅游想必是大多数人的选择。 然后&#x…

Activity中startForResult的原理分析

前言&#xff1a; 如果使用androidX支持库中的ComponentActivity&#xff0c;会推荐使用registerForActivityResult的方式。但是对于不支持androidX的项目&#xff0c;或者就是继承自Activity的页面来说&#xff0c;startActivityForResult仍然是唯一的选择。 如果想了解andr…

虹科教您 | 虹科RELY-TSN-KIT操作指南(3)——基于Linux系统进行TSN协议测试

随着技术的变革和实际生产业务需求的推动&#xff0c;工厂内部互联架构逐渐趋于扁平化&#xff08;IT/OT融合&#xff09;&#xff0c;而TSN则是在这一背景下发展起来的新兴技术&#xff0c;旨在为以太网协议建立“通用”的时间敏感机制&#xff0c;以确保网络数据传输的时间确…

云计算服务安全评估办法

云计算服务安全评估办法 2019-07-22 14:46 来源&#xff1a; 网信办网站【字体&#xff1a;大 中 小】打印 国家互联网信息办公室 国家发展和改革委员会 工业和信息化部 财政部关于发布《云计算服务安全评估办法》的公告 2019年 第2号 为提高党政机关、关键信息基础设施运营者…

鸿蒙系统是什么?鸿蒙与开源鸿蒙的关系?鸿蒙系统的发展历程

鸿蒙OS分布式操作系统简介鸿蒙系统&#xff08;HarmonyOS)&#xff0c;是第一款基于微内核的全场景分布式OS&#xff0c;是华为自主研发的操作系统。现被华为捐献给开放原子基金会管理&#xff0c;为开放原子基金会下的一个项目。 从 系统定位 上来说&#xff0c;HarmonyOS是一…

explain都不懂?搞什么数据库优化,快进来学习了

文章目录 一、 前言二、MySQL EXPLAIN实战三、mysql EXPLAIN输出结果详解3.1 id详解3.2 select_type3.3 table3.4 partitions3.5 type3.6 possible_keys3.7key3.8 key_len3.9 ref3.10 rows3.11 filtered3.12 Extra 一、 前言 EXPLAIN 想必用过mysql的小伙伴都听过&#xff0c;…

PWM 呼吸灯实验

PWM 呼吸灯实验 FPGA实现一个PWM模块&#xff08;硬件&#xff09;来控制灯的亮灭。 实验原理 PWM本质上就是一个输出脉冲的硬件&#xff0c;通过改变一个周期高电平&#xff08;占空比&#xff09;的时间来对其他的硬件进行控制&#xff0c;比如电机。 呼吸灯的实现利用了人…

谈谈如何用开源网关进行 API 管理

需求痛点 1.企业不清楚到底有多少个API&#xff0c;无法形成API资产管理等问题。 2.API在不同集群的生命周期问题。 3.API运行状态监控和告警问题。 4.API请求限流、流量控制以及安全等问题。 功能介绍 Apinto的API管理提供API生命周期控制&#xff1a;可管理所有API&…

Cortex-R52 GIC:Generic Interrupt Controller(一)

ARM Cortex-R52 GIC:Generic Interrupt Controller 1.关于GIC 1.1 GIC Overview ARM的中断控制器被称为GIC(Generic Interrupt Controller)&#xff0c;GIC是支持和管理系统中断的资源的模块。它支持中断优先级、中断路由到CPU或输出端口、中断抢占和中断虚拟化等功能。 中断…

深入浅出Rust核心概念:生命周期

简介 Rust是一种快速、安全、并发的系统级编程语言&#xff0c;它的设计目标是提供一种高效、内存安全的编程方式。而生命周期&#xff08;Lifetime&#xff09;是Rust语言中的一个核心概念&#xff0c;它与内存管理、函数传参和引用操作等方面密切相关。LZ将详细介绍Rust中生…

GitHub 开启 2FA 双重身份验证的方法

为什么要开启 2FA 自2023年3月13日起&#xff0c;我们登录 GitHub 都会看到一个要求 Enable 2FA 的重要提示&#xff0c;具体如下&#xff1a; GitHub users are now required to enable two-factor authentication as an additional security measure. Your activity on Git…

Matplotlib 轴标签和标题

我们可以使用 xlabel() 和 ylabel() 方法来设置 x 轴和 y 轴的标签。 实例 import numpy as np import matplotlib.pyplot as pltx np.array([1, 2, 3, 4]) y np.array([1, 4, 9, 16]) plt.plot(x, y)plt.xlabel("x - label") plt.ylabel("y - label")…

Java BIO

1.Java BIO(Blocking IO:同步并阻塞式IO)编程 1.1.基本介绍 1>.Java BIO就是传统的java io编程,其相关的类和接口在"java.io"包下; 2>.BIO(Blocking I/O): 同步阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处…