【Android】事件分发机制

Android 的事件分发机制主要包括以下几个步骤:

  1. 事件生成:用户在设备上进行触摸、滑动等操作时,系统会生成相应的事件,如触摸事件(MotionEvent)。

  2. 事件发送:生成的事件会被发送到当前活动(Activity)或视图(View)树的根节点。

  3. 事件分发

    1. Activity:首先,事件会被传递给活动的 dispatchTouchEvent() 方法。这个方法决定如何将事件进一步分发。

    2. ViewGroup:如果当前活动包含 ViewGroup(如 LinearLayoutRelativeLayout 等),dispatchTouchEvent() 会先调用 ViewGrouponInterceptTouchEvent() 方法。如果返回 true,则 ViewGroup 会处理事件;如果返回 false,则将事件传递给子视图。

    3. View:对于普通的视图(View),会调用其 onTouchEvent() 方法来处理事件。

  4. 事件处理

    1. onTouchEvent():当视图接收到事件后,会根据事件的类型(如按下、移动、抬起等)在此方法中处理相应的逻辑。

    2. 事件处理过程可能涉及多个视图,尤其是在有嵌套的视图结构中。

  5. 事件消费:如果某个视图处理了事件(返回 true),后续的视图将不会再接收到这个事件。如果没有视图消费事件,事件将向上传递,直到达到活动。

  6. 最终结果:处理完成后,结果可能会影响用户界面的状态或行为。

注意事项:

  • onInterceptTouchEvent():在 ViewGroup 中使用,决定是否拦截子视图的事件。

  • 事件的传递顺序:从上到下(Activity → ViewGroup → View),处理顺序是从下到上(View → ViewGroup → Activity)。

好的,针对你提到的几个基础认知点,以下是更深入的解析:

事件分发的由来

由于 Android 中的视图是树形结构的,多个视图可能重叠并同时响应用户的触摸事件。

这种情况下,如何选择一个特定的视图来处理事件就变得尤为重要。事件分发机制的目的是在复杂的视图层次中,准确、高效地找到合适的视图来响应用户的交互。

事件的定义

触摸事件是用户与设备交互时产生的信号,通常由一系列连续的触摸状态构成。

事件列从 ACTION_DOWN 开始(手指触摸屏幕),

中间可能有多个 ACTION_MOVE 事件(手指移动),

最后以 ACTION_UP 结束(手指离开屏幕)。

这种事件序列使得应用能够理解用户的意图,比如滑动或点击。

事件分发的本质

事件分发的本质在于动态选择并传递触摸事件到合适的视图,同时根据用户的交互来决定事件的处理逻辑。以下是更深入的讲解:

  1. 决策过程:当触摸事件发生时,Activity 首先接收事件,执行 dispatchTouchEvent() 方法。在这个方法中,系统根据事件的类型和当前视图的状态(如是否可见、是否可点击)决定是否继续向下传递事件。

  2. 拦截机制ViewGroup 可以通过重写 onInterceptTouchEvent() 方法来决定是否拦截事件。如果返回 true,则该 ViewGroup 将直接处理事件,子视图将不再接收到此事件。这一机制允许 ViewGroup 在需要时优先处理特定的触摸交互,比如滚动或拖动。

  3. 事件传递链:如果事件没有被拦截,它将继续向下传递到子视图。每个视图都有自己的 onTouchEvent() 方法,用于处理具体的事件。如果一个视图能处理该事件(如点击),它会返回 true,表示事件已被消费,后续视图将不会再接收到该事件。这种机制使得触摸事件的处理能够精准到具体的视图。

  4. 状态管理:视图的状态(如按下、抬起、移动等)影响事件的处理。视图在 onTouchEvent() 中根据当前状态执行不同的逻辑,如更新视觉反馈、触发动画或改变内部状态。这种动态响应使得用户体验更加流畅和自然。

  5. 事件队列与优化:Android 还利用事件队列来优化事件处理。通过合并和优化事件,系统能够减少不必要的处理,提高性能。这在复杂交互或高频触摸操作中尤为重要。

事件传递的对象

事件在 Android 中主要在 ActivityViewGroupView 之间传递。

Activity 作为应用的入口,首先接收事件;ViewGroup 可能根据需要拦截事件,而具体的 View 则负责执行实际的事件处理。这种多层次的传递机制保证了事件处理的灵活性和精确性。

事件分发顺序

  1. Activity:当用户触摸屏幕时,事件首先被发送到当前的 ActivityActivity 会调用其 dispatchTouchEvent() 方法,决定事件的后续处理。

  2. ViewGroup:如果 ActivitydispatchTouchEvent() 方法未拦截事件,事件将传递到 ViewGroupViewGroup 会执行 onInterceptTouchEvent() 方法,判断是否拦截该事件。如果返回 true,则事件会在 ViewGroup 中处理;如果返回 false,事件会继续传递到子视图。

  3. View:最终,事件将传递到具体的 View,在 View 中调用 onTouchEvent() 方法来处理事件。视图可以根据事件的类型(如点击、滑动等)执行相应的逻辑。

事件分发过程中的方法

  • dispatchTouchEvent()

    • 作用:负责分发触摸事件。

    • 调用时刻:当 ActivityViewGroup 收到触摸事件时首先调用。它决定事件是否继续传递给子视图或直接处理。

  • onInterceptTouchEvent()

    • 作用:用于判断 ViewGroup 是否拦截事件。

    • 调用时刻:在 ViewGroupdispatchTouchEvent() 内部调用。通常用于处理复杂的触摸交互,比如滑动或拖动。

  • onTouchEvent()

    • 作用:处理具体的触摸事件。

    • 调用时刻:在 dispatchTouchEvent() 内部调用。用于执行视图的响应逻辑,比如状态更新、动画触发等。

Activity事件分发机制

事件分发机制,首先会将点击事件传递到Activity中,具体是执行 dispatchTouchEvent() 进行事件分发。

/*** 源码分析:Activity.dispatchTouchEvent()*/ public boolean dispatchTouchEvent(MotionEvent ev) {// 仅贴出核心代码// ->>分析1if (getWindow().superDispatchTouchEvent(ev)) {return true;// 若getWindow().superDispatchTouchEvent(ev)的返回true// 则Activity.dispatchTouchEvent()就返回true,则方法结束。即 :该点击事件停止往下传递 & 事件传递过程结束// 否则:继续往下调用Activity.onTouchEvent}// ->>分析3return onTouchEvent(ev);}
/*** 分析1:getWindow().superDispatchTouchEvent(ev)* 说明:*     a. getWindow() = 获取Window类的对象*     b. Window类是抽象类,其唯一实现类 = PhoneWindow类*     c. Window类的superDispatchTouchEvent() = 1个抽象方法,由子类PhoneWindow类实现*/@Overridepublic boolean superDispatchTouchEvent(MotionEvent event) {return mDecor.superDispatchTouchEvent(event);// mDecor = 顶层View(DecorView)的实例对象// ->> 分析2}
/*** 分析2:mDecor.superDispatchTouchEvent(event)* 定义:属于顶层View(DecorView)* 说明:*     a. DecorView类是PhoneWindow类的一个内部类*     b. DecorView继承自FrameLayout,是所有界面的父类*     c. FrameLayout是ViewGroup的子类,故DecorView的间接父类 = ViewGroup*/public boolean superDispatchTouchEvent(MotionEvent event) {return super.dispatchTouchEvent(event);// 调用父类的方法 = ViewGroup的dispatchTouchEvent()// 即将事件传递到ViewGroup去处理,详细请看后续章节分析的ViewGroup的事件分发机制}// 回到最初的分析2入口处
/*** 分析3:Activity.onTouchEvent()* 调用场景:当一个点击事件未被Activity下任何一个View接收/处理时,就会调用该方法*/public boolean onTouchEvent(MotionEvent event) {// ->> 分析5if (mWindow.shouldCloseOnTouch(this, event)) {finish();return true;}return false;// 即 只有在点击事件在Window边界外才会返回true,一般情况都返回false,分析完毕}
/*** 分析4:mWindow.shouldCloseOnTouch(this, event)* 作用:主要是对于处理边界外点击事件的判断:是否是DOWN事件,event的坐标是否在边界内等*/public boolean shouldCloseOnTouch(Context context, MotionEvent event) {if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN&& isOutOfBounds(context, event) && peekDecorView() != null) {// 返回true:说明事件在边界外,即 消费事件return true;}// 返回false:在边界内,即未消费(默认)return false;}

这段源码分析详细讲解了 Activity.dispatchTouchEvent() 的实现和事件分发的过程。以下是对每个分析点的进一步阐释:

分析1:dispatchTouchEvent()

  • 核心逻辑:当触摸事件发生时,Activity.dispatchTouchEvent() 首先调用 getWindow().superDispatchTouchEvent(ev)。如果返回 true,表示该事件已经被处理,事件传递过程结束;如果返回 false,则继续调用 onTouchEvent(ev) 进行后续处理。

分析2:superDispatchTouchEvent()

  • 窗口与视图getWindow() 获取 Window 对象,通常是 PhoneWindow 的实例。superDispatchTouchEvent(ev) 将事件传递给 DecorView,它是应用界面的顶层视图,负责接收和分发事件。

  • 层级关系DecorView 继承自 FrameLayout,是所有界面的根视图,最终调用的是 ViewGroupdispatchTouchEvent() 方法,进入视图分发的下一阶段。

分析3:onTouchEvent()

  • 回调机制:当触摸事件没有被任何子视图处理时,Activity 将调用 onTouchEvent()。这通常用于处理未消费的事件,例如判断用户点击是否在窗口边界之外。

  • 返回值逻辑onTouchEvent() 的返回值主要依赖于 mWindow.shouldCloseOnTouch(this, event),该方法判断点击是否在窗口外。

分析4:shouldCloseOnTouch()

  • 边界判断:此方法的作用是判断用户点击是否在窗口的边界外,特别是在 ACTION_DOWN 事件时。如果点击发生在边界外且设置为关闭窗口,则返回 true,表示事件已被消费;否则,返回 false,表示事件未被消费。

ViewGroup事件分发机制

从上面Activity的事件分发机制可知,在 Activity.dispatchTouchEvent() 实现了将事件从Activity -> ViewGroup 的传递,ViewGroup的事件分发机制从 dispatchTouchEvent() 开始。

/*** 源码分析:ViewGroup.dispatchTouchEvent()*/ public boolean dispatchTouchEvent(MotionEvent ev) { // 仅贴出关键代码... if (disallowIntercept || !onInterceptTouchEvent(ev)) {  // 分析1:ViewGroup每次事件分发时,都需调用onInterceptTouchEvent()询问是否拦截事件// 判断值1-disallowIntercept:是否禁用事件拦截的功能(默认是false),可通过调用requestDisallowInterceptTouchEvent()修改// 判断值2-!onInterceptTouchEvent(ev) :对onInterceptTouchEvent()返回值取反// a. 若在onInterceptTouchEvent()中返回false,即不拦截事件,从而进入到条件判断的内部// b. 若在onInterceptTouchEvent()中返回true,即拦截事件,从而跳出了该条件判断// c. 关于onInterceptTouchEvent() ->>分析1// 分析2// 1. 通过for循环,遍历当前ViewGroup下的所有子Viewfor (int i = count - 1; i >= 0; i--) {  final View child = children[i];  if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE  || child.getAnimation() != null) {  child.getHitRect(frame);  // 2. 判断当前遍历的View是不是正在点击的View,从而找到当前被点击的Viewif (frame.contains(scrolledXInt, scrolledYInt)) {  final float xc = scrolledXFloat - child.mLeft;  final float yc = scrolledYFloat - child.mTop;  ev.setLocation(xc, yc);  child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;  // 3. 条件判断的内部调用了该View的dispatchTouchEvent()// 即 实现了点击事件从ViewGroup到子View的传递(具体请看下面章节介绍的View事件分发机制)if (child.dispatchTouchEvent(ev))  { // 调用子View的dispatchTouchEvent后是有返回值的// 若该控件可点击,那么点击时dispatchTouchEvent的返回值必定是true,因此会导致条件判断成立// 于是给ViewGroup的dispatchTouchEvent()直接返回了true,即直接跳出// 即该子View把ViewGroup的点击事件消费掉了mMotionTarget = child;  return true; }  }  }  }  }  }  ...return super.dispatchTouchEvent(ev);// 若无任何View接收事件(如点击空白处)/ViewGroup本身拦截了事件(复写了onInterceptTouchEvent()返回true)// 会调用ViewGroup父类的dispatchTouchEvent(),即View.dispatchTouchEvent()// 因此会执行ViewGroup的onTouch() -> onTouchEvent() -> performClick() -> onClick(),即自己处理该事件,事件不会往下传递// 具体请参考View事件分发机制中的View.dispatchTouchEvent()... 
}
/*** 分析1:ViewGroup.onInterceptTouchEvent()* 作用:是否拦截事件* 说明:*     a. 返回false:不拦截(默认)*     b. 返回true:拦截,即事件停止往下传递(需手动复写onInterceptTouchEvent()其返回true)*/public boolean onInterceptTouchEvent(MotionEvent ev) {  // 默认不拦截return false;} // 回到调用原处

这段源码分析清楚地描述了 ViewGroup.dispatchTouchEvent() 方法的实现过程,以及如何判断和处理触摸事件。以下是对每个分析点的进一步解释:

分析1:dispatchTouchEvent()

  • 事件拦截判断:在每次事件分发时,ViewGroup 会首先调用 onInterceptTouchEvent(ev) 来询问是否拦截事件。这个决策是基于两个条件:

    • disallowIntercept:如果为 true,表示不允许拦截。

    • !onInterceptTouchEvent(ev):如果返回 false,表示不拦截事件,继续执行事件传递逻辑。

事件传递过程

  • 遍历子视图:如果未拦截,ViewGroup 将通过 for 循环遍历其所有子视图。

  • 可见性检查:在循环中,首先检查子视图的可见性。只有可见或有动画效果的视图会继续处理。

  • 命中检测:使用 getHitRect(frame) 获取子视图的矩形区域,判断用户点击的坐标是否在子视图内。如果点击在子视图内,将事件位置调整为相对于子视图的坐标,并调用该子视图的 dispatchTouchEvent(ev) 方法处理事件。

分析2:子视图的事件处理

  • 返回值逻辑:如果子视图处理了事件(返回 true),ViewGroup.dispatchTouchEvent() 也会返回 true,表示事件被消费,事件不会继续向下传递。

分析3:无视图处理的情况

  • 调用父类方法:如果没有任何子视图处理事件,或者 ViewGroup 自身拦截了事件,将调用其父类 ViewdispatchTouchEvent() 方法。这可能导致 View 自身处理事件,执行点击逻辑(如调用 performClick())。

onInterceptTouchEvent()

  • 默认行为onInterceptTouchEvent() 默认返回 false,即不拦截事件。开发者可以重写此方法以自定义拦截逻辑,返回 true 时会停止事件的进一步传递。

Android事件分发传递到Acitivity后,总是先传递到 ViewGroup 、再传递到 View 。流程总结如下:(假设已经经过了Acitivity事件分发传递并传递到 ViewGroup )

View事件分发机制

从上面 ViewGroup 事件分发机制知道,View事件分发机制从 dispatchTouchEvent() 开始

/*** 源码分析:View.dispatchTouchEvent()*/public boolean dispatchTouchEvent(MotionEvent event) {  if ( (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener != null &&  mOnTouchListener.onTouch(this, event)) {  return true;  } return onTouchEvent(event);  }// 说明:只有以下3个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent()//   1. (mViewFlags & ENABLED_MASK) == ENABLED//   2. mOnTouchListener != null//   3. mOnTouchListener.onTouch(this, event)// 下面对这3个条件逐个分析
/*** 条件1:(mViewFlags & ENABLED_MASK) == ENABLED* 说明:*    1. 该条件是判断当前点击的控件是否enable*    2. 由于很多View默认enable,故该条件恒定为true(除非手动设置为false)*/
/*** 条件2:mOnTouchListener != null* 说明:*   1. mOnTouchListener变量在View.setOnTouchListener()里赋值*   2. 即只要给控件注册了Touch事件,mOnTouchListener就一定被赋值(即不为空)*/public void setOnTouchListener(OnTouchListener l) { mOnTouchListener = l;  
} 
/*** 条件3:mOnTouchListener.onTouch(this, event)* 说明:*   1. 即回调控件注册Touch事件时的onTouch();*   2. 需手动复写设置,具体如下(以按钮Button为例)*/button.setOnTouchListener(new OnTouchListener() {  @Override  public boolean onTouch(View v, MotionEvent event) {  return false;  // 若在onTouch()返回true,就会让上述三个条件全部成立,从而使得View.dispatchTouchEvent()直接返回true,事件分发结束// 若在onTouch()返回false,就会使得上述三个条件不全部成立,从而使得View.dispatchTouchEvent()中跳出If,执行onTouchEvent(event)// onTouchEvent()源码分析 -> 分析1}  });
/*** 分析1:onTouchEvent()*/public boolean onTouchEvent(MotionEvent event) {  ... // 仅展示关键代码// 若该控件可点击,则进入switch判断中if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  // 根据当前事件类型进行判断处理switch (event.getAction()) { // a. 事件类型=抬起View(主要分析)case MotionEvent.ACTION_UP:  performClick(); // ->>分析2break;  // b. 事件类型=按下Viewcase MotionEvent.ACTION_DOWN:  postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());  break;  // c. 事件类型=结束事件case MotionEvent.ACTION_CANCEL:  refreshDrawableState();  removeTapCallback();  break;// d. 事件类型=滑动Viewcase MotionEvent.ACTION_MOVE:  final int x = (int) event.getX();  final int y = (int) event.getY();  int slop = mTouchSlop;  if ((x < 0 - slop) || (x >= getWidth() + slop) ||  (y < 0 - slop) || (y >= getHeight() + slop)) {  removeTapCallback();  if ((mPrivateFlags & PRESSED) != 0) {  removeLongPressCallback();  mPrivateFlags &= ~PRESSED;  refreshDrawableState();  }  }  break;  }  // 若该控件可点击,就一定返回truereturn true;  }  // 若该控件不可点击,就一定返回falsereturn false;  
}
/*** 分析2:performClick()*/  public boolean performClick() {  if (mOnClickListener != null) {// 只要通过setOnClickListener()为控件View注册1个点击事件// 那么就会给mOnClickListener变量赋值(即不为空)// 则会往下回调onClick() & performClick()返回trueplaySoundEffect(SoundEffectConstants.CLICK);  mOnClickListener.onClick(this);  return true;  }  return false;  }

这段源码分析清晰地描述了 View.dispatchTouchEvent()View.onTouchEvent() 方法的工作原理,以及它们如何处理触摸事件。以下是对每个关键点的进一步解析:

dispatchTouchEvent()

  • 返回条件dispatchTouchEvent() 会在三个条件同时满足时返回 true,表示事件已被消费:

    • 条件1(mViewFlags & ENABLED_MASK) == ENABLED,判断视图是否启用。一般情况下,这个条件为真,除非手动设置为禁用。

    • 条件2mOnTouchListener != null,确保视图注册了触摸事件监听器。

    • 条件3mOnTouchListener.onTouch(this, event),调用监听器的 onTouch() 方法并检查返回值。如果返回 true,事件被消费,方法结束;如果返回 false,将调用 onTouchEvent(event) 继续处理。

onTouchEvent()

  • 事件处理逻辑onTouchEvent() 负责处理实际的触摸事件,首先检查视图的可点击性。如果视图可点击,会进入 switch 语句根据事件类型进行处理:

    • ACTION_UP:抬起手指时调用 performClick(),触发点击事件。

    • ACTION_DOWN:按下时可能设置延迟检查点击状态。

    • ACTION_CANCEL:处理取消事件,重置状态。

    • ACTION_MOVE:处理移动事件,检查是否超过触摸阈值。

performClick()

  • 点击事件触发performClick() 方法会检查是否注册了点击事件监听器 mOnClickListener。如果存在,调用 onClick() 方法执行点击逻辑,并返回 true 表示点击事件已成功处理。

点击按钮会产生两个类型的事件-按下View与抬起View,所以会回调两次 onTouch() ;

因为 onTouch() 返回了false,所以事件无被消费,会继续往下传递,即调用 View.onTouchEvent() ;

调用 View.onTouchEvent() 时,对于抬起View事件,在调用 performClick() 时,因为设置了点击事件,所以会回调 onClick() 。

点击按钮会产生两个类型的事件-按下View与抬起View,所以会回调两次 onTouch() ;

因为 onTouch() 返回true,所以事件被消费,不会继续往下传递, View.dispatchTouchEvent() 直接返回true;

所以最终不会调用 View.onTouchEvent() ,也不会调用 onClick() 。

事件分发机制流程总结

从上表可以看到 Activity和 View都是没有事件拦截的,这是因为:

Activity 作为原始的事件分发者,如果 Activity 拦截了事件会导致整个屏幕都无法响应事件,这肯定不是我们想要的效果。

View作为事件传递的最末端,要么消费掉事件,要么不处理进行回传,根本没必要进行事件拦截。

安卓为了保证所有的事件都是被一个 View 消费的,对第一次的事件( ACTION_DOWN )进行了特殊判断,View 只有消费了 ACTION_DOWN 事件,才能接收到后续的事件(可点击控件会默认消费所有事件),并且会将后续所有事件传递过来,不会再传递给其他 View,除非上层 View 进行了拦截。如果上层 View 拦截了当前正在处理的事件,会收到一个 ACTION_CANCEL,表示当前事件已经结束,后续事件不会再传递过来。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【linux】线程 (三)

13. 常见锁概念 &#xff08;一&#xff09;了解死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源&#xff0c;但因互相申请被其他进程占有的&#xff0c;且不释放的资源&#xff0c;而处于的一种永久等待状态 &#xff08;二&#xff09;死锁四个必要条件 互斥条件…

uniapp项目结构基本了解

基本结构的解释 App.vue&#xff1a;应用的根组件&#xff0c;定义全局布局和逻辑。pages/&#xff1a;存放各个页面的 .vue 文件&#xff0c;定义应用的具体页面和功能模块。main.js&#xff1a;应用入口文件&#xff0c;初始化应用&#xff0c;挂载 App.vue。manifest.json&…

【C++】— 一篇文章让你认识STL

文章目录 &#x1f335;1.什么是STL&#xff1f;&#x1f335;2.STL的版本&#x1f335;3.STL的六大组件&#x1f335;4.STL的重要性&#x1f335;5. 如何学习STL&#x1f335;6. 学习STL的三种境界 &#x1f335;1.什么是STL&#xff1f; STL是Standard Template Library的简称…

『完整代码』靠近显示对话图标

在NPC预制体中增加Canvas 并设置 创建Image 并设置 隐藏Image 在场景中创建Canvas 重命名为CurrentCanvas 创建空物体设置底端锚点 重命名为DownPin 创建Image重命名为TalkUI 选择图片设置 创建Image并设置 重命名为imgNpc 创建文本并设置 重命名为txtNpc 可以给图片与文本加一…

centos 安装达梦数据库

一、环境准备 1.1、确认操作系统的版本和数据库的版本是否一致 ## 查看系统版本&#xff1a;cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core)1.2、关闭防火墙和Selinux # 查看selinux是不是disabled / enforce cat /etc/selinux/config## 查看防火墙状态 fir…

windows mysql 8.0版本重置root密码

1.停止mysql服务 以管理员运行cmd 2.安全模式启动 mysqld --console --skip-grant-tables --shared-memory 3.修改密码 再开个cmd窗口就可以进入了&#xff1a;mysql 先进入mysql database&#xff1a;use mysql 修改密码&#xff1a;ALTER USER rootlocalhost IDENTIFIED …

使用 InfiniBand 写入带宽对 NVIDIA GPUDirect RDMA 进行基准测试

简介 性能基准测试是 HPC 的标志。最现代的超级计算机是具有异构架构的计算节点集群。在这样的节点中&#xff0c;我们可以看到经典 CPU 和专用计算协处理器 (GPU)。本教程介绍了使用基于 InfiniBand 写入带宽 (ib_write_bw) 构建的定制脚本对 NVIDIA GPUDirect 远程直接内存访…

Xmind一款极简思维导图和头脑风暴软件,支持PC和移动端,Xmind 2024.10.01101版本如何升级到Pro版?简单操作,最新可用!

文章目录 Xmind下载安装Xmind免费升级到Pro Xmind 是一款全功能的思维导图和头脑风暴软件&#xff0c;不限制节点和文件数&#xff0c;创新无限&#xff0c;界面纯净简洁无广告&#xff0c;支持PC和移动端&#xff0c;思维导图和大纲视图自由切换&#xff0c;可本地化文档存储&…

AutoFixture:.NET 的假数据生成工具

上次推荐过《Bogus&#xff1a;.NET的假数据生成利器》方便我们制造假数据测试。今天继续推荐另外一个也是非常流行的工具。 01 项目简介 AutoFixture 是一个用于 .NET 的测试工具&#xff0c;它允许开发者在单元测试中自动生成随机的测试数据。它支持广泛的数据类型&#xf…

如何使用DockerSpy检测你的Docker镜像是否安全

关于DockerSpy DockerSpy是一款针对Docker镜像的敏感信息检测与安全审计工具&#xff0c;该工具可以帮助广大研究人员在Docker Hub上检测和搜索自己镜像的安全问题&#xff0c;并识别潜在的泄漏内容&#xff0c;例如身份验证密钥等敏感信息。 功能介绍 1、安全审计&#xff1a…

React源码03 - React 中的更新

03 - React 中的更新 React 中创建更新的方式&#xff1a; 初次渲染&#xff1a;ReactDOM.render、ReactDOM.hydrate 后续更新&#xff1a;setState、forceUpdate 1. ReactDOM.render() 先创建 ReactRoot 顶点对象然后创建 FiberRoot 和 RootFiber创建更新&#xff0c;使应用进…

ArcGIS应用指南:多尺度渔网创建

在GIS中&#xff0c;创建渔网矢量文件是GIS中的一项常见任务&#xff0c;通过将研究区域划分为规则的网格&#xff0c;可以更精细地分析和管理城市空间数据。本文以厦门市行政区为例&#xff0c;详细介绍了如何创建不同尺度的渔网矢量网格&#xff0c;以适应不同区域的发展特点…

DCS项目调试踩坑记录

最近在调试一个DCS项目&#xff08;集散控制系统&#xff09;&#xff0c;实际上就是一个新建厂区的控制系统。PLC用的是西门子1500&#xff0c;控制画面使用组态王7.5。 在调试过程中&#xff0c;发现给西门子DB块的变量转移到组态王太难了&#xff0c;因此记录一下&#xff0…

RHCE【远程连接服务器】

目录 一、远程连接服务器简介 二、加密技术简介 SSH工作过程&#xff1a; &#xff08;1&#xff09;版本协商阶段 &#xff08;2&#xff09;密钥和算法协商阶段 &#xff08;3&#xff09;认证阶段 &#xff08;4&#xff09;会话请求阶段 &#xff08;5&#xff0…

互联网人口红利趋缓下的社群粉丝经济新模式探索

摘要&#xff1a;随着互联网人口红利消失近十年&#xff0c;国内互联网人口红利爆发时期凭借大量用户取得成功的模式不再适用。如今互联网人口增长进入平缓期&#xff0c;社群粉丝经济成为新方向。其能借助人群画像精准推送营销信息&#xff0c;降低成本。如“21 链动模式 AI 智…

android openGL ES详解——混合

一、混合概念 混合是一种常用的技巧&#xff0c;通常可以用来实现半透明。但其实它也是十分灵活的&#xff0c;你可以通过不同的设置得到不同的混合结果&#xff0c;产生一些有趣或者奇怪的图象。混合是什么呢&#xff1f;混合就是把两种颜色混在一起。具体一点&#xff0c;就…

【前端】如何制作一个自己的网页(16)

上次&#xff0c;我们学习了两种复合CSS选择器&#xff0c;以及两种内容分组的方式&#xff1a;整体布局的div元素和局部布局的span元素。 学习目标 学习另一种对内容分组的方式&#xff1a;列表结构。首先&#xff0c;我们会简单了解下什么是HTML的列表结构。然后&#xff0…

《深度学习》YOLO v1网络架构 、损失值、NMS极大值抑制

目录 一、Yolo系列v1 1、核心思想 2、示例 3、流程图解析 二、YOLO系列v1损失函数 1、位置误差 2、置信度误差 3、类别概率损失 三、NMS非极大值抑制 1、概念 2、步骤 四、YOLO v1优缺点 1、优点 1&#xff09;速度快 2&#xff09;端到端 3&#xff09;多尺度…

Python 学习笔记(十二)—— 网络编程

目录 一、网络编程的基本概念 1.1 IP地址 1.1.1 IP的版本 1.1.2 IP的分类 1.1.2.1 公有地址 1.1.2.2 私有地址 1.1.3 IP地址的范围 1.1.4 回环测试 1.2 常见的网络设备 1.3 端口 1.3.1 端口分配 二、网络通信协议 2.1 常用网络协议 2.2 OSI网络协议七层模型 2.3…

Java访问修饰符private,default,protected,public

在Java中&#xff0c;访问修饰符决定了类、方法、变量和构造器的可见性和可访问范围。这里有四个主要的访问修饰符&#xff1a;private、default&#xff08;未显式指定&#xff09;、protected 和 public。下面分别解释它们的作用&#xff1a; 1.private 作用&#xff1a;使…