当前位置: 首页 > news >正文

seo网站分析/百度seo培训课程

seo网站分析,百度seo培训课程,手机优化网站建设,欣宝儿在什么网站做直播绘制原理 Android 程序员都知道 Android 的绘制流程分为 Measure、Layout、Draw 三步骤,其中 Measure 负责测量 View 的大小Layout 负责确定 View 的位置Draw 负责将 View 画在屏幕上 由 ViewRootImpl 实现的 performTraversal 方法是 Measure、layout、draw 的真正…

绘制原理

        Android 程序员都知道 Android 的绘制流程分为 Measure、Layout、Draw 三步骤,其中

  • Measure 负责测量 View 的大小
  • Layout 负责确定 View 的位置
  • Draw 负责将 View 画在屏幕上

        由 ViewRootImpl 实现的 performTraversal 方法是 Measure、layout、draw 的真正执行者,本文将不会关注 measure、layout、draw 的执行细节。而是展示 measure、layout、draw 和 Traversal 之间的关系,沉浸在细节里面,反而对全貌会难以知晓,这一系列将会循循渐进,先介绍全貌,再由具体问题分析具体的细节。从 View Root 视角展示 Traversal 流程是如何统筹、控制和执行三大流程的。

        本文将会带着两个问题去学习

  1. 刷新率为 60fps 的手机,每秒钟会执行 60 次 traversal 吗?
  2. ViewRootImpl 在一段时间内执行 60 次 Traversal,Measure / Layout / Draw 都会执行60次吗?

背景知识

Measure 与 onMeasure

public class View {/*** 此方法用于确定 view 的大小,由父 view 提供宽度、高度的限制条件* 真正的测量工作是由此方法调用的 onMeasure(int, int) 执行的* 因此子类只可以覆盖 onMeasure(int, int) 方法*/public final void measure(int widthMeasureSpec, int heightMeasureSpec) {onMeasure(widthMeasureSpec, heightMeasureSpec);}/*** 覆盖此方法以实现对自身内容的提供准确、高效地测量*/protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
}public class CustomViewGroup extends ViewGroup {protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {measureSelf();children.forEach(child -> {child.measure(childWidthMeasureSpec, childHeightMeasureSpec);});}
}

        layout 和 onLayout 关系和 measure 和 onMeasure 一致;draw 和 onDraw 的关系也大体上可以认为是一致的。

ViewRootImpl

        Android中 view 是按照树形结构组织的,根节点一般是 DecorView,树中的每一个非叶子节点都是一个 ViewGroup,ViewGroup 负责组织、管理子 view,而子 View 持有的 parent 引用却是被声明为 ViewParent 类型,这暗示了 View 的父容器并不一定是 View/ViewGroup,更准确的说:树中的每一个非叶子节点都是一个 ViewParent。

public class View {/*** Gets the parent of this view. Note that the parent is a* ViewParent and not necessarily a View.*/public final ViewParent getParent() {return mParent;}//  Finds the topmost view in the current view hierarchy.public View getRootView() {View parent = this;while (parent.mParent != null && parent.mParent instanceof View) {parent = (View) parent.mParent;}return parent;}// Gets the view root associated with the View.public ViewRootImpl getViewRootImpl() {if (mAttachInfo != null) {return mAttachInfo.mViewRootImpl;}return null;}
}

        ViewParent 定义 View 容器的职责,其中定义了子 view 想要与父 view 交互的时候所能看到的 API。ViewRootImpl 是其存在的重要理由。ViewRootImp 并没有继承 View、ViewGroup,但是实现了 ViewParent 接口,是 View Tree 根节点的 parent 节点,因此可以认为

View Tree的物理根节点是 DecorView,逻辑根节点是 ViewRootImpl

        究其原因,DecorView 作为物理上的 View Root,但其本质上仍然只是一个 View,并没有管理整颗 View 树的能力。查看其代码也可以发现,其能力只有针对自己这个 view 的绘制、事件分发等等,因此才会有 ViewRootImpl作为 DecorView 逻辑上、抽象 View Parent,链接 View 和 Window,负责事件分发、视图更新等流程。本文的主题,就是 ViewRootImpl 负责实现的。

Choreographer

协调动画、输入和绘图的时间

        Chereographer 是 Android APP 对于 V_SYNC信号(可以简单认为 V_SYNC信号是在开启 V_SYNC的显示设备的缓冲区就绪的信号)的接受者,当收到 V_SYNC信号以后,意味着缓冲区已经就绪,可以触发和下一帧画面相关的任务,包括输入响应、动画、traversal、自定义任务。V_SYNC一般默认和显示设备的刷新率同步,对于主流的 Android 手机,fps 通常是 60、90或者 120.

Traversal

概览

        Traversal 在 Android32 中是一个超过 800行的复杂函数,这里只展示一下精简版,先从宏观视角对其内容有一个粗略的了解。

public final class ViewRootImpl implements ViewParent {private void performTraversals() {boolean layoutRequested = mLayoutRequested && !mStopped;if (layoutRequested) {performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);}if (layoutRequested) {performLayout(lp, mWidth, mHeight);}  performDraw();}
}

        排除掉 window、surface、focus 等非本文关注内容,Traversal 就是按顺序决定是否需要执行 measure、layout、draw 方法。

起源

        绘制流程是 Traversal 执行的,那 Traversal 的又是谁执行的呢?

        最普遍的情况下,View 的 onMeasure 堆栈结构如下图所谓:

        如上文提到的,编舞者在收到 V_SYNC信号以后,开始通过 handler 通知 ViewRootImpl 执行绘制流程以准备下一帧的任务,从 DecorView 出发,最终调用自定义 View 的 onMeasure 方法。从堆栈来看,V_SYNC似乎是 Traversal 的起点,但是展开Choreographer的doFrame的函数,发现doFrame只是执行Choreographer.CALLBACK_TRAVERSAL类型的回调任务,而Traversal的callback正是ViewRootImpl自己注册的。

public final class Choreographer {private final CallbackQueue[] mCallbackQueues;void doFrame(long frameTimeNanos, int frame) {...doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);...}void doCallbacks(int callbackType, long frameTimeNanos) {callbacks = mCallbackQueues[callbackType]callbacks.foreach { it.run() }}
}

        通过进一步追溯Choreographer.CALLBACK_TRAVERSAL类型的callback注册,只有ViewRootImpl的某些方法才会注册Choreographer.CALLBACK_TREAVERSAL类型的callback,用于在下一次V_SYNC信号到来的时候执行Traversal流程。所以Traversal的源头并不是V_SYNC,而是ViewRootImpl的某些操作,V_SYNC本身只是Traversal异步任务的一个触发器。

public final class ViewRootImpl implements ViewParent {void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);}}
}

        ViewRootImpl 中会触发 scheduleTraversals 的方法一共有 18 个(sdk 30) :

  • requestLayout
  • invalidate
  • requestFitSystemWindows
  • requestChildFocus
  • notifyInsetsChanged
  • invalidateRectOnScreen
  • handleDispatchSystemUiVisibilityChanged
  • ......

        从会触发的 scheduleTraversals 方法名可以看出,只有当 UI 界面发生变化时,才会在下一帧安排 traversal。所以:Traversal 相对于 V_SYNC 信号而言,是按需执行的,即执行 traversal 的次数 <= 屏幕刷新的次数。至此我们回答了第一个问题:刷新率60fps的手机,每秒并不会执行60次traversal。

requestLayout

        以最常用的requestLayout方法为例子,追溯一下Traversal的真正起源:

public final class ViewRootImpl implements ViewParent {@Overridepublic void requestLayout() {checkThread();mLayoutRequested = true;scheduleTraversals();}
}public class View {public void requestLayout() {mPrivateFlags |= PFLAG_FORCE_LAYOUT;// 避免重复调用if (mParent != null && (mparent.mPrivateFlags & PFLAG_FORCE_LAYOUT) != PFLAG_FORCE_LAYOUT) {mParent.requestLayout();}}public boolean isLayoutRequested() {return (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;}
}

requestLayout是ViewParent接口中定义的方法,同时View中也定义了同名方法,当View Tree中的任何一个View需要请求重新布局的时候,调用自身requestLayout,该方法向上回溯执行,知道最顶层的ViewParent。这会导致:

  1. 对于链路上所有的View,会被打上PFLAG_FORCE_LAYOUT(我们可以将该标记理解为:我需要measure)标记;
  2. 对于最终的ViewParent节点,既最顶层View的父结点→ViewRootImpl,则会设置自身的mLayoutRequested标志,同时开始注册下一帧的Traversal callback

Measure

        许多App的UI都是长时间不变化,比如阅读、工具类APP,如此设计肯定可以节省大量的资源。而且UI发生变化时,往往都是局部变化,比如一个按钮的点赞动画、播放器画面的更新,既然Traversal已经实现了按需执行,measure、layout、draw也应该需要按需执行。

执行条件

        Measure是Traversal的第一个环节

public final class ViewRootImpl implements ViewParent {// DeocrViewView mView;private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);}private void performTraversals() {boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);if (layoutRequested || windowInsetsChanged) {performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);}...}
}

        对于ViewRootImpl,其可以通过requestLayout中设置的mLayoutRequested标志位决定是否调用performMeasure,进而控制Root View是否执行measure方法。注意上述代码,执行performMeasure的条件是

  1. mLayoutRequested == true
  2. 应用不处在stop状态或者mReportNextDraw = true

        那么类似的,View同样可以借助requestLayout中设置的PFLAG_FORCE_LAYOUT标志位,决定自身是否执行onMeasure流程,那来过滤没必要的测量操作。View的measure方法如下:

public class View {public final void measure(int widthMeasureSpec, int heightMeasureSpec) {// 是否执行了 requestLayoutfinal boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;// measureSpec 是否变化final boolean specChange = widthMeasureSpec != mOldWidthMeasureSpec|| heightMeasureSpec != mOldHeightMeasureSpec;final boolean isSpecExactly = specMode == EXACTLY;final boolean matchesSpecSize = specSize = currentSize;final boolean needsLayout = specChanged && (!isSpecExactly || !matchesSpecSize);if (forceLayout || needsLayout) {onMeasure(widthMeasureSpec, heightMeasureSpec);mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;}  }
}

        如下面的代码和注释展示的。仅当满足下面条件之一时,才会执行onMeasure函数进行真正的测量:

  1. measureSpec变化且layoutWidth、layoutHeight不是准确数值或者准确数值与自身大小不一致
  2. PFLAG_FORCE_LAYOUT标记存在时候。

        因此Traversal流程中,许多不满足以上条件的View是可以跳过自身的onMeasure以及后代的measure函数,这对于复杂的视图结构是很有意义的。

        当onMeasure执行完成以后,并没有清空PFLAG_FORCE_LAYOUT标志,而且追加设置了一个新的标志→ PFLAG_LAYOUT_REQUIRED(我们可以将该标志位理解为:我需要layout)

  1. 从PFLAG_LAYOUT_REQUIRED名字可以看出,这意味着执行过onMeasure的函数理论上一定需要执行layout流程
  2. requestLayout设置的PFLAG_FORCE_LAYOUT是强制layout的含义,但是onMeasure的执行流程也依赖该标志位
  3. ViewRootImpl的Traversal流程中关于是否执行performMeasure和performLayout的判断条件也都是mLayoutRequested标志位

        从以上三点不难看出,layout流程是绑定measure的,重新测量以后一定需要重新布局。

MeasureCache

        此外,View引入了MeasureCache,用于保存历史widthMeasureSpec/heightMeasureSpce对应的测量结果,如果命中了缓存,就可以执行设置结果,跳过onMeasure的测量工作(但没有完全的跳过),不过,又设置了一个新的标志位PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT

public class View {private LongSparseLongArray mMeasureCache;public final void measure(int widthMeasureSpec, int heightMeasureSpec) {long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);if (forceLayout || needsLayout) {int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);if (cacheIndex < 0) {onMeasure(widthMeasureSpec, heightMeasureSpec);mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;} else {long value = mMeasureCache.valueAt(cacheIndex);setMeasuredDimensionRaw((int) (value >> 32), (int) value);mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;}mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;}mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension}
}

Layout

执行条件

        和performMeasure相同,在ViewRootImpl的performTraversal中,mLayoutRequested标志也是layout流程执行的必要条件

public final class ViewRootImpl implements ViewParent {// DeocrViewView mView;private void performTraversals() {...if (mLayoutRequested) {performLayout(lp, mWidth, mHeight);}...}private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {...mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());...}
}

        和 measure 类似,onLayout 也只会满足一定条件时,才会执行,用于实现按需执行:

  • View 的坐标发生变化(类比 onMeasure 的 needsLayout)
  • PFLAG_LAYOUT_REQUIRED ****标记存在时(类比 onMeasure 的 forceLayout)

        以上两个条件满足任一即可。

public class View {public void layout(int l, int t, int r, int b) {int oldL = mLeft;int oldT = mTop;int oldB = mBottom;int oldR = mRight;boolean changed = setFrame(l, t, r, b);if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {onLayout(changed, l, t, r, b);mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;}mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;}protected boolean setFrame(int left, int top, int right, int bottom) {...return mLeft != left || mRight != right || mTop != top || mBottom != bottom;}
}

        在 onMeasure 中设置的标志位 PFLAG_LAYOUT_REQUIRED 在 onLayout 执行后被清除,同时注意,先前通过 requestLayout 设置的标志:PFLAG_FORCE_LAYOUT 也是在此处(layout)清除的。

Draw

        draw相对于measure、layout,无论是从流程控制上还是具体实现上,都要复杂很多,这里只是简单介绍一下执行流程。因为现在硬件加速渲染已经成为绝对主流,接下来的内容以硬件渲染为例子

        简单介绍一下软件绘制和硬件绘制的区别:

  • 软件绘制:View通过操作Canvas,调用CPU在Buffer上绘制自身内容
  • 硬件绘制:View通过操作RecordingsCanvas,将绘制自身内容指令(DrawXXX)缓存起来,由GPU负责渲染

        通过控制调用 View.draw(Canvas) 传入的 Canvas 类型,实现了对开发者透明的软件绘制和硬件绘制的切换

class RecordingCanvas final : public SkCanvasVirtualEnforcer<SkNoDrawCanvas> {void onDrawRRect(const SkRRect&, const SkPaint&) override;DisplayListData* fDL;
}void RecordingCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {fDL->drawRRect(rrect, paint);
}void DisplayListData::drawRRect(const SkRRect& rrect, const SkPaint& paint) {this->push<DrawRRect>(0, rrect, paint);
}struct DrawRRect final : Op {static const auto kType = Type::DrawRRect;DrawRRect(const SkRRect& rrect, const SkPaint& paint) : rrect(rrect), paint(paint) {}SkRRect rrect;SkPaint paint;void draw(SkCanvas* c, const SkMatrix&) const { c->drawRRect(rrect, paint); }
};

        接下来看一下 ViewRootImpl 是如何发起 Draw 流程的。

public class ViewRootImpl {private void performTraversals() {boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw();if (!cancelDraw) {performDraw();}}private void performDraw() {mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);}
}public final class ThreadedRenderer {void draw(View view, AttachInfo attachInfo, DrawCallbacks callbacks) {updateRootDisplayList(view, callbacks);}private void updateRootDisplayList(View view, DrawCallbacks callbacks) {// RecordingCanvas 是实现 硬件渲染 的关键RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight);try {final int saveCount = canvas.save();canvas.drawRenderNode(view.updateDisplayListIfDirty());canvas.restoreToCount(saveCount);} finally {mRootNode.endRecording();}}
}

        Draw 和 measure、layout 不同:

  1. ViewRootImpl Traversal 一定执行会 performDraw,并没有使用 mLayoutRequested 标志。(dispatchOnPreDraw 是 ViewRootImpl 暴露的一个节点,可以用于业务方跳过绘制,Android 的 Transform 动画就依赖此接口实现)。
  2. 没有直接调用 DecorView 的 View.draw 方法开始,而是通过 ThreadedRenderer 开始,对 View Tree 上所有 View 执行 updateDisplayListIfDirty,DisplayList 的遍历方式比 xxx 和 onXxx 的结构要复杂一些,不过原理是相同的:利用标记实现 DisplayList 的按需更新。

        阅读 View 关于 draw 的实现,在 updateDisplayListIfDirty 函数中,PFLAG_DRAWING_CACHE_VALID 用于判断 View 是否需要参与本次 draw 流程,而 View 内部的 mRecreateDisplayList 则决定了 View 自己的内容是否需要更新,mRecreateDisplayList 标志位是 draw(canvas, this, drawingTime) 方法依据 PFLAG_INVALIDATED 确定的。

public class View {boolean mRecreateDisplayList = false;public RenderNode updateDisplayListIfDirty() {final RenderNode renderNode = mRenderNode;if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 && !mRecreateDisplayList) {mPrivateFlags |= PFLAG_DRAWING_CACHE_VALID;if (mRecreateDisplayList) {draw(canvas);} else {dispatchGetDisplayList();}}return renderNode;}public void draw(Canvas canvas) {...onDraw(canvas);dispatchDraw(canvas);...}boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;mPrivateFlags &= ~PFLAG_INVALIDATED;renderNode = updateDisplayListIfDirty();((RecordingCanvas) canvas).drawRenderNode(renderNode);    mRecreateDisplayList = false;}
}public class ViewGroup {protected void dispatchDraw(Canvas canvas) {children.forEach(child -> {child.draw(canvas, this, drawingTime);});}protected void dispatchGetDisplayList() {children.forEach(child -> {child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;child.mPrivateFlags &= ~PFLAG_INVALIDATED;child.updateDisplayListIfDirty();child.mRecreateDisplayList = false;});}
}

        PFLAG_DRAWING_CACHE_VALID 和 PFLAG_INVALIDATED 这两个标志的生命周期和我们熟悉的 View.invalidate 相关,和 requestLayout 类似,invalidate 也借助于 ViewParent 接口,层层向上通知,直到 ViewRootImpl,ViewRootImpl 会在收到 invalidate 请求后开始安排下一次 Traversal。

public class View {void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {mPrivateFlags |= PFLAG_INVALIDATED;mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;mParent.invalidateChild(this);}
}public class  ViewGroup {public final void invalidateChild(View child, final Rect dirty) {onDescendantInvalidated(child, child);}public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;if (mParent != null) {mParent.onDescendantInvalidated(this, target);}}
}public class ViewRootImpl {public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {// TODO: Re-enable after camera is fixed or consider targetSdk checking this// checkThread();invalidate();}void invalidate() {// mWillDrawSoon 在 ViewRootImpl 的 measure / layout 阶段会设置为 true,// 此时 draw 流程还没开始,invalidate 请求可以在即将到来的 draw 流程中被消费if (!mWillDrawSoon) {scheduleTraversals();}}
}

        View 和 ViewRootImpl 的 invalidate 并没有关联 requestLayout,即Traversal 中的 draw 不需要以 measure / layout 作为其预备条件,可以单独执行,不像 measure 和 layout 总是彼此相互绑定,所以:

        invalidate 可以满足诉求时,不要使用 reqeustLayout

        虽然 Draw 不必需 measure、layout,但是 measure、layout 后总是需要执行 draw,这是因为完整的 requestLayout 代码如下:

public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {@CallSuperpublic void requestLayout() {if (mMeasureCache != null) mMeasureCache.clear();if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {// Only trigger request-during-layout logic if this is the view requesting it,// not the views in its parent hierarchyViewRootImpl viewRoot = getViewRootImpl();if (viewRoot != null && viewRoot.isInLayout()) {if (!viewRoot.requestLayoutDuringLayout(this)) {return;}}mAttachInfo.mViewRequestingLayout = this;}mPrivateFlags |= PFLAG_FORCE_LAYOUT;mPrivateFlags |= PFLAG_INVALIDATED; // 设置了该标志位,意味着一定需要drawif (mParent != null && !mParent.isLayoutRequested()) {mParent.requestLayout();}if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {mAttachInfo.mViewRequestingLayout = null;}}
}

回答最一开始的两个问题

  1. 刷新率为 60fps 的手机,每秒钟会执行 60 次 traversal 吗?
    1. 不会,只有注册了callback并且V_SYNC执行到了才会调用traversal
  2. ViewRootImpl 在一段时间内执行 60 次 Traversal,Measure / Layout / Draw 都会执行60次吗?
    1. 并不会,注册回调以后,只能触发traversal,但响应的Measure、layout、draw还有对应的条件

后续

1. 后续将进一步讨论使用requestLayout可能会遇到的问题。 

2. MeasureCache

3. Layout during Layout

相关文章:

安卓绘制原理概览

绘制原理 Android 程序员都知道 Android 的绘制流程分为 Measure、Layout、Draw 三步骤&#xff0c;其中 Measure 负责测量 View 的大小Layout 负责确定 View 的位置Draw 负责将 View 画在屏幕上 由 ViewRootImpl 实现的 performTraversal 方法是 Measure、layout、draw 的真正…...

接口测试工具开发文档

1 开发规划 1.1 开发人员 角 色 主要职责 负责模块 人员 备注 n xxx模块 xxx 1.2 开发计划 <附开发计划表> 1.3 开发环境和工具 开发工具 工具 作用 Notepad 编辑器 Perl 解释器 2 总体设计 设计思路&#xff1a;因为测试app和server。首先必须…...

面试题速记:JavaScript有哪些数据类型,它们的区别是?

JavaScript有哪些数据类型&#xff0c;它们的区别&#xff1f; JavaScript共有八种数据类型&#xff0c;分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。 其中 Symbol 和 BigInt 是ES6 中新增的数据类型&#xff1a; ●Symbol 代表创建后独一无二…...

Spring Cloud面试题

为什么需要学习Spring Cloud 不论是商业应用还是用户应用&#xff0c;在业务初期都很简单&#xff0c;我们通常会把它实现为单体结构的应用。但是&#xff0c;随着业务逐渐发展&#xff0c;产品思想会变得越来越复杂&#xff0c;单体结构的应用也会越来越复杂。这就会给应用带…...

计算机网络自顶向下-web页面请求历程

1. 准备: DHCP、 UDP、 IP 和以太网 假定 Bob 启动他的便携机&#xff0c;然后将其用一根以太网电缆连接到学校的以太网交换机 &#xff0c; 交换机与学校的路由器相连。学校的路由器与一个 ISP 连接&#xff0c; 本例中 ISP 为 comcast.net &#xff0c;为学校提供了 DNS 服务…...

打造西南交通感知新范式,闪马智能携手首讯科技落地创新中心

9月4日&#xff0c;2023年中国国际智能产业博览会&#xff08;以下简称“智博会”&#xff09;在重庆拉开帷幕。大会期间&#xff0c;由上海闪马智能科技有限公司&#xff08;以下简称“闪马智能”&#xff09;与重庆首讯科技股份有限公司&#xff08;以下简称“首讯科技”&…...

Android11去掉Settings中的网络和互联网一级菜单

碰到一个不要wifi不要蓝牙的项目&#xff0c;客户要求去掉Settings中的网络和互联网一级菜单&#xff0c;因为硬件都不贴&#xff0c;所以软件对应也要去掉。 我们可以根据packages/apps/Settings/res/xml/top_level_settings.xml的布局文件找到TopLevelNetworkEntryPreferenc…...

基于Python开发的五子棋小游戏(源码+可执行程序exe文件+程序配置说明书+程序使用说明书)

一、项目简介 本项目是一套基于Python开发的五子棋小游戏&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&a…...

JDBC入门到精通-10w总结

JDBC核心技术 笔记是以尚硅谷讲师宋红康JDBC课程为基础&#xff0c;加入自身学习体会&#xff0c;略有修改 第1章&#xff1a;JDBC概述 JDBC是java应用程序和数据库之间的桥梁。JDBC提供一组规范&#xff08;接口&#xff09;。向上是面向应用API&#xff0c;共应用程序使用。向…...

Linux之查看so/bin依赖(三十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

day-45 代码随想录算法训练营(19)动态规划 part 07

70.爬楼梯&#xff08;进阶&#xff09; 分析&#xff1a;基本动态规划转换为完全背包&#xff0c;因为1、2 两种上楼梯方式是无限选择的 思路&#xff1a; 1. j 表示 容量为 j 时&#xff0c;装满有dp[j]种方法2. dp[j]dp[j-nums[i]]3. 初始化 dp[0]1,dp[1]14. 遍历顺序&am…...

static关键字和final关键字

在java的关键字中&#xff0c;static关键字和final关键字是两个必须掌握的关键字。static关键字和final关键字用法多样&#xff0c;且在一定环境下使用&#xff0c;可以提高程序的运行性能&#xff0c;优化程序的结构。下面将依次介绍static关键字和final关键字。注意&#xff…...

使用Postman如何在接口测试前将请求的参数进行自定义处理

1、前言 当我们使用 Postman 进行接口测试时&#xff0c;对于简单的不需要处理的接口&#xff0c;直接请求即可&#xff0c;但是对于需要处理的接口&#xff0c;如需要转码、替换值等&#xff0c;则就麻烦一些&#xff0c;一般我们都是先手动把修改好的值拷贝到请求里再进行请…...

QT第二天

1.优化登陆界面&#xff0c;当点击登录按钮后&#xff0c;在该按钮对应的槽函数中&#xff0c;判断账户和密码框内的数据是否为admin和123456&#xff0c;如果账户密码匹配成功&#xff0c;则提示登陆成功并关闭登录界面&#xff0c;如果账户密码匹配失败&#xff0c;则提示登录…...

鸿蒙应用程序入口UIAbility详解

一、UIAbility概述 UIAbility是一种包含用户界面的应用组件&#xff0c;主要用于和用户进行交互。UIAbility也是系统调度的单元&#xff0c;为应用提供窗口在其中绘制界面。每一个UIAbility实例&#xff0c;都对应于一个最近任务列表中的任务。一个应用可以有一个UIAbility&am…...

扫地僧站群·静态养站王:自动万站智能LOGO功能说明

扫地僧站群静态养站王:自动万站智能LOGO功能说明,无需配置,自动根据网站状态生成对应LOGO&#xff0c;高质量LOGO万站不重样.说明&#xff1a;模板调用智能LOGO方式&#xff1a;{当前域名}/logo.png 自动识别HTTP/HTTPS 只需在模板需要展示LOGO的地方放上如上标签即可。以下为L…...

【Day-32慢就是快】代码随想录-二叉树-合并二叉树

给定两个二叉树&#xff0c;想象当你将它们中的一个覆盖到另一个上时&#xff0c;两个二叉树的一些节点便会重叠。 你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠&#xff0c;那么将他们的值相加作为节点合并后的新值&#xff0c;否则不为 NULL 的节点将直接…...

接口测试系列 —— 什么是接口测试?

接口测试系列 为什么要做这个事情&#xff1f; 对自己过往在接口测试上的经验&#xff0c;写一个小结的系列文章&#xff0c;是一个系统性的思考和知识构建。发布的同时&#xff0c;也是希望获得更多感兴趣的同学的意见和反馈&#xff0c;可以把这个部分做的更好。 接口测试概…...

qt作业day2

//widget.cpp#include "widget.h" #include "ui_widget.h"void Widget::usr_login() {if("admin" this->edit_acc->text()){if("123456" this->edit_psd->text()){speech->say("登录成功");emit jump_sig1…...

JWT一篇通

JWT 是什么&#xff1f; JWT&#xff08;JSON Web Token&#xff09;是一种开放的标准&#xff0c;标准的编号是RFC7591。用于在不同实体之间安全地传输信息。它是基于 JSON 编码的令牌。 JWT 的组成 JWT由三个部分组成&#xff1a;头部&#xff08;Header&#xff09;、载荷…...

【2023-09-01】vue中自定义按钮设置disabled属性后,异常触发click事件

vue中自定义按钮设置disabled属性后&#xff0c;异常触发click事件 项目中自定义按钮&#xff0c;使用a标签实现。设置disabled属性后&#xff0c;点击可以触发click事件。 由于各种原因&#xff0c;项目中并未使用成熟的第三方组件库&#xff0c;例如element-ui&#xff0c;a…...

nginx报错file not found解决

文章目录 一、nginx配置文件修改1.1 配置文件位置1.2 php fastcgi配置1.3 测试 二、nginxphp运行原理三、外网访问内网设置 采用nginxphp作为webserver的架构模式&#xff0c;在现如今运用相当广泛。然而第一步需要实现的是如何让nginx正确的调用php。由于nginx调用php并不是如…...

【力扣】96. 不同的二叉搜索树 <动态规划>

【力扣】96. 不同的二叉搜索树 给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;5 示例 2&#xff1a; 输入&am…...

Win11搭建 Elasticsearch 7 集群(一)

一&#xff1a; ES与JDK版本匹配一览表 elasticsearch从7.0开始默认安装了java运行环境&#xff0c;以便在没有安装java运行环境的机器上运行。如果配置了环境变量JAVA_HOME&#xff0c;则elasticsearh启动时会使用JAVA_HOME作为java路径&#xff0c;否则使用elasticsearch根目…...

哭了,python自动化办公,终于支持 Mac下载了

想了解更多精彩内容&#xff0c;快来关注程序员晚枫 大家好&#xff0c;这里是程序员晚枫&#xff0c;小红薯/小破站也叫这个名。 在我的主页发布的免费课程&#xff1a;给小白的《50讲Python自动化办公》&#xff0c;一直在更新中&#xff0c;昨晚12点多&#xff0c;有朋友在…...

【已更新建模代码】2023数学建模国赛B题matlab代码--多波束测线问题

一、 问题重述 1.1问题背景 海洋测深是测定水体深度与海底地形的重要任务&#xff0c;有两种主要技术&#xff1a;单波束测 深与多波束测深。单波束适用于简单任务&#xff0c;但多波束可提供更精确的地形数据。多 波束系统的关键在于覆盖宽度与重叠率的设计&#xff0c;以确保…...

GMSL技术让汽车数据传输更为高效(转)

目前&#xff0c;大部分车企都在其旗舰车型上配备了达到Level 2水平的自动驾驶技术&#xff0c;也就是高级自动驾驶辅助 ADAS系统。ADAS系统硬件主要由以下几部分组成&#xff0c;包括传感器、串行器、解串器、ADAS处理器等。 除了ADAS系统&#xff0c;包括传感器融合、音视频影…...

ARM+Codesys标准通用型控制器

整机工业级设计&#xff0c;通讯外设经过隔离保护 电源宽电压设计(9~36V DC ) 丰富的通讯接口&#xff0c;满足多种场合控制和通讯需求 四核工业级处理器&#xff0c;高性能&#xff0c;低功耗&#xff0c;高可靠性 机身无风扇设计&#xff0c;外壳小巧 搭载内核 100% 自主…...

YOLOV8从零搭建一套目标检测系统(修改model结构必看)附一份工业缺陷检测数据集

目录 1.YOLOV8介绍 2.YOLOV8安装 2.1环境配置 3.数据集准备 1.YOLOV8介绍 Yolov8结构图&#xff1a; YoloV8相对于YoloV5的改进点&#xff1a; Replace the C3 module with the C2f module. Replace the first 6x6 Conv with 3x3 Conv in the Backbone. Delete two Convs …...

Maven 的其它插件

文章目录 Maven 的其它插件dockerfile 插件Apache Maven Checkstyle Pluginp3c-pmd Maven 的其它插件 dockerfile 插件 dockerfile-maven-plugin 是 spotify 公司新提供的、用以替代 docker-maven-plugin 的插件&#xff0c;它同样是用于在 maven 中将当前项目打成一个 docke…...