谈一谈你对View的认识和View的工作流程

news2024/10/6 0:38:10

都2023年了,不会还有人不知道什么是View吧,不会吧,不会吧。按我以往的面试经验来看,View被问到的概率不比Activity低多少哦,个人感觉View在Android中的重要性也和Activity不相上下,所以这篇文章将介绍下View的基础知识及其工作原理。

一、初识View

什么是View ?

所谓的View,就是Android中所有控件的基类(例如:Button、TextView、LinearLayout等等),甚至是ViewGroup也是继承自View的,然而ViewGroup是一个控件组,里面包含许多控件(也就是一组View),所以这就意味着View本身既可以是单个控件,也可以是由许多控件组成的一个控件,这样看来View其实也没那么玄乎啊。

View的位置参数

首先我们要知道,在Android手机中,坐标系是以手机屏幕的最左上角为原点而建立的,大家可以参考下面的图片理解下。

其次,View的初始位置由四个属性决定,分别是:top、left、right、bottom。其中top和left为View左上角的纵坐标和横坐标,而bottom和right为View右下角的纵坐标和横坐标。(看官注意:这些坐标全都是相对于View所在的父容器来说的,是一个相对坐标,并不是在手机屏幕中的实际坐标)同样,在下面放上图示帮助大家理解:

所以,通过上面的一通分析,我们可以得出View的宽高和坐标之间的关系:

width = right - left
height = bottom -top

细心的看官可能发现了,在上面我把”初始位置“四个字给标红了。没错,那四个属性不仅仅是初始位置,而且在你的View不论是发生旋转或者平移时候,他们都不会改变,改变的其实是另外的位置参数。从Android3.0开始,View增加了几个额外的参数,它们分别是x、y、translationX、translationY,其中x和y是View左上角的坐标,而translationX、translationY是View左上角相对于父容器的偏移量,(注意:这几个参数同样是针对父容器而言的)并且translationX和translationY的默认值是0,它们有以下的换算关系:

x = left + translationX
y = top +translationY

所以不难看出,当View发生位置改变时,改变的其实是x、y、translationX、translationY这四个参数。好了,以上就是View位置参数的全部内容,如果以上内容各位看起来比较轻松的话,那么接下来的内容可能比较费劲,接下来继续发车了。

二、DecorView和MeasureSpec

View的三大流程无非就是Measure、Layout、Draw,但这三大流程都是基于DecorView中呈现的,然而想要呈现出View,还需要知道View的大小,在测量过程中MeasureSpec又是其中的关键,所以接下来我们有必要了解下他们。

初识DecorView

DecorView是Activity里的顶级View,它一般来说是一个竖直方向的LinearLayout(这与Android的版本和主题有关),在这个LinearLayout里面有上下两部分,上面是标题栏,下面是内容栏。我们在Activity的onCreate()方法中通过setContentView所设置的布局文件就被加入到了DecorView的内容栏之中,内容栏的id为content,通过ViewGroup content = findViewById(R.android.id.content)可以得到content,View层的事件都会先经过DecorView之后才继续向下传递的。同样是一图胜千言:

理解MeasureSpec

第一个问题,什么是MeasureSpec?在《Android开发艺术探索》一书中对它的解释是这样的:MeasureSpec翻译过来是”测量规格“或者”测量说明书“,是一个32位的int值,高2位代表SpecMode,低30位代表SpecSize。而SpecMode指测量模式,SpecSize指在某种测量模式下的规格大小

第二个问题,MeasureSpec是干啥的?同样书中的解释是:它在很大程度上决定了一个View的尺寸规格,之所以说是很大程度上是因为这个过程还受父容器的影响,因为父容器影响View的MeasureSpec的创建过程。在测量过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个measureSpec来测量出View的宽高。

可谓是听君一席话,胜似听君一席话,我一开始看也是云里雾里的,接下来我尽量不深入代码,带大家通俗的理解一下:

我回答下第一个问题,MeasureSpec其实就是一串数字,一串长度为32位的数字,里面包含着一些View的测量信息。

第二个问题的答案就是,通过这一串数字可以帮助系统测量出View的宽和高。

这够通俗了吧,这就跟你网购的快递一样的,快递单号就是那一串莫名其妙的数字(MeasureSpec),通过这一串数字能帮助你查询到快递运到哪了,换算到View上,不就是帮助系统知道View的宽和高嘛,这是一个道理,听懂就来点掌声。

那么接下来就要详细了解下系统是怎么通过这串数字(MeasureSpec)来测量出View的宽高的。在上面提到SpecMode和SpecSize。 SpecSize比较简单,通俗理解就是View在父容器下的实际大小或者是可用大小,有人可能会问为什么还会有个可用大小?这里我解释下:当你在布局文件中给定一个View一个确切的大小时,那么SpecSize就是实际大小,例如:android:layout_width = "x dp"android:layout_height = "x dp"。反之如果这两个属性给的值为”match_parent“、或者是"wrap_content"时,此时的SpecSize就是父容器下的可用大小。

SpecMode相对于SpecSize而言削微有那么点抽象,它分为三类,分别如下:

  1. UNSPECIFIE:父容器不对View有任何限制,要多大就给大多,这一般用于系统内部,表示一种测量的状态,一般来说可忽视。
  2. EXACTLY:父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值,它对应于LayoutParams中的match_parent赋予具体数值的这两种模式
  3. AT_MOST:父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值需要看不同View的具体实现。它对应于LayoutParams中的wrap_content

通俗的说,你在layout布局文件中给View的”match_parent“、和"wrap_content"属性设置不同的值,再根据父容器的SpecMode加以对应,就会得到View实际的SpecMode,具体的对应关系如下:图来!!!(图中的parentSize指的是父容器中目前可使用的大小,表格中的UNSPECIFIED中的Size为 0 表示忽略,在普通的View中是不会出现的,只会在例如DecorView这种系统级别的才会出现)

parentSpecMode /childLayoutParamsEXACTLYAT_MOSTUNSPECIFIED
dp/pxEXACTLY (childSize)EXACTLY (childsize)EXACTLY (childsize)
match_parentEXACTLY (parentSize)AT_MOST (parentSize)UNSPECIFIED (0)
wrap_contentAT_MOST (parentSize)AT_MOST (parentSize)UNSPECIFIED (0)

所以,只要提供了父容器的MeasureSpec和子元素的LayoutParams,就可以确定出子View的MeasureSpec了,从而就可以进一步确定出View测量后的大小了,至此MeasureSpec扫盲结束。让我们喝口水,继续讲述View的三大流程。等等,你刚才说,喝什么???

三、View的三大流程

此为面试热点,面试官一般会从这里引入,然后不断对你进行摸底,各位要跳槽的看官要注意了,View的三大流程是指measure、layout、draw,即测量、布局和绘制。其中measure确定View的测量宽高,layout确定View的最终宽高和四个顶点位置,而draw则是将View绘制到屏幕上。所以我们逐一进行分析,同样我也不深入代码,有想深入了解代码的可以自己查阅相关信息或者参考《Android开发艺术探索》一书。

measure过程

measure过程主要分为两类,一类是单个View的measure,另一类是对于ViewGroup的measure。单个View的measure比较简单,直接通过调用自身的measure方法就完成了测量过程。然而对于ViewGroup而言,除了会完成自身的measure过程外,还会去遍历调用所有子元素的measure方法,各个子元素再递归去执行这个流程。

1. View的measure过程

上文有提到View的measure通过调用自身的measure()方法就完成了测量过程,然而measure()方法中又会去调用onMeasure(),具体代码如下:

//View的measure方法
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
     ...
     // measure ourselves, this should set the measured dimension flag back
     onMeasure(widthMeasureSpec, heightMeasureSpec);
     ...    
}

//View的onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),
        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

从上面的代码我们可以知道两点,一是宽和高分别有自己的MeasureSpec,至于什么是MeasureSpec,上文有提到过了。二是宽或高的测量大小是通过getDefaultSize()方法得到的,至于他是怎么得到的,我们继续往下看:

public static int getDefaultSize(int size, int measureSpec) { 
    int result = size; 
    int specMode = MeasureSpec.getMode(measureSpec); 
    int specSize = MeasureSpec.getSize(measureSpec); 
    switch (specMode) {
        case MeasureSpec.UNSPECIFIED: 
            result = size; 
            break;

        case MeasureSpec.AT_MOST: 
        case MeasureSpec.EXACTLY: 
            result = specSize; 
            break; 
    } 
        return result; 
}

对于MeasureSpec.getMode()MeasureSpec.getSize()方法我详细解释下,这是系统提供的一个解包方法,也可以理解为解密吧(其实也就是二进制的与操作),将一个measureSpec通过解包,从而得到sepcMode和specSize,再往后就是根据specMode返回对应的大小了。

另外,getDefaultSize()方法还传入了一个getSuggestedMinimumHeight()getSuggestedMinimumWidth()参数,这看名字应该是一个系统推荐的默认值,至于这个默认值怎么来的,我就不带着大家分析代码了,我直接给出结论,有兴趣的看官可以自行了解,我这里以getSuggestedMinimumWidth()为例给结论就行,因为getSuggestedMinimumHeight()也是一模一样的。

getSuggestedMinimumWidth()结论: 如果View没有设置背景,那么返回android:minWidth这个属性所指定的值(这个值可以为0),如果View设置了背景,则返回android:minWidth和背景的最小宽度这两者中的最大值.

面试点细节总结

敲黑板了,注意了,此为魔鬼细节和面试问点,各位看官要注意了

面试官:直接继承自View的自定义控件需要注意什么?

我:当然是需要重写onDraw()方法啦!

面试官:…这简直是一句犀利的废话。

直接继承View的自定义View需要重写onMeasure方法并设置wrap_content时的大小,否则在布局中使用wrap_content时就相当于match_parent,导致使用match_parent和使用wrap_content时完全没有区别。

之所以会出现这样的现象是因为View在布局中使用wrap_content时,那么它的specMode是AT_MOST模式,在这种模式下,它的宽、高specSize都是parentSize(这一部分前面有讲过,可以查阅表格,如果懒得往上翻的朋友,我会在下面再放一次表格),而parentSize代表的是父容器中目前可以使用的大小。所以在这种情况下,View的宽高就会等于父容器可使用空间大小,我们可以再看表格,艾,巧了,当我们使用match_parent时,specSize同样也是parentSize,所以呈现的效果完全一致,这下大家都明白了吧?

parentSpecMode /childLayoutParamsEXACTLYAT_MOSTUNSPECIFIED
dp/pxEXACTLY (childSize)EXACTLY (childsize)EXACTLY (childsize)
match_parentEXACTLY (parentSize)AT_MOST (parentSize)UNSPECIFIED (0)
wrap_contentAT_MOST (parentSize)AT_MOST (parentSize)UNSPECIFIED (0)

呐,还是给大家举个栗子,帮助理解。

大家可以看到我在布局中加了两个控件,一个是普通的View,背景色为黑色(这里我们也可以看成自定义View),另一个是TextView,背景色为白色。这控件的宽高属性全部是wrap_content,然而我们自定义的View却撑满了整个屏幕,TextView却没有。这是因为TextView中已经重写了onMeasure()方法,在方法中对specMode为AT_MOST时,做了特殊处理,大家感兴趣可以自己查看源码,而View中没有处理。所以出现了上述的问题。

所以当大家在写自定义View时,记得也加入这样的处理,我在下面为大家放上一个解决方案,具体值得大小还需要你们自己去灵活定义:

//代码来源于《Android开发艺术探索》
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    int widthSize = MeasureSpec.getSize(widthMeasureSpec); 
    int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
    int heightSize = MeasureSpec.getSize(heightMeasureSpec); 
    int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
    if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) { 
        setMeasuredDimension(mWidth, mHeight); 
    } else if (widthMode == MeasureSpec.AT_MOST) { 
        setMeasuredDimension(mWidth, heightSize); 
    } else if (heightMode == MeasureSpec.AT_MOST) { 
        setMeasuredDimension(widthSize, mHeight); 
    } 
}

2. ViewGroup的measure过程

ViewGroup自身是没有重写onMeasure()方法的,而View是有重写的。但是ViewGroup提供了一个measureChild方法,其作用就是取出子元素的LayoutParams,进一步获得子元素的MeasureSpec,接着将MeasureSpec传递给View的measure方法进行测量,View的measure测量流程已经在上面做了详细分析了。

大家看到这应该也明白了,为什么自定义View不用必须重写onMeasure,而自定义ViewGroup必须重写onMeasure方法的原因了

所以ViewGroup的测量流程简单而言可以分为两块内容,第一块递归对子View进行measure第二块根据每个子View的测量结果,累计加总测量出ViewGroup自身的宽高。第一块内容在上文详细介绍过,因此我们主要关注第二块内容,接下来以LinearLayout为例子进行介绍,我们先来看下LinearLayout的onMeasure方法:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

上述代码可谓简单明了,我们就只以VERTICAL方向上去看一下,另一个也大同小异,大家可以自行了解,由于measureVertical方法比较长,我就截取部分源码,描述下大概逻辑,首先看代码:

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    mTotalLength = 0;
    ...
    // See how tall everyone is. Also remember max width.
    //(遍历每个子View的高度,并且记录下总高度,其中mTotalLength就是总高度)
    for (int i = 0; i < count; ++i) {
        final View child = getVirtualChildAt(i);
        ...
        // Determine how big this child would like to be. If this or
        // previous children have given a weight, then we allow it to
        // use all available space (and we will shrink things later
        // if needed).
        final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
        measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                heightMeasureSpec, usedHeight);

        final int childHeight = child.getMeasuredHeight();
        final int totalLength = mTotalLength;

        mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
               lp.bottomMargin + getNextLocationOffset(child));
        ...
    }

    //所有子元素遍历结束,开始测量自身大小
    // Add in our padding,加顶部和底部的padding统计进总高度
    mTotalLength += mPaddingTop + mPaddingBottom;
    int heightSize = mTotalLength;

    // Check against our minimum height
    heightSize = Math.max(heightSize, getSuggestedMinimumHeight());

    // Reconcile our calculated size with the heightMeasureSpec
    // 根据父容器的大小和自身的MeasureSpec计算出最终高度,因为子元素高度总和是不能超过父元素剩余空间的
    int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
    ...
    maxWidth += mPaddingLeft + mPaddingRight;
    // Check against our minimum width
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

    //传入最终测量出的宽高尺寸,从而设置ViewGroup的宽高
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
        heightSizeAndState);
}

上述代码的主要逻辑就是系统会遍历所有子元素并对他们执行measureChildBeforeLayout方法,在这个方法内部会调用子元素的measure方法,也就是进入到了第一块内容中(上文已经讲解过),系统还通过mTotalLength来记录LinearLayout在竖直方向的总高度,每测量出一个子元素,mTotalLength就会增加,增加的部分主要包括子元素的高度以及子元素在竖直方向上的margin、padding等。最终设置ViewGroup的测量宽高,至此测量完成!

面试点细节总结

  1. 自定义ViewGroup,继承ViewGroup后,必须要重写onMeasure方法测量自身和子View,进而重写onLayout,这点与自定义View差别较大,需要特别注意.
  1. 不论是自定义View还是自定义ViewGroup,他们在measure过程得到的宽高都不是最终宽高,仅仅是测量宽高。最终宽高是在onLayout过程中才真正确定的,所以要获取一个控件的宽高,最好在onLayout方法中去获取。当然大多数情况下,控件的测量宽高和最终宽高是相等的.
  1. 由于View的measure过程和Activity的生命周期方法是不同步执行的,因此无法保证Activity执行了onCreate、onStart、onResume时某个View已经被测量完毕了,如果此时View还没有测量完毕,我们获取到的宽高值将会是0,要解决这种问题,我们可以采用view.post(runnable)方法,通过post将一个runnable投递到消息队列尾部,等View初始化完成后,就会从Looper中调用此runnable,从而拿到测量出的宽高值,代码示例如下:
    mView.post(new Runnable() {
        @Override
        public void run() {
            int width = mView.getMeasuredWidth();
            int height = mView.getMeasuredHeight();
        }
    });

layout过程

Layout流程是用于确定View或ViewGroup的位置,因为layout流程相对于measure而言比较简单,我们先看看view的layout大致代码逻辑:

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 = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);
        ...
    }
    ...
}

在上述代码中,大致流程是,layout方法首先会通过setFrame方法来设定View的四个顶点位置,即初始化mLeft、mTop、mRight、mBottom这四个值,View的四个顶点一旦确定,View在父容器中的位置也就随之确定了。接下来就会调用onLayout方法,这个方法的用途是父容器确定子元素位置的,通俗而言就是layout是确定自身的位置,onLayout是确定其子View的位置,因为单个View没有子元素,ViewGroup类布局的不确定性,所以他们均对onLayout方法都是空实现,即如下所示:

/**
 * Called from layout when this view should
 * assign a size and position to each of its children.
 *
 * Derived classes with children should override
 * this method and call layout on each of
 * their children.
 */
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}

因为LinearLayout继承自ViewGroup,所以它必然实现了onLayout方法,所以我们继续以它为例,看看它是如何实现的,代码如下所示:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (mOrientation == VERTICAL) {
        layoutVertical(l, t, r, b);
    } else {
        layoutHorizontal(l, t, r, b);
    }
}

啊哈,看来和onMeasure()中的实现逻辑类似,我们还是以layoutVertical进行讲解,同样继续给出主要代码逻辑:

void layoutVertical(int left, int top, int right, int bottom) {
    ...
    final int count = getVirtualChildCount();
    ...
    for (int i = 0; i < count; i++) {
        final View child = getVirtualChildAt(i);
        if (child == null) {
            childTop += measureNullChild(i);
        } else if (child.getVisibility() != GONE) {
            final int childWidth = child.getMeasuredWidth();
            final int childHeight = child.getMeasuredHeight();

            final LinearLayout.LayoutParams lp =
                    (LinearLayout.LayoutParams) child.getLayoutParams();
            ...
            if (hasDividerBeforeChildAt(i)) {
                childTop += mDividerHeight;
            }

            childTop += lp.topMargin;
            setChildFrame(child, childLeft, childTop + getLocationOffset(child),
                    childWidth, childHeight);
            childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);

            i += getChildrenSkipCount(child, i);
        }
    }
}

所以综上所述:layoutVertical方法会遍历所有子元素并调用setChildFrame方法来为子元素指定对应位置,其中childTop记录当前末端子元素的高度位置,childTop会逐渐增大,意味着后面的子元素会被放置在靠下的位置,这正是竖直方向上LinearLayout的特性。

再简单说一下setChildFrame方法,它仅仅是调用子元素(这里我们可以看成是儿子元素)的layout方法,然后当该元素(儿子元素)确定了自己的位置以后又调用onLayout方法安排其子元素(孙子元素)的位置。好家伙,这简直就是俄罗斯套娃(递归)。

draw过程

Draw过程就更简单了,尤其是对于做了许多自定义View的友友来说。它的作用就是将View绘制到屏幕上面,绘制过程大致如下:

  1. 绘制背景background.draw(canvas).
  2. 绘制自己 (onDraw).
  3. 绘制children (dispatchDraw).
  4. 绘制装饰 (onDrawScrollBars).

关于绘制过程,是三大流程中最为简单的了,看官可以自行查看源码,这里就不再赘述了,另外ViewGroup一般不用重写onDraw来绘制自己,只需要对子View进行绘制就可以。但明确知道一个ViewGroup需要通过onDraw来绘制内容时,我们需要调用setViewNotDraw(false)来进行设置;

好了,至此View三大工作流程已经讲解完毕,没错,接下来当然是划重点啦。

四、自定义View相关总结

自定义View的分类

  1. 继承View:需重写onDraw方法,一般用于实现一些不规则的效果。
  2. 继承ViewGroup:需重写onMeasure、onLayout方法,即自己定义一种除了像linearLayout、RelativeLayout等,这几种系统布局之外的布局,这种情况比较少
  3. 继承特定的View:比如继承ImgView等,一般用于拓展某种已有的View的功能。
  4. 继承特定的ViewGroup: 比如继承LinearLayout,一般也是用于拓展功能。但好处是它不需要自己重写onMeasure和onLayout方法,并且也比较常用。

自定义View的注意事项

  1. 让View支持wrap_content,这一点在上面View的measure过程的面试点细节总结里详细介绍过。
  2. 在自定义View时,不要在onDraw()方法中定义变量和执行循环操作,不然会导致内存溢出和卡顿掉帧的现象。
  3. 如果View中有线程或者动画,需要及时停止,否则会造成内存泄露的情况。
  4. View带有滑动嵌套情形时,需要处理好滑动冲突。
  5. 不建议使用自定义的Handler,而应该使用view.post(runnable)方法进行替代。
  6. 如果有必要,让你的View支持padding。对于直接继承View的控件,如果不在onDraw()方法中处理padding,那么padding属性是没有效果的。对于直接继承自ViewGroup的控件,需要在onMeasure()onLayout()中考虑padding和子元素的margin对其造成的影响,否则也会导致padding和子元素的margin失效。

更多Android 面试知识点:https://qr18.cn/CKV8OZ

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

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

相关文章

【Python工具】WiFi工具

文章目录前言一、介绍二、使用步骤1.完整代码2.简单使用总结免责声明&#xff1a;前言 注意&#xff1a;此工具为自己测试&#xff0c;不可用作恶意使用。 考虑在windows中的wifi破解工具五花八门很多的广告程序&#xff0c;就自己写一个&#xff0c;原理比较简单&#xff0c…

11技术太卷我学APEX-数据加载

11技术太卷我学APEX-数据加载 0 所谓的数据加载 就是导入数据到数据库表中&#xff0c;本示例就采用Excel导入数据到《技术太卷我学APEX》的apex_learn表。表结构大概是这样的 CREATE TABLE "APEX_LEARN" ( "P_ID" NUMBER(17,0) NOT NULL ENABLE, &quo…

PMP真的有那么厉害?你需要考PMP吗?

这个含金量是有的&#xff0c;是目前项目管理界含金量较高的证书&#xff0c;但也要分人&#xff0c; 因为这是职业证书&#xff0c;主要用于提高职场工作能力&#xff0c;不搞这一行的&#xff0c;PMP证书含金量再高也是一张废纸&#xff0c;可以看下下面这张图&#xff0c;这…

【Redis】Redis 的过期策略以及内存淘汰机制详解

Redis 的过期策略以及内存淘汰机制详解1. Redis 的过期策略1.1 如何设置 key 的过期时间&#xff1f;1.2 key 设置且到了过期时间后&#xff0c;该 key 保存的数据还占据内存么&#xff1f;1.3 Redis 如何删除过期的数据1.3.1 定期删除1.3.2 惰性删除2. Redis 的内存淘汰机制2.…

智能新冠疫苗接种助手管理系统

项目背景介绍 近几年来,网络事业&#xff0c;特别是Internet发展速度之快是任何人都始料不及的。目前&#xff0c;由于Internet表现出来的便捷&#xff0c;快速等诸多优势&#xff0c;已经使它成为社会各行各业&#xff0c;甚至是平民大众工作&#xff0c;生活不可缺少的一个重…

【UE4 制作自己的载具】2-导入UE

在上一篇博客中&#xff08;【UE4 制作自己的载具】1-使用3dsmax制作载具&#xff09;已经完成了载具模型的制作&#xff0c;本篇需要完成的是将模型导入UE中并进行一些调整。本篇制作步骤&#xff1a;新建一个UE工程&#xff0c;可以添加一个载具模板创建一个“Vehicle”文件夹…

windows服务器上传文件解决方案

1.说明 1.如果上传到linux系统&#xff0c;通常使用ftp相关技术&#xff0c;配合windows端的ftp客户端工具比如FileZilla等进行大文件的上传工作。 2.同理windows服务器也可以开启ftp服务用来传输大文件。 3.本文介绍偷懒方式&#xff08;常规是开启windows的ftp服务&#xff0…

案例|山东省中医院基于ZABBIX构建网络设备监控预警平台

作者介绍&#xff1a;董晨&#xff0c;山东省中医院信息科机房运维管理 本文从背景、演进、成效来分享建设过程&#xff0c;最终得出结论&#xff0c;类似Zabbix的国产成熟产品市场价值动辄上百万&#xff0c;而我们以极小成本&#xff0c;为医院节省了大量资金&#xff0c;取…

Java多线程(一)--多线程基础知识

1. 为什么要使用并发编程提升多核CPU的利用率&#xff1a;一般来说一台主机上的会有多个CPU核心&#xff0c;我们可以创建多个线程&#xff0c;理论上讲操作系统可以将多个线程分配给不同的CPU去执行&#xff0c;每个CPU执行一个线程&#xff0c;这样就提高了CPU的使用效率&…

通过NPM生态系统中的依赖树揭开脆弱性传播及其演化的神秘面纱

通过NPM生态系统中的依赖树揭开脆弱性传播及其演化的神秘面纱 本文实现了一个依赖约束解析器来解决NPM依赖约束的多样性&#xff0c;并在此基础上构建了一个完整的依赖漏洞知识图&#xff08;DVGraph&#xff09;&#xff0c;以捕获所有NPM包之间的依赖关系。 https://www.se…

关于thumb2指令下函数运行地址对齐问题及验证固件分析

近期&#xff0c;在分析Thumb2指令的一个固件文件时&#xff0c;发现Thumb2指令集下执行函数的运行地址不对齐&#xff1f; 于是&#xff0c;为了分析一下原因&#xff0c;找了手头上现有的一款基于Cortex3内核的一块板子来实际执行看一下&#xff0c;结合编译后的Hex文件&…

【性能测试】loadrunner(一)知识准备

【性能测试】loadrunner&#xff08;一&#xff09;知识准备 目录&#xff1a;导读 1.0. 前言 1.1 性能测试术语介绍 1.2 性能测试分类 1.3 HTTP我们需要知道的 1.4 Loadrunner 12.55安装 1.0. 前言 ​ 在性能测试中&#xff0c;牵扯到了许多比较杂的知识点&#xff0c;…

Java文件IO操作:File类的相关内容

Java文件IO操作一、File类1.相对路径和绝对路径2.路径分隔符&#xff08;同一路径下、多个路径下&#xff09;3.实例化4.常见方法一、File类 File类继承自Object类&#xff0c;实现了Serializable接口和Comparable接口&#xff1b; File类属于java.io包&#xff1b; File类是文…

高通平台开发系列讲解(WIFI篇)802.11 基本概念

文章目录 一、WLAN概述二、802.11发展历程三、802.11基本概念沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文将基于高通平台介绍802.11基本概念。 一、WLAN概述 WLAN是Wireless Local Area Network的简称,指应用无线通信技术将计算机设备互联起来,构成可以互相通…

Python计算 -- 内附蓝桥题:相乘,三角回文数

计算 ~~不定时更新&#x1f383;&#xff0c;上次更新&#xff1a;2023/02/23 &#x1f5e1;常用函数&#xff08;方法&#xff09; 1. 求一个整数的最末位 举个栗子&#x1f330; n int(input()) end n % 10蓝桥例题1 - 相乘✖️ 题目描述 本题为填空题&#xff0c;…

【python学习笔记】:输出与输入

01 输出方式 表达式语句、print()函数和使用文件对象的write()方法。 02 输出形式 格式化输出str.format()函数、转成字符串可以使用repr()或str()函数来实现。 (1)repr()&#xff1a;产生一个解释器易读的表达形式&#xff0c;便于字符串的拼接。 例&#xff1a;输出平方与…

OpenGL超级宝典学习笔记:着色器存储区块、原子内存操作、内存屏障

前言 本篇在讲什么 本篇为蓝宝书学习笔记 着色器存储区块 原子内存操作 内存屏障 本篇适合什么 适合初学Open的小白 本篇需要什么 对C语法有简单认知 对OpenGL有简单认知 最好是有OpenGL超级宝典蓝宝书 依赖Visual Studio编辑器 本篇的特色 具有全流程的图文教学 重…

五月天演唱会门票开售,retry了一小时也没re进去!到底是什么高科技在作祟?!

等待了三年&#xff0c;大型线下演唱会终于回归了&#xff01;前段时间&#xff0c;文化和旅游部市场管理司发布通知称&#xff0c;自2023年2月16日起&#xff0c;各地文化和旅游行政部门恢复对涉港澳台营业性演出的受理和审批。据中国演出行业协会在业内调研了解&#xff0c;周…

Python Pytorch开发环境搭建(Windows和Ubuntu)

Python Pytorch开发环境搭建&#xff08;Windows和Ubuntu&#xff09; 目录 Pytorch开发环境搭建 1. 安装cuda cudnn (1)Windows安装方法 (2)Ubuntu18.04安装方法 2. 安装Python(推荐使用Anaconda) (1)Windows安装方法 (2)Ubuntu18.04安装方法 3. Pytorch安装 4. 安装…

渗透测试之端口探测实验

渗透测试之端口探测实验实验目的一、实验原理1.1 端口1.2 服务二、实验环境2.1 操作机器2.2 实验工具三、实验步骤1. 使用netstat手动探测指定服务2. 使用namp工具进行端口扫描2. 使用ssh命令总结实验目的 了解端口、服务的基本概念熟悉手工探测方式netstat命令的使用掌握扫描…