Android ImageView分析并展开

Android ImageView分析并展开

大家好,又见面了,我是全栈君,今天给大家准备了Idea注册码。

版本号:1.0 
日期:2014.6.11 
2014.6.12
版权:© 2014 kince 转载注明出处

  ImageView是开发中经常使用到的一个控件,也能够说是不可缺少的。

对于它的使用。除了注意ScaleType的理解和设置外,还须要注意其它一些问题,比方设置一张大的背景图片内存占用和释放等。

还有它的拓展性方面,像圆角图片、圆形图片、图片边框等等。因此,假设想熟练使用这个控件,就须要对事实上现的机制有一个基本的了解。

  
ImageView也是直接继承于View类。基本的结构图例如以下:

Android ImageView分析并展开

 鉴于篇幅大小,就不copy ImageView的总体代码,选择结构图中的部分作为重点。首先是构造方法,代码例如以下:

 public ImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initImageView();

        TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.ImageView, defStyle, 0);

        Drawable d = a.getDrawable(com.android.internal.R.styleable.ImageView_src);
        if (d != null) {
            setImageDrawable(d);
        }

        mBaselineAlignBottom = a.getBoolean(
                com.android.internal.R.styleable.ImageView_baselineAlignBottom, false);

        mBaseline = a.getDimensionPixelSize(
                com.android.internal.R.styleable.ImageView_baseline, -1);

        setAdjustViewBounds(
            a.getBoolean(com.android.internal.R.styleable.ImageView_adjustViewBounds,
            false));

        setMaxWidth(a.getDimensionPixelSize(
                com.android.internal.R.styleable.ImageView_maxWidth, Integer.MAX_VALUE));
       
        setMaxHeight(a.getDimensionPixelSize(
                com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
       
        int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
        if (index >= 0) {
            setScaleType(sScaleTypeArray[index]);
        }

        int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0);
        if (tint != 0) {
            setColorFilter(tint);
        }
       
        int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
        if (alpha != 255) {
            setAlpha(alpha);
        }

        mCropToPadding = a.getBoolean(
                com.android.internal.R.styleable.ImageView_cropToPadding, false);
       
        a.recycle();

        //need inflate syntax/reader for matrix
    }

    private void initImageView() {
        mMatrix     = new Matrix();
        mScaleType  = ScaleType.FIT_CENTER;
        mAdjustViewBoundsCompat = mContext.getApplicationInfo().targetSdkVersion <=
                Build.VERSION_CODES.JELLY_BEAN_MR1;
    }
  在构造方法中也是非经常规的从attrs文件里读取属性值,并进行设置。也能够看到ImageView默认使用的ScaleType
是FIT_CENTER。说到
ScaleType。它是一个枚举类型,用于设置。寻常使用的ScaleType就是在这里定义的。

/**     * Options for scaling the bounds of an image to the bounds of this view.     */    public enum ScaleType {        /**         * Scale using the image matrix when drawing. The image matrix can be set using         * {@link ImageView#setImageMatrix(Matrix)}. From XML, use this syntax:         * <code>android:scaleType="matrix"</code>.         */        MATRIX      (0),        /**         * Scale the image using {@link Matrix.ScaleToFit#FILL}.         * From XML, use this syntax: <code>android:scaleType="fitXY"</code>.         */        FIT_XY      (1),        /**         * Scale the image using {@link Matrix.ScaleToFit#START}.         * From XML, use this syntax: <code>android:scaleType="fitStart"</code>.         */        FIT_START   (2),        /**         * Scale the image using {@link Matrix.ScaleToFit#CENTER}.         * From XML, use this syntax:         * <code>android:scaleType="fitCenter"</code>.         */        FIT_CENTER  (3),        /**         * Scale the image using {@link Matrix.ScaleToFit#END}.         * From XML, use this syntax: <code>android:scaleType="fitEnd"</code>.         */        FIT_END     (4),        /**         * Center the image in the view, but perform no scaling.         * From XML, use this syntax: <code>android:scaleType="center"</code>.         */        CENTER      (5),        /**         * Scale the image uniformly (maintain the image's aspect ratio) so         * that both dimensions (width and height) of the image will be equal         * to or larger than the corresponding dimension of the view         * (minus padding). The image is then centered in the view.         * From XML, use this syntax: <code>android:scaleType="centerCrop"</code>.         */        CENTER_CROP (6),        /**         * Scale the image uniformly (maintain the image's aspect ratio) so         * that both dimensions (width and height) of the image will be equal         * to or less than the corresponding dimension of the view         * (minus padding). The image is then centered in the view.         * From XML, use this syntax: <code>android:scaleType="centerInside"</code>.         */        CENTER_INSIDE (7);               ScaleType(int ni) {            nativeInt = ni;        }        final int nativeInt;    }
 功能是设置图片的显示位置和大小等方面。

接着就是onMeasure()方法了,它用于设置ImageView的大小。我们在xml文件里设置ImageView的时候,假设指定了固定的宽高,那么onMeasur()方法中測量的大小就是固定的宽高大小;假设是包裹内容,那么就须要进一步的计算。

@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        resolveUri();//获取图片Drawable        int w;        int h;               // Desired aspect ratio of the view's contents (not including padding)        float desiredAspect = 0.0f;               // We are allowed to change the view's width        boolean resizeWidth = false;               // We are allowed to change the view's height        boolean resizeHeight = false;               final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);        if (mDrawable == null) {            // If no drawable, its intrinsic size is 0.            mDrawableWidth = -1;            mDrawableHeight = -1;            w = h = 0;        } else {            w = mDrawableWidth;在updateDrawable(Drawable d)方法赋值的。            h = mDrawableHeight;            if (w <= 0) w = 1;            if (h <= 0) h = 1;            // We are supposed to adjust view bounds to match the aspect            // ratio of our drawable. See if that is possible.            if (mAdjustViewBounds) {                resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;                resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;                               desiredAspect = (float) w / (float) h;            }        }               int pleft = mPaddingLeft;        int pright = mPaddingRight;        int ptop = mPaddingTop;        int pbottom = mPaddingBottom;        int widthSize;        int heightSize;        if (resizeWidth || resizeHeight) {            /* If we get here, it means we want to resize to match the                drawables aspect ratio, and we have the freedom to change at                least one dimension.            */            // Get the max possible width given our constraints            widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);            // Get the max possible height given our constraints            heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);            if (desiredAspect != 0.0f) {                // See what our actual aspect ratio is                float actualAspect = (float)(widthSize - pleft - pright) /                                        (heightSize - ptop - pbottom);                               if (Math.abs(actualAspect - desiredAspect) > 0.0000001) {                                       boolean done = false;                                       // Try adjusting width to be proportional to height                    if (resizeWidth) {                        int newWidth = (int)(desiredAspect * (heightSize - ptop - pbottom)) +                                pleft + pright;                        // Allow the width to outgrow its original estimate if height is fixed.                        if (!resizeHeight && !mAdjustViewBoundsCompat) {                            widthSize = resolveAdjustedSize(newWidth, mMaxWidth, widthMeasureSpec);                        }                        if (newWidth <= widthSize) {                            widthSize = newWidth;                            done = true;                        }                    }                                       // Try adjusting height to be proportional to width                    if (!done && resizeHeight) {                        int newHeight = (int)((widthSize - pleft - pright) / desiredAspect) +                                ptop + pbottom;                        // Allow the height to outgrow its original estimate if width is fixed.                        if (!resizeWidth && !mAdjustViewBoundsCompat) {                            heightSize = resolveAdjustedSize(newHeight, mMaxHeight,                                    heightMeasureSpec);                        }                        if (newHeight <= heightSize) {                            heightSize = newHeight;                        }                    }                }            }        } else {            /* We are either don't want to preserve the drawables aspect ratio,               or we are not allowed to change view dimensions. Just measure in               the normal way.            */            w += pleft + pright;            h += ptop + pbottom;                           w = Math.max(w, getSuggestedMinimumWidth());            h = Math.max(h, getSuggestedMinimumHeight());            widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);            heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);        }        setMeasuredDimension(widthSize, heightSize);    }

  
在onMeasure方法中,首先调用了resolveUri()这种方法。目的就是为了确定Drawable。

假设设置了drawableResource。那么Drawable就是其值;假设没有。那么就从ContentResolver获取一个Drawable。

 private void resolveUri() {
        if (mDrawable != null) {
            return;
        }

        Resources rsrc = getResources();
        if (rsrc == null) {
            return;
        }

        Drawable d = null;

        if (mResource != 0) {
            try {
                d = rsrc.getDrawable(mResource);
            } catch (Exception e) {
                Log.w("ImageView", "Unable to find resource: " + mResource, e);
                // Don't try again.
                mUri = null;
            }
        } else if (mUri != null) {
            String scheme = mUri.getScheme();
            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
                try {
                    // Load drawable through Resources, to get the source density information
                    ContentResolver.OpenResourceIdResult r =
                            mContext.getContentResolver().getResourceId(mUri);
                    d = r.r.getDrawable(r.id);
                } catch (Exception e) {
                    Log.w("ImageView", "Unable to open content: " + mUri, e);
                }
            } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
                    || ContentResolver.SCHEME_FILE.equals(scheme)) {
                InputStream stream = null;
                try {
                    stream = mContext.getContentResolver().openInputStream(mUri);
                    d = Drawable.createFromStream(stream, null);
                } catch (Exception e) {
                    Log.w("ImageView", "Unable to open content: " + mUri, e);
                } finally {
                    if (stream != null) {
                        try {
                            stream.close();
                        } catch (IOException e) {
                            Log.w("ImageView", "Unable to close content: " + mUri, e);
                        }
                    }
                }
        } else {
                d = Drawable.createFromPath(mUri.toString());
            }
   
            if (d == null) {
                System.out.println("resolveUri failed on bad bitmap uri: " + mUri);
                // Don't try again.
                mUri = null;
            }
        } else {
            return;
        }

        updateDrawable(d);
    }
  之后在resolveUri()这种方法的最后,调用了 updateDrawable(d)方法。这种方法代码例如以下:
 private void updateDrawable(Drawable d) {
        if (mDrawable != null) {
            mDrawable.setCallback(null);
            unscheduleDrawable(mDrawable);
        }
        mDrawable = d;
        if (d != null) {
            d.setCallback(this);
            if (d.isStateful()) {
                d.setState(getDrawableState());
            }
            d.setLevel(mLevel);
            d.setLayoutDirection(getLayoutDirection());
            d.setVisible(getVisibility() == VISIBLE, true);
            mDrawableWidth = d.getIntrinsicWidth();
            mDrawableHeight = d.getIntrinsicHeight();
            applyColorMod();
            configureBounds();
        } else {
            mDrawableWidth = mDrawableHeight = -1;
        }
    }
  能够看到就是为了Drawable宽高赋值的。回过头来继续看。假设Drawable的宽高不为空的话就分别赋值给w和h。假设为空的话值为-1。然后是一个if推断,mAdjustViewBounds作为推断的变量,它是在setAdjustViewBounds方法中设置的,默觉得false,所以必须设置为true,这个推断才会运行。当然这个变量的值也能够在xml文件里设置(android:adjustViewBounds)。

那这种方法是做什么用的呢?设置View的最大高度,单独使用无效,须要与setAdjustViewBounds一起使用。假设想设置图片固定大小,又想保持图片宽高比,须要例如以下设置:

1) 设置setAdjustViewBounds为true;


2) 设置maxWidth、MaxHeight;


3) 设置设置layout_width和layout_height为wrap_content。

  再看一下这个推断。

if (mAdjustViewBounds) {
                resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
                resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
               
                desiredAspect = (float) w / (float) h;
            }
  widthSpecMode假设不是指定大小的话。由于假设指定了固定大小就不须要又一次设置大小了。

然后接下来的推断也是基于 resizeWidth和resizeHeight 的值,假设不为true的情况下,会运行例如以下代码:

 w += pleft + pright;            h += ptop + pbottom;            w = Math.max(w, getSuggestedMinimumWidth());            h = Math.max(h, getSuggestedMinimumHeight());            widthSize = resolveSizeAndState(w, widthMeasureSpec, 0);            heightSize = resolveSizeAndState(h, heightMeasureSpec, 0);        }        setMeasuredDimension(widthSize, heightSize);
  考虑了填充,最后设置ImageView的大小。

  最后看一下onDraw()方法,

  @Override
 protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mDrawable == null) {
            return; // couldn't resolve the URI
        }

        if (mDrawableWidth == 0 || mDrawableHeight == 0) {
            return;     // nothing to draw (empty bounds)
        }

        if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) {
            mDrawable.draw(canvas);
        } else {
            int saveCount = canvas.getSaveCount();
            canvas.save();
           
            if (mCropToPadding) {
                final int scrollX = mScrollX;
                final int scrollY = mScrollY;
                canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
                        scrollX + mRight - mLeft - mPaddingRight,
                        scrollY + mBottom - mTop - mPaddingBottom);
            }
           
            canvas.translate(mPaddingLeft, mPaddingTop);

            if (mDrawMatrix != null) {
                canvas.concat(mDrawMatrix);
            }
            mDrawable.draw(canvas);
            canvas.restoreToCount(saveCount);
        }
    }
  在onDraw()方法中,实现方式比較简单,假设mDrawMatrix为空,那么就直接绘制出图片;假设不为空,那么还须要绘制矩阵。这就涉及到mDrawMatrix矩阵了。它是在哪赋值的呢,就是ScaleType。

这个是在configureBounds()方法中设置的,

private void configureBounds() {        if (mDrawable == null || !mHaveFrame) {            return;        }        int dwidth = mDrawableWidth;        int dheight = mDrawableHeight;        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;        int vheight = getHeight() - mPaddingTop - mPaddingBottom;        boolean fits = (dwidth < 0 || vwidth == dwidth) &&                       (dheight < 0 || vheight == dheight);        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {            /* If the drawable has no intrinsic size, or we're told to                scaletofit, then we just fill our entire view.            */            mDrawable.setBounds(0, 0, vwidth, vheight);            mDrawMatrix = null;        } else {            // We need to do the scaling ourself, so have the drawable            // use its native size.            mDrawable.setBounds(0, 0, dwidth, dheight);            if (ScaleType.MATRIX == mScaleType) {                // Use the specified matrix as-is.                if (mMatrix.isIdentity()) {                    mDrawMatrix = null;                } else {                    mDrawMatrix = mMatrix;                }            } else if (fits) {                // The bitmap fits exactly, no transform needed.                mDrawMatrix = null;            } else if (ScaleType.CENTER == mScaleType) {                // Center bitmap in view, no scaling.                mDrawMatrix = mMatrix;                mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f),                                         (int) ((vheight - dheight) * 0.5f + 0.5f));            } else if (ScaleType.CENTER_CROP == mScaleType) {                mDrawMatrix = mMatrix;                float scale;                float dx = 0, dy = 0;                if (dwidth * vheight > vwidth * dheight) {                    scale = (float) vheight / (float) dheight;                    dx = (vwidth - dwidth * scale) * 0.5f;                } else {                    scale = (float) vwidth / (float) dwidth;                    dy = (vheight - dheight * scale) * 0.5f;                }                mDrawMatrix.setScale(scale, scale);                mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));            } else if (ScaleType.CENTER_INSIDE == mScaleType) {                mDrawMatrix = mMatrix;                float scale;                float dx;                float dy;                               if (dwidth <= vwidth && dheight <= vheight) {                    scale = 1.0f;                } else {                    scale = Math.min((float) vwidth / (float) dwidth,                            (float) vheight / (float) dheight);                }                               dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);                dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);                mDrawMatrix.setScale(scale, scale);                mDrawMatrix.postTranslate(dx, dy);            } else {                // Generate the required transform.                mTempSrc.set(0, 0, dwidth, dheight);                mTempDst.set(0, 0, vwidth, vheight);                               mDrawMatrix = mMatrix;                mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));            }        }    }
  能够看到在if推断中,对各个ScaleType的类型都进行了推断。依据不同的ScaleType设置不同的矩阵mDrawMatrix。然后通过矩阵对图像进行变换,从而显示出不同的效果。

  除了这一点经常使用到之外,还有就是怎样设置图片资源了,有下面几个方法:setImageResource(int resId)、setImageURI(Uri uri)、setImageDrawable(Drawable drawable)、setImageBitmap(Bitmap bm)等。或者也能够在xml文件里设置。

可是这样直接使用会有一个隐形的弊端,假设显示的图片过多或者单张显示的图片像素过大,就easy出现OOM问题。因此就应该依据需求对图片进行预处理,经常用法有下面几种:

1、缩放、边界压缩
     在内存中载入图片时直接在内存中做处理。

关于图片压缩有非常多方法,这里仅仅是列举一个简单的样例,实际使用价值不大。如有需求能够自行參考其它资料。

  InputStream is = this.getResources().openRawResource(R.drawable.xx); 
     BitmapFactory.Options options=new BitmapFactory.Options(); 
     options.inJustDecodeBounds = false; 
     options.inSampleSize = 10;   //width。hight设为原来的十分一 
     Bitmap btp =BitmapFactory.decodeStream(is,null,options); 
2、直接调用JNI
     当使用像 imageView.setBackgroundResource,imageView.setImageResource, 或者 BitmapFactory.decodeResource 这种方法来设置一张大图片的时候,这些函数在完毕decode后,终于都是通过java层的createBitmap来完毕的,须要消耗很多其它内存。

  因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap。再将其设为ImageView的 source,decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完毕decode。无需再使用java层的createBitmap,从而节省了java层的空间。

假设在读取时加上图片的Config參数,能够跟有效降低载入的内存。从而跟有效阻止抛out of Memory异常。
  另外,须要特别注意:decodeStream是直接读取图片资料的字节码了, 不会依据机器的各种分辨率来自己主动适应。使用了decodeStream之后。须要在hdpi和mdpi,ldpi中配置对应的图片资源,否则在不同分辨率机器上都是相同大小(像素点数量)。显示出来的大小就不正确了。

  public static Bitmap readBitMap(Context context, int resId){       BitmapFactory.Options opt = new BitmapFactory.Options();           opt.inPreferredConfig = Bitmap.Config.RGB_565;          opt.inPurgeable = true;         opt.inInputShareable = true;         InputStream is = context.getResources().openRawResource(resId);          return BitmapFactory.decodeStream(is,null,opt);        }
3、手动收回占用资源
     尽管虚拟机会自己主动回收垃圾资源,可是有时候不是那么及时,这时候能够手动回收。

   if(!bmp.isRecycle() ){ 
         bmp.recycle()   //回收图片所占的内存 
         system.gc()  //提醒系统及时回收 
     } 
4、优化Dalvik虚拟机的堆内存分配
     使用 dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法能够增强程序堆内存的处理效率。
  private final static float TARGET_HEAP_UTILIZATION = 0.75f; 

     在程序onCreate时就能够调用

 VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION);

 就可以。

     除了 优化Dalvik虚拟机的堆内存分配 外,还能够强制定义自己软件的对内存大小。使用Dalvik提供的 dalvik.system.VMRuntime类来设置最小堆内存为例:Dalvik.VMRuntime类,提供对虚拟机全局,Dalvik的特定功能的接口。

Android为每一个程序分配的内存能够通过Runtime类的 totalMemory() 、freeMemory() 两个方法获取VM的一些内存信息。

private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ;
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //设置最小heap内存为6MB大小。

  
  下面解说一下怎样自己定义一个类继承于ImageView。首先以
CircleButton为例,这是github上一个项目,实现一个圆形有点击效果的按钮。例如以下:

Android ImageView分析并展开
   实现思路是这种。先画两个圆形图案,一个是实心的圆。一个是圆环。圆环半径小于实心圆半径。这样默认就看不到圆环,然后再画出设置的图片,覆盖在二者之上。

最后在按下的时候启动一个属性动画,将圆环放大显示,关于具体的分析能够看android-circlebutton介绍 这篇文章。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/117511.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Xmind快捷键(mac版和wind版本)

    Xmind快捷键(mac版和wind版本)https://www.xmind.net/m/ZQPm/

  • gateway网关详解_天翼网关扩展wifi

    gateway网关详解_天翼网关扩展wifi文章目录Gateway简介网关的功能搭建Gateway网关路由断言工厂路由过滤器全局过滤器过滤器执行顺序跨域问题处理Gateway简介Gateway是SpringCloud中的网关组件,SpringCloudGateway旨在提供一种简单而有效的方式来路由到API。SpringCloud在1.x版本中都是使用Zuul网关,但在2.x版本中使用Gateway替代了Zuul。Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的We

    2022年10月11日
  • dos下拷贝服务器文件命令行,win7在DOS环境下怎么使用copy命令?使用copy命令复制文件的方法…[通俗易懂]

    dos下拷贝服务器文件命令行,win7在DOS环境下怎么使用copy命令?使用copy命令复制文件的方法…[通俗易懂]win7系统在DOS环境下,如果想复制文件,该怎么操作呢?正常复制粘贴用不了,我们可以使用copy这条命令。copy命令复制文件占用系统资源会更少,复制速度也会略快。那么DOS环境下怎么使用copy命令?我们一起阅读下文教程吧。1、在DOS环境下,如果想复制一个文件副本,并且不对文件名进行更改,则可输入如下命令:copy源文件新文件位置比如说,我想将当前目录下的1.txt文件复制到…

  • centos配置yum源_linux配置离线yum源

    centos配置yum源_linux配置离线yum源执行yuminstall报错Error:Failedtodownloadmetadataforrepo‘appstream’:Cannotprepareinternalmirrorlist:NoURLsinmirrorlist排查:查看CentOS8所在服务器网络是否出现问题,可以用pingwww.baidu.com进行测试。网络没问题就看对应的软件源是否出现问题,具体排查/etc/yum.repos.d目录下这三个文件:CentOS-Ba

  • 孰能生巧

    孰能生巧

  • 常用的几款抓包工具[通俗易懂]

    常用的几款抓包工具[通俗易懂]常用的几款抓包工具!标签:软件测试软件测试方法软件测试学习原创来自于我们的微信公众号:软件测试大师最近很多同学,说面试的时候被问道,有没有用过什么抓包工具,其实抓包工具并没有什么很难的工具,只要你知道你要用抓包是干嘛的,就知道该怎么用了!一般对于测试而言,并不需要我们去做断点或者是调试代码什么的,只需要用一些抓包工具抓取发送给服务器的请求,观察下它的请求时间还有发送内容等等,…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号