Android Glide 3.7.0 源码解析(七) , 细说图形变换和解码

9 篇文章 0 订阅
订阅专栏

个人博客传送门

一、概览

在这里插入图片描述

Glide 3.7.0 里面涉及的解码/图形变换主要是 大小缩放, CenterCrop , FitCenter, 其中大小缩放是基于Downsampler(解码工具) 实现的, 而剩余两个则是 Transformation(图形变换) 接口的两个实现类.
所以本文主要介绍3点:

  • CenterCrop
  • FitCenter
  • 大小缩放

Android Glide 3.7.0 源码解析 (二) , 从一次图片加载流程看源码文中的流程可以看出是在 DecodeJob 里面进行 解码 --> 图形变换 的
在这里插入图片描述
关于 fitCenter 和 centerCrop 的理解可以参考这篇文章的描述 Android ImageView 的scaleType 属性图解

按照惯例先介绍原理框架, 免得看源码时候迷路
在这里插入图片描述
解码流程详述

  • 读取图片的配置, width, height, config, orientation,
  • 根据传入的目标 targetWidth 和 targetHeight , 计算出来目标采样率, 就是缩放比例
  • 根据缩放比例开始解析原始图片流, 解析出缩放尺寸的图片
  • 根据方向 ( orientation ) 信息对图片进行矩阵变换, 翻转/旋转图片

解码的过程会伴随着大量对象池思想的使用, 关于对象池概念,参看 Android Glide 3.7.0 源码解析(四) , BitmapPool作用及原理

图形转换流程

  • 根据目标宽高计算出来合适的缩放比例和偏移量
  • 然后通过矩阵变换实现图形变换

二、解码

还记得 Android Glide 3.7.0 源码解析 (二) , 从一次图片加载流程看源码文中提到过 DownSampler 这个类是将原始图片资源流解析成图片, 我们的解码过程就是在这个类中进行的

// Downsampler
	public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
        final ByteArrayPool byteArrayPool = ByteArrayPool.get();
        final byte[] bytesForOptions = byteArrayPool.getBytes();
        final byte[] bytesForStream = byteArrayPool.getBytes();
        final BitmapFactory.Options options = getDefaultOptions();

        // Use to fix the mark limit to avoid allocating buffers that fit entire images.
        RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(is, bytesForStream);
        // Use to retrieve exceptions thrown while reading.
        // TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine
        // if a Bitmap is partially decoded, consider removing.
        ExceptionCatchingInputStream exceptionStream = ExceptionCatchingInputStream.obtain(bufferedStream);
        // Use to read data.
        // Ensures that we can always reset after reading an image header so that we can still attempt to decode the
        // full image even when the header decode fails and/or overflows our read buffer. See #283.
        MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
        try {
            exceptionStream.mark(MARK_POSITION);
            int orientation = 0;
            try {
                orientation = new ImageHeaderParser(exceptionStream).getOrientation();
            } catch (IOException e) {
                if (Log.isLoggable(TAG, Log.WARN)) {
                    Log.w(TAG, "Cannot determine the image orientation from header", e);
                }
            } finally {
                try {
                    exceptionStream.reset();
                } catch (IOException e) {
                    if (Log.isLoggable(TAG, Log.WARN)) {
                        Log.w(TAG, "Cannot reset the input stream", e);
                    }
                }
            }

            options.inTempStorage = bytesForOptions;

            final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
            final int inWidth = inDimens[0];
            final int inHeight = inDimens[1];

            final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
            final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);

            final Bitmap downsampled = downsampleWithSize(invalidatingStream, bufferedStream, options, 
            											  pool, inWidth, inHeight, sampleSize, decodeFormat);

            // BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
            // and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
            // we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
            final Exception streamException = exceptionStream.getException();
            if (streamException != null) {
                throw new RuntimeException(streamException);
            }

            Bitmap rotated = null;
            if (downsampled != null) {
                rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);

                if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
                    downsampled.recycle();
                }
            }

            return rotated;
        } finally {
            byteArrayPool.releaseBytes(bytesForOptions);
            byteArrayPool.releaseBytes(bytesForStream);
            exceptionStream.release();
            releaseOptions(options);
        }
    }

这个函数还蛮长的, 一个片段一个片段看

对象池狂魔

  • ByteArrayPool典型的对象池的实现, bytesForOptions 赋值给了options.inTempStorage, inTempStorage官方给的解释是解码的时候会用到的缓存, 这里用对象池管理回收,防止内存抖动, 看到此处回收的代码了吗 byteArrayPool.releaseBytes(bytesForOptions) ;
  • 同理 bytesForStream 也被安排了, RecyclableBufferedInputStream 看名称就很容易能猜到了, 我们知道在解析流的时候, 如果要求这个流可以回溯读取(读过的内容再读取一遍), 一般需要一个Buffer来缓存从流中读出的数据, 而这里就把这个 Buffer 抽象出来交给 ByteArrayPool 管理了
  • 再来看 ExceptionCatchingInputStream exceptionStream = ExceptionCatchingInputStream.obtain(bufferedStream); 这行代码, 是不是联想到 Message.obtain(), 没错这里也是个典型的对象池的概念, exceptionStream.release(); 在此处回收进入对象池, 这里就不详述了,感兴趣可以自行跟进 ExceptionCatchingInputStream 看看
  • 最后, 再来看 final BitmapFactory.Options options = getDefaultOptions();releaseOptions(options); 这一组, 也是一个对象池实现
  • 还没有结束 downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize, decodeFormat); 这行里面的 pool , 就是一个 BitmapPool , 是 Bitmap 的对象池

Android 3.0 之后可以将流中的图像数据解码在一个不用的已创建的 Bitmap 实例里面, 具体参见 Android Bitmap(一), 资源重用

读取图片配置

// DownSampler.decode

        RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(is, bytesForStream);
        ExceptionCatchingInputStream exceptionStream = ExceptionCatchingInputStream.obtain(bufferedStream);
        MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
        

先来三层 InputStream 包装 (设计模式: 装饰者模式),

  • 第一层 RecyclableBufferedInputStream 实现流的回溯功能(mark/reset), 暴露 Buffer 方便接入外面的对象池管理;
  • 第二层 ExceptionCatchingInputStream 担任异常处理功能;
  • 第三层 MarkEnforcingInputStream 这一层是为了防止读取图片头部属性等数据的时候读超了 mark 标记的位数, 之后就无法 reset 了

InputStream mark / reset 方法的解释
在这里插入图片描述
mark(int limit) 的作用是标记一段长度为limit的流, 使它可以被重新读取, 而 reset() 就是将当前的读取位置指向之前 mark() 的位置, 但当超限( 例如: readPos_2位置 )时就无法 reset() 了

  • 读取位置是 readPos_0 时 , mark(int limit) 标记当前读取流的位置
  • 读取位置是 readPos_1 时 , 调用 reset 回溯有效( readPos_1 <= limitPos ), 没有超出 limit 的限制, 会回到 markPos 再读一遍流
  • 读取位置是 readPos_2 时, 调用 reset 无效 (readPos_1 > limitPos ), 超出 limit 限制

关于 RecyclableBufferedInputStream 如何实现 mark 和 reset 方法的, 参考 Android Glide 3.7.0 源码解析(八) , RecyclableBufferedInputStream 的 mark/reset 实现

// DownSampler.decode

	try {
            exceptionStream.mark(MARK_POSITION);
            int orientation = 0;
            try {
                orientation = new ImageHeaderParser(exceptionStream).getOrientation();
            } catch (IOException e) {
                if (Log.isLoggable(TAG, Log.WARN)) {
                    Log.w(TAG, "Cannot determine the image orientation from header", e);
                }
            } finally {
                try {
                    exceptionStream.reset();
                } catch (IOException e) {
                    if (Log.isLoggable(TAG, Log.WARN)) {
                        Log.w(TAG, "Cannot reset the input stream", e);
                    }
                }
            }
            ...
     }

紧接着就用到了 mark / reset 功能 , 读取头部信息里面存储的方向信息

关于 orientation 值代表的详细含义参考这篇文章: EXIF 方向参数 Orientation

// DownSampler.decode

	final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
    final int inWidth = inDimens[0];
    final int inHeight = inDimens[1];

	public int[] getDimensions(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
            BitmapFactory.Options options) {
        options.inJustDecodeBounds = true;
        decodeStream(is, bufferedStream, options);
        options.inJustDecodeBounds = false;
        return new int[] { options.outWidth, options.outHeight };
    }

这里获取了待解析图片的宽高

计算缩放比例

// DownSampler.decode

	// 计算图片被旋转的角度
	final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
    final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
     
    private int getRoundedSampleSize(int degreesToRotate, int inWidth, int inHeight, int outWidth, int outHeight) {
        int targetHeight = outHeight == Target.SIZE_ORIGINAL ? inHeight : outHeight;
        int targetWidth = outWidth == Target.SIZE_ORIGINAL ? inWidth : outWidth;

        final int exactSampleSize;
        if (degreesToRotate == 90 || degreesToRotate == 270) {
            // 90 和 270 度 需要把长宽对调来计算缩放比例
            exactSampleSize = getSampleSize(inHeight, inWidth, targetWidth, targetHeight);
        } else {
            exactSampleSize = getSampleSize(inWidth, inHeight, targetWidth, targetHeight);
        }

        // 去一个最大的 且  <= exactSampleSize 且 是2的次方
        final int powerOfTwoSampleSize = exactSampleSize == 0 ? 0 :
                Integer.highestOneBit(exactSampleSize);

        // powerOfTwoSampleSize == 0 代表不缩放,也就是返回 1 倍
        return Math.max(1, powerOfTwoSampleSize);
    }
    
    public static final Downsampler AT_LEAST = new Downsampler() {
        @Override
        protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight) {
        	// 按照目标的长宽比判定 至少需要缩放多少倍
            return Math.min(inHeight / outHeight, inWidth / outWidth);
        }

        @Override
        public String getId() {
            return "AT_LEAST.com.bumptech.glide.load.data.bitmap";
        }
    };
    
    public static final Downsampler AT_MOST = new Downsampler() {
        @Override
        protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight) {
        	// 按照目标的长宽比判定 至多需要缩放多少倍
            int maxIntegerFactor = (int) Math.ceil(Math.max(inHeight / (float) outHeight,
                inWidth / (float) outWidth));
            int lesserOrEqualSampleSize = Math.max(1, Integer.highestOneBit(maxIntegerFactor));
            return lesserOrEqualSampleSize << (lesserOrEqualSampleSize < maxIntegerFactor ? 1 : 0);
        }

        @Override
        public String getId() {
            return "AT_MOST.com.bumptech.glide.load.data.bitmap";
        }
    };

先根据目标宽高算出整数的缩放比例, 有两种计算方式(但其实查看 3.7.0的代码, 只用到了 AT_LEAST)

  • AT_LEAST 取 sampleSize 的最小值, 意思是: 至少需要缩放多少倍
  • AT_MOST 取 sampleSize 的最大值 (而且还是 ceil 的方式向上取整) 意思是最多需要缩放多少倍

这步计算我们拿到的 exactSampleSize 却不是最终的 sampleSize, 官文里面有提到, sampleSize 需要是 2 的整数次方 且 大于一, 所以我们需要在exactSampleSize 范围内找一个最大的满足 2 的整数次方的最终 sampleSize , 并且与 1 进行比较

sampleSize == 4 代表缩小 4 倍
在这里插入图片描述

缩放比例就计算完了, 下一步

解析原图为对应缩放比例

// DownSampler.decode

	final Bitmap downsampled = downsampleWithSize(invalidatingStream, bufferedStream, 
												options, pool, inWidth, inHeight,
												sampleSize,decodeFormat);

	private Bitmap downsampleWithSize(MarkEnforcingInputStream is, RecyclableBufferedInputStream  bufferedStream,
            BitmapFactory.Options options, BitmapPool pool, int inWidth, int inHeight, int sampleSize,
            DecodeFormat decodeFormat) {
		
		// 读取 config
        Bitmap.Config config = getConfig(is, decodeFormat);
        // 初始化 options
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = config;

		// 这里利用 BitmapPool 对象池 和 Bitmap 的重用机制, 做了一个Bitmap内存重用的东东
        if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
            int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
            int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
            setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
        }
        // 开始解析
        return decodeStream(is, bufferedStream, options);
    }

	private static void setInBitmap(BitmapFactory.Options options, Bitmap recycled) {
        if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) {
        	// 给 options 的 inBitmap 字段赋值, 可以将原始图片资源解析到一个不用的 Bitmap 对象中去
            options.inBitmap = recycled;
        }
	}

	private static Bitmap decodeStream(MarkEnforcingInputStream is, RecyclableBufferedInputStream bufferedStream,
            BitmapFactory.Options options) {
        ...
        final Bitmap result = BitmapFactory.decodeStream(is, null, options);
		...

        return result;
    }
    
  • 读取 config
  • 把 config 和之前算好的 sampleSize 赋值给 options
  • 给 options 的 inBitmap 字段赋值, 可以将原始图片资源解析到一个不用的 Bitmap 对象中去
  • 利用 Bitmap 的资源重用机制完成对原始图片的解码操作

关于Bitmap的重用机制可以参考, Android Bitmap(一), 资源重用
关于对象池的概念可以参考, Android Glide 3.7.0 源码解析(四) , BitmapPool作用及原理

解码过程分析完毕, 接下来的图形转换就很简单了, 一共就两个函数

三、图形转换 fitCenter

public class FitCenter extends BitmapTransformation {

    public FitCenter(Context context) {
        super(context);
    }

    public FitCenter(BitmapPool bitmapPool) {
        super(bitmapPool);
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
    	// 关键代码在这里
        return TransformationUtils.fitCenter(toTransform, pool, outWidth, outHeight);
    }

    @Override
    public String getId() {
        return "FitCenter.com.bumptech.glide.load.resource.bitmap";
    }
}

// TransformationUtils
	
	public static Bitmap fitCenter(Bitmap toFit, BitmapPool pool, int width, int height) {
        if (toFit.getWidth() == width && toFit.getHeight() == height) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "requested target size matches input, returning input");
            }
            return toFit;
        }
		
		// 计算缩放比例 2 代表放大 2 倍, 这里去最小值, 意思是保证能放的进去 ImageView 控件
        final float widthPercentage = width / (float) toFit.getWidth();
        final float heightPercentage = height / (float) toFit.getHeight();
        final float minPercentage = Math.min(widthPercentage, heightPercentage);

        
        final int targetWidth = (int) (minPercentage * toFit.getWidth());
        final int targetHeight = (int) (minPercentage * toFit.getHeight());

        if (toFit.getWidth() == targetWidth && toFit.getHeight() == targetHeight) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "adjusted target size matches input, returning input");
            }
            return toFit;
        }

		// 对象池获取一个旧的大小匹配的
        Bitmap.Config config = getSafeConfig(toFit);
        Bitmap toReuse = pool.get(targetWidth, targetHeight, config);
        if (toReuse == null) {
            toReuse = Bitmap.createBitmap(targetWidth, targetHeight, config);
        }
        // 设置透明属性, 官文描述在某些情况下可以提升Bitmap的绘制速度
        TransformationUtils.setAlpha(toFit, toReuse);

		// 矩阵变换控制缩放
        Canvas canvas = new Canvas(toReuse);
        Matrix matrix = new Matrix();
        matrix.setScale(minPercentage, minPercentage);
        Paint paint = new Paint(PAINT_FLAGS);
        canvas.drawBitmap(toFit, matrix, paint);

        return toReuse;
    }

以上源码,所见即所得,非常简单, 需要注意的是

  • 缩放比例选取最小值, 是为的能放的进去界面组件, 因为是FitCenter
  • TransformationUtils.setAlpha 设置是否包含透明像素的标志位, 某些情况下可以提升绘制速度,查看官文描述如下

在这里插入图片描述

四、图形转换 centerCrop

// CenterCrop

	protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig() != null
                ? toTransform.getConfig() : Bitmap.Config.ARGB_8888);
        // 还是在 TransformationUtils 中进行处理
        Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);
        if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
            toReuse.recycle();
        }
        return transformed;
    }

	public static Bitmap centerCrop(Bitmap recycled, Bitmap toCrop, int width, int height) {
        if (toCrop == null) {
            return null;
        } else if (toCrop.getWidth() == width && toCrop.getHeight() == height) {
            return toCrop;
        }
        
        final float scale;
        float dx = 0, dy = 0;
        Matrix m = new Matrix();
        // 这个算式改成除法比较好理解 toCrop.getWidth()/width > toCrop.getHeight()/height, 结合 CenterCrop 的属性理解
        if (toCrop.getWidth() * height > width * toCrop.getHeight()) {
        	// 宽度超限了,需要对宽度进行裁剪
        	// 按照高度比例进行缩放
            scale = (float) height / (float) toCrop.getHeight();
            dx = (width - toCrop.getWidth() * scale) * 0.5f;
        } else {
        	// 高度超限了,需要对高度进行裁剪
        	// 按照宽度比例进行缩放
            scale = (float) width / (float) toCrop.getWidth();
            dy = (height - toCrop.getHeight() * scale) * 0.5f;
        }

		// 先缩放
        m.setScale(scale, scale);
        // 再平移
        m.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));

		
		// Bitmap重用机制
        final Bitmap result;
        if (recycled != null) {
            result = recycled;
        } else {
            result = Bitmap.createBitmap(width, height, getSafeConfig(toCrop));
        }

        // 同前文, 提速用的
        TransformationUtils.setAlpha(toCrop, result);

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint(PAINT_FLAGS);
        // 绘制到 canvas 上
        canvas.drawBitmap(toCrop, m, paint);
        return result;
    }

代码比较简单,就是通过缩放和平移,摆好位置后,直接绘制到新的 Bitmap 上, 唯一需要注意的是toCrop.getWidth()/width > toCrop.getHeight()/height会选取一个比例小的按比例缩放, 把比例大的哪个平移裁剪掉

Android图片框架Glide-3.7.0(最新,很强大)
03-31
Android图片框架Glide-3.7.0(最新,很强大),超好用的图片框架(包含jar和源码Glide 是一个高效、开源、 Android设备上的媒体管理框架,它遵循BSD、MIT以及Apache 2.0协议发布。Glide具有获取、解码和展示视频剧照、图片、动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里。创建Glide的主要目的有两个,一个是实现平滑的图片列表滚动效果,另一个是支持远程图片的获取、大小调整和展示。近日,Glide 3.0发布,现已提供 jar包下载 ,同时还支持使用Gradle以及Maven进行构建。该版本包括很多值得关注的新功能,如支持Gif 动画和视频剧照解码、智能的暂停和重新开始请求、支持缩略图等,具体新增功能如下如下: GIF 动画的解码 :通过调用Glide.with(context).load(“图片路径“)方法,GIF动画图片可以自动显示为动画效果。如果想有更多的控制,还可以使用Glide.with(context).load(“图片路径“).asBitmap()方法加载静态图片使用Glide.with(context).load(“图片路径“).asGif()方法加载动画图片 本地视频剧照的解码: 通过调用Glide.with(context).load(“图片路径“)方法,Glide能够支持Android设备中的所有视频剧照的加载和展示 缩略图的支持: 为了减少在同一个view组件里同时加载多张图片的时间,可以调用Glide.with(context).load(“图片路径“).thumbnail(“缩略比例“).into(“view组件“)方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图 Activity 生命周期的集成: 当Activity暂停和重启时,Glide能够做到智能的暂停和重新开始请求,并且当Android设备的连接状态变化时,所有失败的请求能够自动重新请求 转码的支持: Glide的toBytes() 和transcode() 两个方法可以用来获取、解码变换背景图片,并且transcode() 方法还能够改变图片的样式 动画的支持: 新增支持图片的淡入淡出动画效果(调用crossFade()方法)和查看动画的属性的功能 OkHttp 和Volley 的支持: 默认选择HttpUrlConnection作为网络协议栈,还可以选择OkHttp和Volley作为网络协议栈 其他功能: 如在图片加载过程中,使用Drawables对象作为占位符、图片请求的优化、图片的宽度和高度可重新设定、缩略图和原图的缓存等功能
glide-3.7.0
11-24
glide3.7.0jar,Glide,是Android中一个图片加载开源库
推荐文章:GlideWebpDecoder - 让Android平台轻松拥抱WebP图像的利器
最新发布
gitblog_00028的博客
08-22 753
推荐文章:GlideWebpDecoder - 让Android平台轻松拥抱WebP图像的利器 GlideWebpDecoderA Glide WebpDecoder Intergration Library for decoding and displaying webp images项目地址:https://gitcode.com/gh_mirrors/gl/GlideWebpDecoder...
Android Glide 3.7.0 源码解析(六) , 缓存结构详述
πππ
03-27 245
结构总览 内存缓存是由 LruResourceCache 和 activeResources 组成, 缓存的是 EngineResource 类型 第一级缓存: LruResourceCache 是一个最终是一个 LinkedHashMap 来实现 Lru , 存储的是没有被界面使用的缓存资源, 并由LRU控制缓存大小 第二级缓存: activeResources 是由一个 Map<Key, WeakReference<EngineResource<?>>> 构成, 存
glide-3.7.0.jar
02-17
Glide 一个专注于平滑滚动的图片加载和缓存库
glide 3.7.0源码
05-15
在深入探讨Glide 3.7.0的源码之前,我们需要理解Glide的基本工作原理和核心概念。 Glide的核心在于其模块化的架构,主要包括以下几个关键组件: 1. **RequestManager**: 这是Glide的入口点,负责初始化和协调请求...
Android图片框架Glide-3.7.0(最新,很强大),超好用的图片框架(包含jar和源码
11-12
Android图片框架Glide-3.7.0(最新,很强大),超好用的图片框架(包含jar和源码[注:本内容来自网络,在此分享仅为帮助有需要的网友,如果侵犯了您的权利,麻烦联系我,我会第一时间删除,谢谢您。]
Glide3.7.0 jar包啊啊啊啊啊啊啊啊啊.zip
06-10
Android应用开发中,正确地集成和使用Glide3.7.0可以帮助优化图像资源的处理,提高应用性能,并减少内存消耗。 Glide的核心功能包括: 1. **图片加载**:Glide通过URL或资源ID加载图片,支持本地文件系统、网络...
Glide3.7.0源码详解
bluerheaven的博客
01-20 515
基于的Glide版本:3.7.0 本文分析的是Glide最基本的一行代码: Glide.with(this).load(url).into(imageView); 我们认准这一个功能点,来看看Glide默默为我们做了什么。这篇文章会分析这行代码背后的代码,也会简单地分析代码用到的框架和设计模式。 这行代码可很容易分成三个部分:with、load、into,我们也降分为三个分类来分析。首先来...
Glide 3.7源码及jar包
08-05
包含glide的最新jar包 及jar包源码
Android Glide 3.7.0 源码解析(一), 准备工作
πππ
03-08 138
Android Glide 3.7.0 源码详解 (一) , 准备工作 Android Glide 3.7.0 源码解析 (二) , 从一次图片加载流程看源码 Android Glide 3.7.0 源码解析(三) , 生命周期绑定 Android Glide 3.7.0 源码解析(四) , BitmapPool作用及原理 Android Glide 3.7.0 源码解析(五) , 如何获得ImageView的宽高
Android框架之美Glide3.0
厚积薄发,持之以恒
05-12 636
Android框架之美Glide3.0 Glide是一个非常迅速和高效的媒体流及图片导入的Android框架,包涵媒体解流,内存和磁盘缓存等。并将其集成到一个非常简单易用的接口中。 Glide支持抓取、解码和显示视频照片,图片和动画gif(可以将gif图片自动转换为动画)。它包含一个灵活的API使开发者可以迅速在网络堆栈使用使用方法1.导入Glide1.1可以导入jar包https:/
Glide 3.7.0 本地构建
xmt328的博客
03-30 803
Glide 3.7.0构建
Android Glide 3.7.0 源码解析(二), 从一次图片加载流程看源码
πππ
03-10 249
Glide.with(activity).load(url).into(imageView); Glide.with 方法, 创建 RequestManager 实例 RequestManager.load 方法, 创建 GenericRequestBuilder 实例, 并打包编/解码, 转码, 图形转换, 下载等工具 解码: File, InputStream 转换成 Bitmap, Drawable 编码: 将数据写入缓存区
GlideDemo【Glide3.7.0版本的简单使用以及圆角功能】
weixin_34209851的博客
08-04 649
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 本Demo主要记录Glide3.7.0版本的简单运用和实现圆角方案。 效果图 代码分析 Glide的centerCrop()和fitCenter()的使用效果: Glide 提供了两个类似的方法 CenterCrop() 和 FitCenter(),CenterCrop() 方法是将图片按比例缩放到足矣填充 Ima...
"Glide源码解析Android图片加载框架的执行流程及API简析
Glide会根据我们传递的参数,执行一系列的操作,包括网络请求、图片解码、内存和磁盘缓存等,最终将图片展示在界面上。 总结起来,Glide是一款功能强大且简单易用的图片加载框架,通过几行简单的代码就可以实现图片...
写文章

热门文章

  • github 提速方案, 亲测有效! 1026
  • Android Glide 3.7.0 源码解析(四), BitmapPool作用及原理 640
  • Android Glide 3.7.0 源码解析(八) , RecyclableBufferedInputStream 的 mark/reset 实现 452
  • Android Glide 3.7.0 源码解析(九) , gif 的加载实现 420
  • Android Glide 3.7.0 源码解析(七) , 细说图形变换和解码 394

分类专栏

  • 数据结构 3篇
  • Glide 3.7.0 源码解析 9篇
  • 内存管理 1篇
  • Case 1篇
  • Android 环境搭建

大家在看

  • Linux下操作文件时提示:Operation not permitted
  • 在企业开发中的并行计算与异步UI更新方式
  • CSP-J1 CSP-S1 初赛 第1轮 攻略等
  • 基于字符的图片验证码识别算法的设计与实现 1344
  • 数字业务转型的核心指南:如何从理论框架到企业实现数字化转型的成功路径 1573

最新文章

  • 数据结构(三), 弄懂红黑树(多图警告!!!)
  • 数据结构(二), AVL平衡二叉树
  • 数据结构(一), 二叉查找树BST
2021年14篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

玻璃钢生产厂家成都不锈钢玻璃钢雕塑厂家玻璃钢雕塑产品不合格品描述合肥创意玻璃钢雕塑哪家便宜冯国豪雕塑玻璃钢图片濮阳玻璃钢雕塑订做西双版纳玻璃钢雕塑批发商九江市玻璃钢雕塑渑池玻璃钢雕塑白银城市玻璃钢雕塑制作宣城大型户外玻璃钢雕塑报价武威卡通玻璃钢雕塑厂家镇江椭圆形玻璃钢花盆山东公园玻璃钢雕塑制作曲靖玻璃钢花盆厂家直销增城玻璃钢造型雕塑浙江大型商场创意商业美陈创作商场灯光美陈定制工厂贵州省专业玻璃钢雕塑厂家淮北公园玻璃钢雕塑订做价格昌吉园林玻璃钢雕塑洛阳玻璃钢雕塑图片五一商场美陈方案玻璃钢雕塑容易损坏吗玻璃钢雕塑怎么不开裂大型玻璃钢雕塑哪个好水果玻璃钢雕塑定制广东定制玻璃钢雕塑南湖红船玻璃钢雕塑模型淮南多彩玻璃钢雕塑订做价格玻璃钢雕塑制作步骤香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化