美文网首页
Android 图片加载(三)图片加载框架Glide 进阶篇

Android 图片加载(三)图片加载框架Glide 进阶篇

作者: 怡红快绿 | 来源:发表于2019-06-19 17:12 被阅读0次

    原文链接:http://bumptech.github.io/glide/
    Github地址:https://github.com/bumptech/glide

    上一篇:Android 图片加载(二)图片加载框架Glide 入门篇


    一、变换

    在Glide中,Transformations 可以获取资源并修改它,然后返回被修改后的资源。通常变换操作是用来完成剪裁或对位图应用过滤器,但它也可以用于转换GIF动画,甚至自定义的资源类型。

    Glide 提供了很多内置的变换,包括:

    1. 默认变换

    我们可以使用多种方式实现变换:

    RequestOptions requestOptions = new RequestOptions()
            .centerCrop();  
    Glide.with(this)
            .load(IMAGE_URL)
            .apply(requestOptions)   //通过requestOptions设置变换
            .into(imageView);
    
    • RequestOptions类的静态方法
    Glide.with(this)
            .load(IMAGE_URL)
            .apply(RequestOptions.centerInsideTransform())  //静态方法设置变换
            .into(imageView);
    
    • 内联方法
    Glide.with(this)
            .load(IMAGE_URL)
            .fitCenter()   //内联方法设置变换
            .into(imageView);
    

    2. 多重变换

    默认情况下,每个 transform() 调用,或任何特定转换方法(fitCenter(), centerCrop(), bitmapTransform())的调用都会替换掉之前的变换。如果你想在单次加载中应用多个变换,请使用 MultiTransformation 类。

    Glide.with(this)
            .load(IMAGE_URL)
            .transform(new MultiTransformation<>(new CenterInside(), new Rotate(180)))  //设置多重变换
    //        .transform(new CenterCrop(), new Rotate(90))  //效果等同于new MultiTransformation<>()
            .into(imageView);
    

    MultiTransformation 构造器变换参数的顺序,决定了这些变换的应用顺序。

    3. 自定义变换

    尽管 Glide 提供了各种各样的内置 Transformation 实现,如果你需要额外的功能,你也可以实现你自己的Transformation

    如果你只需要变换 Bitmap,最好是从继承 BitmapTransformation 开始。BitmapTransformation 为我们处理了一些基础的东西,例如,如果你的变换返回了一个新修改的 Bitmap ,BitmapTransformation将负责提取和回收原始的 Bitmap。

    先看个简单的示例:

    public class MyBitmapTransformation extends BitmapTransformation {
    
        private static final String ID = "test.android.com.testapp.bitmap.MyBitmapTransformation";
        private static byte[] ID_BYTES = null;
    
        static {
            try {
                ID_BYTES = ID.getBytes(STRING_CHARSET_NAME);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
            if (toTransform.getHeight() == outHeight && toTransform.getWidth() == outWidth) {
                return toTransform;
            }
            return Bitmap.createScaledBitmap(toTransform, outWidth, outHeight, /*filter=*/ true);
        }
    
        @Override  
        public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
            messageDigest.update(ID_BYTES);
        }
    
        @Override  
        public boolean equals(@Nullable Object obj) {
            return obj instanceof MyBitmapTransformation;
        }
    
    
        @Override  
        public int hashCode() {
            return ID.hashCode();
        }
    }
    

    请特别注意,对于任何Transformation子类,包括BitmapTransformation,下面列出的三个方法你都必须实现,以使得磁盘和内存缓存正确地工作

    1. equals()
    2. hashCode()
    3. updateDiskCacheKey

    说明:即使你没有实现这三个方法,也能通过编译,但这可能导致自定义的Transformation无法正常工作。

    如果你的 Transformation 没有参数,通常使用一个包含完整包限定名的 static final String 来作为一个 ID,它可以构成 hashCode() 的基础,并可用于更新 updateDiskCacheKey() 传入的 MessageDigest。如果你的Transformation有参数而且它会影响到Bitmap被变换的方式,它们也必须被包含到这三个方法中。

    例如Glide提供的RoundedCorners变换,它含有一个圆角弧度参数roundingRadius,而且roundingRadius参与到了Bitmap的变换中,因此参数必须包含到三个方法中。RoundedCorners源码如下所示:

    /**
     * A {@link BitmapTransformation} which rounds the corners of a bitmap.
     */
    public final class RoundedCorners extends BitmapTransformation {
      private static final String ID = "com.bumptech.glide.load.resource.bitmap.RoundedCorners";
      private static final byte[] ID_BYTES = ID.getBytes(CHARSET);
    
      private final int roundingRadius;
    
      /**
       * @param roundingRadius the corner radius (in device-specific pixels).
       * @throws IllegalArgumentException if rounding radius is 0 or less.
       */
      public RoundedCorners(int roundingRadius) {
        Preconditions.checkArgument(roundingRadius > 0, "roundingRadius must be greater than 0.");
        this.roundingRadius = roundingRadius;
      }
    
      @Override
      protected Bitmap transform(
          @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
        return TransformationUtils.roundedCorners(pool, toTransform, roundingRadius);
      }
    
      @Override
      public boolean equals(Object o) {
        if (o instanceof RoundedCorners) {
          RoundedCorners other = (RoundedCorners) o;
          return roundingRadius == other.roundingRadius;
        }
        return false;
      }
    
      @Override
      public int hashCode() {
        return Util.hashCode(ID.hashCode(),
            Util.hashCode(roundingRadius));
      }
    
      @Override
      public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
        messageDigest.update(ID_BYTES);
    
        byte[] radiusData = ByteBuffer.allocate(4).putInt(roundingRadius).array();
        messageDigest.update(radiusData);
      }
    }
    

    二、自定义模块

    • 添加对Glide 的注解解析器的依赖和对OkHttp集成库的依赖
    implementation 'com.github.bumptech.glide:glide:4.9.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
    implementation 'com.github.bumptech.glide:okhttp3-integration:4.8.0'
    
    • 创建自定义模块
    1. 实现AppGlideModule 类
    2. 给上述实现添加@GlideModule注解。
    @GlideModule
    public class MyAppGlideModule extends AppGlideModule {
        @Override
        public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
            
        }
    
        @Override
        public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
            
        }
    }
    

    这样模块类就创建完成了。由于自定义模块类涉及的内容和应用场景比较复杂,可能会抽时间写一篇专门的文章详细介绍它,这里只是为接下来介绍【应用程序选项】做个简单的铺垫。

    三、应用程序选项

    Glide 允许应用通过模块类 AppGlideModule 实现来完全控制Glide 的内存和磁盘缓存使用。Glide 试图提供对大部分应用程序合理的默认选项,但对于部分应用,可能就需要定制这些值。在你做任何改变时,请注意测量其结果,避免出现性能的倒退。

    1. 内存缓存

    默认情况下,Glide使用 LruResourceCache ,这是 MemoryCache 接口的一个缺省实现,使用固定大小的内存和 LRU 算法。LruResourceCache的大小由 Glide 的 MemorySizeCalculator 类来决定,这个类主要关注设备的内存类型,设备 RAM 大小,以及屏幕分辨率。

    应用程序可以自定义MemoryCache的大小,具体是在它们的AppGlideModule中使用 applyOptions(Context, GlideBuilder) 方法配置MemorySizeCalculator

    @GlideModule
    public class MyAppGlideModule extends AppGlideModule {
        @Override
        public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
            //1.根据设备信息计算出合适的内存缓存大小
            MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
                    .setMemoryCacheScreens(2)
                    .build();
            builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
            //2.也可以直接覆盖缓存大小
            int memoryCacheSize = 1024*1024*15; //15M内存缓存
            builder.setMemoryCache(new LruResourceCache(memoryCacheSize));
            //3.提供自己的MemoryCache实现
            builder.setMemoryCache(new MemoryCache() {
                //省略方法实现
            });
        }
    }
    

    2. Bitmap池

    Glide 使用 LruBitmapPool 作为默认的 BitmapPoolLruBitmapPool是一个内存中的固定大小的 BitmapPool,使用 LRU 算法清理。默认大小基于设备的分辨率和密度,同时也考虑内存类和 isLowRamDevice 的返回值。具体的计算通过 Glide 的MemorySizeCalculator来完成,与 Glide 的MemoryCache的大小检测方法相似。

    BitmapPool配置方式与内存缓存相似:

    @GlideModule
    public class MyAppGlideModule extends AppGlideModule {
        @Override
        public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
            //1.根据设备信息计算出合适的BitmapPool缓存大小
            MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
                    .setBitmapPoolScreens(4)
                    .build();
            builder.setBitmapPool(new LruBitmapPool(calculator.getBitmapPoolSize()));
            //2.也可以直接覆盖BitmapPool大小
            int bitmapPoolSizeSize = 1024*1024*15; //15M内存缓存
            builder.setBitmapPool(new LruBitmapPool(bitmapPoolSizeSize));
            //3.提供自己的BitmapPool实现
            builder.setBitmapPool(new BitmapPool() {
                //省略方法实现
            });
        }
    }
    

    3. 磁盘缓存

    Glide 使用 DiskLruCacheWrapper 作为默认的 磁盘缓存DiskLruCacheWrapper是一个使用 LRU 算法的固定大小的磁盘缓存。默认磁盘大小为 250 MB,位置是在应用的缓存文件夹中的一个 特定目录。

    缓存位置可以是外部存储或者内部存储,我们还可以自己设置缓存大小、改变缓存文件夹在外存或内存上的名字:

    @GlideModule
    public class MyAppGlideModule extends AppGlideModule {
        @Override
        public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
            int diskCacheSize = 1024 * 1024 * 100;  //100MB 缓存大小
            String diskCacheFileName = "diskCacheFileName";  //缓存文件夹名称
            //外部存储
            builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context));
            builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context, diskCacheSize));
            builder.setDiskCache(new ExternalPreferredCacheDiskCacheFactory(context,diskCacheFileName, diskCacheSize));
            //内部存储
            builder.setDiskCache(new InternalCacheDiskCacheFactory(context));
            builder.setDiskCache(new InternalCacheDiskCacheFactory(context, diskCacheSize));
            builder.setDiskCache(new InternalCacheDiskCacheFactory(context,diskCacheFileName, diskCacheSize));
        }
    }
    

    应用程序还可以自行选择 DiskCache 接口的实现,并提供自己的 DiskCache.Factory 来创建缓存。Glide 使用一个工厂接口来在后台线程中打开磁盘缓存,这样方便缓存做诸如检查路径存在性等的IO操作而不用触发 严格模式

    @GlideModule
    public class MyAppGlideModule extends AppGlideModule {
        @Override
        public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
            builder.setDiskCache(new DiskCache.Factory() {
                @Nullable
                @Override
                public DiskCache build() {
                    return new DiskCache() {
                        //省略方法实现
                    };
                }
            });
        }
    }
    

    三、默认请求选项

    通常情况下请求选项 由每个请求来单独指定,但是我们也可以通过 AppGlideModule 配置默认请求选项以作用于你应用中启动的每个加载:

    @GlideModule
    public class MyAppGlideModule extends AppGlideModule {
        @Override
        public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
            RequestOptions defaultOptions = new RequestOptions()
                    .centerCrop()
                    .diskCacheStrategy(DiskCacheStrategy.NONE)
                    .skipMemoryCache(true);
            builder.setDefaultRequestOptions(defaultOptions);
        }
    }
    

    尽管你已经在AppGlideModule中配置了默认请求选项,任何单独请求里应用的选项还是会覆盖 GlideBuilder 里设置的冲突选项

    相关文章

      网友评论

          本文标题:Android 图片加载(三)图片加载框架Glide 进阶篇

          本文链接:https://www.haomeiwen.com/subject/qabhqctx.html