详解Glide4.0集成及使用注意事项

Glide 4.0由Google的各种团队内部使用,4.0被认为是内部稳定的。但外部用户可能会发现内部尚未发现的问题。因此,将此作为RC发布。如果没有发现稳定性或API中的重大问题,预计不久之后就会发布非RC版本。

一、集成

1、project gradle

 repositories {
    mavenLocal()
 }

2、app gradle

compile 'com.android.support:support-v4:25.3.1'
compile 'com.github.bumptech.glide:glide:4.0.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'

3、混淆

#glide4.0
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
 **[] $VALUES;
 public *;
}

# for DexGuard only
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule

# 从glide4.0开始,GifDrawable没有提供getDecoder()方法,
# 需要通过反射获取gifDecoder字段值,所以需要保持GifFrameLoader和GifState类不被混淆
-keep class com.bumptech.glide.load.resource.gif.GifDrawable$GifState{*;}
-keep class com.bumptech.glide.load.resource.gif.GifFrameLoader {*;}

4、在4.0中不用像3.X需要在AndroidManifest.xml配置GlideModule,而是通过注解继承AppGlideModule的子类来配置。

@GlideModule
public class GlideConfiguration extends AppGlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    //自定义缓存目录,磁盘缓存给150M 另外一种设置缓存方式
    builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "GlideImgCache", 150 * 1024 * 1024));
    //配置图片缓存格式 默认格式为8888
    builder.setDefaultRequestOptions(RequestOptions.formatOf(DecodeFormat.PREFER_ARGB_8888));
    ViewTarget.setTagId(R.id.glide_tag_id);
  }

  /**
   * 禁止解析Manifest文件
   * 主要针对V3升级到v4的用户,可以提升初始化速度,避免一些潜在错误
   * @return
   */
  @Override
  public boolean isManifestParsingEnabled() {
    return false;
  }

}

二、使用注意事项

1、使用GlideApp代替Glide,asBitmap、asGif、asDrawable、asFile都要放到load之前(glide3.7.0都是要在load之后调用)。

public static void loadImg(Context context,String url, ImageView imageView){
    GlideApp.with(context)
        .asBitmap()
        .load(url)
        .placeholder(R.drawable.placeholder) //设置资源加载过程中的占位符
        .into(imageView);
  }

2、占位符.placeholder(R.drawable.placeholder)不能用.9图,占位图片和加载的目标图片会同时显示,只是目标图片会先显示缩略图,然后显示正常。fallback和error还没测试过,有兴趣的可以测试看看。

3、加载gif图时,若调用dontAnimate()移除所有动画,gif就会加载失败。

4、计算gif播放一次的动画时长。

glide 3.7.0你可以这样获取动画时长:

 public void loadGif(Context context,ImageView mIvGif,int url){
    Glide.with(context)
        .load(url)
        .listener(new RequestListener<Integer, GlideDrawable>() {
          @Override
          public boolean onException(Exception e, Integer model, Target<GlideDrawable> target, boolean isFirstResource) {
            return false;
          }

          @Override
          public boolean onResourceReady(GlideDrawable resource, Integer model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
            try {
              int duration = 0;
              GifDrawable gifDrawable = (GifDrawable) resource;
              GifDecoder decoder = gifDrawable.getDecoder();
              for (int i = 0; i < gifDrawable.getFrameCount(); i++) {
                duration += decoder.getDelay(i);
              }
              Log.e("Glide3.7.0","gif播放一次动画时长duration:"+duration);
            } catch (Throwable e) {
            }
            return false;
          }
        })
        .into(new GlideDrawableImageViewTarget(mIvGif, 1));
  }

glide4.0中GifDrawable没有提供getDecoder()方法并且还去掉了decoder这个成员变量。除此之外,glide4.0还去掉了GlideDrawableImageViewTarget类,那我们该如何来计算gif播放一次的时长呢?只能从源码中找答案了。

(1)寻找decoder

glide3.7.0 GifDrawable中我们可以发现decoder最终于会传入GifFrameLoader类中并赋值给gifDecoder变量。

//源码
//glide 3.7.0 GifDrawable.java
 GifDrawable(GifState state) {
    if (state == null) {
      throw new NullPointerException("GifState must not be null");
    }

    this.state = state;
    this.decoder = new GifDecoder(state.bitmapProvider);
    this.paint = new Paint();
    decoder.setData(state.gifHeader, state.data);
    frameLoader = new GifFrameLoader(state.context, this, decoder, state.targetWidth, state.targetHeight);
    frameLoader.setFrameTransformation(state.frameTransformation);
  }
/*---------------------------------------------------------------------------------------------------*/
//glide 3.7.0 GifFrameLoader.java
private final GifDecoder gifDecoder;//私有属性
public GifFrameLoader(Context context, FrameCallback callback, GifDecoder gifDecoder, int width, int height) {
    this(callback, gifDecoder, null,
        getRequestBuilder(context, gifDecoder, width, height, Glide.get(context).getBitmapPool()));
 }

  GifFrameLoader(FrameCallback callback, GifDecoder gifDecoder, Handler handler,
      GenericRequestBuilder<GifDecoder, GifDecoder, Bitmap, Bitmap> requestBuilder) {
    if (handler == null) {
      handler = new Handler(Looper.getMainLooper(), new FrameLoaderCallback());
    }
    this.callback = callback;
    //看这里
    this.gifDecoder = gifDecoder;
    this.handler = handler;
    this.requestBuilder = requestBuilder;
  }

glide4.0 GifDrawable类的构造中我们可以看到有一个gifDecoder的参数,这个参数的解释是解码器用于解码GIF数据(The decoder to use to decode GIF data)。继续看这个构造,发现gifDecoder最终也是被传到GifFrameLoader类中并赋值给gifDecoder变量。所以glide3.7.0中的decoder其实就是4.0中的gifDecoder。

//源码
//glide 4.0 GifDrawable.java
private final GifState state;
/*
  * @param gifDecoder     The decoder to use to decode GIF data.
  * @param firstFrame     The decoded and transformed first frame of this GIF.
  * @see #setFrameTransformation(com.bumptech.glide.load.Transformation, android.graphics.Bitmap)
  */
 public GifDrawable(Context context, GifDecoder gifDecoder, BitmapPool bitmapPool,
   Transformation<Bitmap> frameTransformation, int targetFrameWidth, int targetFrameHeight,
   Bitmap firstFrame) {
  this(
    new GifState(
      bitmapPool,
      new GifFrameLoader(
        // TODO(b/27524013): Factor out this call to Glide.get()
        Glide.get(context),
        gifDecoder,
        targetFrameWidth,
        targetFrameHeight,
        frameTransformation,
        firstFrame)));
 }
/*---------------------------------------------------------------------------------------------*/
//glide4.0 GifFrameLoader.java
private final GifDecoder gifDecoder;//私有属性
 public GifFrameLoader(
   Glide glide,
   GifDecoder gifDecoder,
   int width,
   int height,
   Transformation<Bitmap> transformation,
   Bitmap firstFrame) {
  this(
    glide.getBitmapPool(),
    Glide.with(glide.getContext()),
    gifDecoder,
    null /*handler*/,
    getRequestBuilder(Glide.with(glide.getContext()), width, height),
    transformation,
    firstFrame);
 }

 @SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
 GifFrameLoader(
   BitmapPool bitmapPool,
   RequestManager requestManager,
   GifDecoder gifDecoder,
   Handler handler,
   RequestBuilder<Bitmap> requestBuilder,
   Transformation<Bitmap> transformation,
   Bitmap firstFrame) {
  this.requestManager = requestManager;
  if (handler == null) {
   handler = new Handler(Looper.getMainLooper(), new FrameLoaderCallback());
  }
  this.bitmapPool = bitmapPool;
  this.handler = handler;
  this.requestBuilder = requestBuilder;

  //看这里
  this.gifDecoder = gifDecoder;

  setFrameTransformation(transformation, firstFrame);
 }

(2)获取decoder

从上面Glide4.0的GifDrawable构造中可以看到gifDecoder被传递到GifFrameLoader中赋值给私有属性gifDecoder;,而GifFrameLoader又被传入GifState中并被赋值给它的成员变量frameLoader,那要怎么获取frameLoader?

从源码中,可以看到GifDrawable提供了getConstantState()方法来获取state变量(这个变量的类型就是GifState),但是GifState并没有get方法获取frameLoader,frameLoader对象中的gifDecoder也是私有,也没有提供get方法来获取,那么我们只能通过反射来获取了。

//源码
//glide4.0 GifDrawable.java
private final GifState state;
 @Override
 public ConstantState getConstantState() {
  return state;
 }
 static class GifState extends ConstantState {
  static final int GRAVITY = Gravity.FILL;
  final BitmapPool bitmapPool;
  final GifFrameLoader frameLoader;

  public GifState(BitmapPool bitmapPool, GifFrameLoader frameLoader) {
   this.bitmapPool = bitmapPool;
   this.frameLoader = frameLoader;
  }

  @Override
  public Drawable newDrawable(Resources res) {
   return newDrawable();
  }

  @Override
  public Drawable newDrawable() {
   return new GifDrawable(this);
  }

  @Override
  public int getChangingConfigurations() {
   return 0;
  }
 }

通过反射来获取获取decoder

.listener(new RequestListener<GifDrawable>() {
          @Override
          public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<GifDrawable> target, boolean isFirstResource) {
            return false;
          }

          @Override
          public boolean onResourceReady(GifDrawable gifDrawable, Object model, Target<GifDrawable> target, DataSource dataSource, boolean isFirstResource) {
            try {
              int duration = 0;
              // 计算动画时长
              //GifDecoder decoder = gifDrawable.getDecoder();//4.0开始没有这个方法了
              Drawable.ConstantState state = gifDrawable.getConstantState();
              if(state!=null){
                //不能混淆GifFrameLoader和GifState类
                Object gifFrameLoader = getValue(state,"frameLoader");
                if(gifFrameLoader!=null){
                  Object decoder = getValue(gifFrameLoader,"gifDecoder");
                  if(decoder!=null && decoder instanceof GifDecoder){
                    for (int i = 0; i < gifDrawable.getFrameCount(); i++) {
                      duration += ((GifDecoder) decoder).getDelay(i);
                    }
                  }
                }
                Log.e("Glide4.0","gif播放动画时长duration:"+duration);
              }
            } catch (Throwable e) {
            }
            return false;
          }
        })

/*---------------------------------------------------------------------------------------------*/
/**
   * 通过字段名从对象或对象的父类中得到字段的值
   * @param object 对象实例
   * @param fieldName 字段名
   * @return 字段对应的值
   * @throws Exception
   */
  public static Object getValue(Object object, String fieldName) throws Exception {
    if (object == null) {
      return null;
    }
    if (TextUtils.isEmpty(fieldName)) {
      return null;
    }
    Field field = null;
    Class<?> clazz = object.getClass();
    for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
      try {
        field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(object);
      } catch (Exception e) {
        //这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。
        //如果这里的异常打印或者往外抛,则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
      }
    }

    return null;
  }

(3)设置gif循环播放次数

glide4.0中没有GlideDrawableImageViewTarget类,那么怎么设置循环播放次数呢?

从glide3.7.0源码可以发现GlideDrawableImageViewTarget是通过GlideDrawable的setLoopCount方法来设置循环播放次数的,查看setLoopCount具体实现地方是在GifDrawable,所以这里调用的其实是GifDrawable的setLoopCount方法。glide4.0中没有GlideDrawable类和GlideDrawableImageViewTarget类,但是仍然有GifDrawable类,并且onResourceReady方法中第一个参数就是GifDrawable,所以可以直接调用GifDrawable的setLoopCount(loopCount)来设置播放次数。

//源码
//3.7.0 GlideDrawableImageViewTarget.java
 public GlideDrawableImageViewTarget(ImageView view, int maxLoopCount) {
    super(view);
    this.maxLoopCount = maxLoopCount;
 }
  @Override
  public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
    if (!resource.isAnimated()) {
      //TODO: Try to generalize this to other sizes/shapes.
      // This is a dirty hack that tries to make loading square thumbnails and then square full images less costly
      // by forcing both the smaller thumb and the larger version to have exactly the same intrinsic dimensions.
      // If a drawable is replaced in an ImageView by another drawable with different intrinsic dimensions,
      // the ImageView requests a layout. Scrolling rapidly while replacing thumbs with larger images triggers
      // lots of these calls and causes significant amounts of jank.
      float viewRatio = view.getWidth() / (float) view.getHeight();
      float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight();
      if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN
          && Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) {
        resource = new SquaringDrawable(resource, view.getWidth());
      }
    }
    super.onResourceReady(resource, animation);
    this.resource = resource;
    //********看这里******
    //android studio可以通过快捷键Ctrl+Alt+B查看其实现
    resource.setLoopCount(maxLoopCount);
    resource.start();
  }

glide4.0 计算gif一次播放时长代码:

  public static void loadGifImg(Context context,String url, ImageView imageView){
    GlideApp.with(context)
        .asGif()
        .load(url)
        .placeholder(R.drawable.placeholder)
        .fallback(R.drawable.fallback)
        .listener(new RequestListener<GifDrawable>() {
          @Override
          public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<GifDrawable> target, boolean isFirstResource) {
            return false;
          }

          @Override
          public boolean onResourceReady(GifDrawable gifDrawable, Object model, Target<GifDrawable> target, DataSource dataSource, boolean isFirstResource) {
            try {
              //设置循环播放次数为1次
              gifDrawable.setLoopCount(1);
              // 计算动画时长
              int duration = 0;
              //GifDecoder decoder = gifDrawable.getDecoder();//4.0开始没有这个方法了
              Drawable.ConstantState state = gifDrawable.getConstantState();
              if(state!=null){
                //不能混淆GifFrameLoader和GifState类
                Object gifFrameLoader = getValue(state,"frameLoader");
                if(gifFrameLoader!=null){
                  Object decoder = getValue(gifFrameLoader,"gifDecoder");
                  if(decoder!=null && decoder instanceof GifDecoder){
                    for (int i = 0; i < gifDrawable.getFrameCount(); i++) {
                      duration += ((GifDecoder) decoder).getDelay(i);
                    }
                  }
                }
                Log.e("Glide4.0","gif播放一次动画时长duration:"+duration);
              }
            } catch (Throwable e) {
            }
            return false;
          }
        })
        .into(imageView);
  }

注意:因为用了反射获取decoder,所以不能混淆GifFrameLoader和GifState类

5、设置淡入淡出动画

glide3.7.0

Glide.with(context)
        .load(url)
        .crossFade(100) //系统渐变动画
        .placeholder(R.drawable.placeholder)
        .fallback(R.drawable.fallback)
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .into(imageView);

glide4.0

GlideApp.with(context)
        .load(url)
        .transition(DrawableTransitionOptions.withCrossFade(100))//淡入淡出100m
        .placeholder(R.drawable.placeholder)
        .fallback(R.drawable.fallback)
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .into(imageView);

6、缓存策略

glide3.7.0

//DiskCacheStrategy.SOURCE:缓存原始数据
//DiskCacheStrategy.RESULT:缓存变换(如缩放、裁剪等)后的资源数据
//DiskCacheStrategy.NONE:什么都不缓存
//DiskCacheStrategy.ALL:缓存SOURC和RESULT
//默认采用DiskCacheStrategy.RESULT策略,对于download only操作要使用DiskCacheStrategy.SOURCE

glide4.0

//DiskCacheStrategy.ALL 使用DATA和RESOURCE缓存远程数据,仅使用RESOURCE来缓存本地数据。
// DiskCacheStrategy.NONE 不使用磁盘缓存
// DiskCacheStrategy.DATA 在资源解码前就将原始数据写入磁盘缓存
// DiskCacheStrategy.RESOURCE 在资源解码后将数据写入磁盘缓存,即经过缩放等转换后的图片资源。
// DiskCacheStrategy.AUTOMATIC 根据原始图片数据和资源编码策略来自动选择磁盘缓存策略。
//默认采用DiskCacheStrategy.AUTOMATIC策略
/*-------------------------------------------------------------------------------*/
//源码 RequestOptions.java
private DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.AUTOMATIC;

7、占位符、错误图片设置

glide4.0 若into中设置的是target,占位符(placeholder、error)需要在回调中再次设置,否则无效。

 public static void loadImg(String url, ImageView imageView) {
    //into中用Target,占位符(placeholder、error)需要在回调中设置
    GlideApp.with(FanhuanApplication.getInstance().getApplication())
        .asBitmap()
        .load(url)
        .placeholder(drawbleId) //设置资源加载过程中的占位符
        .fallback(drawbleId)
        .error(drawbleId)
        .diskCacheStrategy(DiskCacheStrategy.ALL)
        .into(new SimpleTarget<Bitmap>() {
          @Override
          public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
            imageView.setImageBitmap(resource);
         }

          @Override
          public void onLoadFailed(@Nullable Drawable errorDrawable) {
            super.onLoadFailed(errorDrawable);
             if(errorDrawable!=null){
              imageView.setImageDrawable(errorDrawable);
            }
          }

          @Override
          public void onLoadStarted(@Nullable Drawable placeholder) {
            super.onLoadStarted(placeholder);
            if(placeholder!=null){
              imageView.setImageDrawable(placeholder);
            }
          }
        });
  }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Glide4 高效加载图片的配置详解

    本文介绍了Glide4 高效加载图片的配置详解,分享给大家,具体如下: 在build.gradle中添加glide依赖 // glide 依赖 compile 'com.github.bumptech.glide:glide:4.6.1' // glide 相关注解,生成GlideApp代码 annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1' // Glide网络库配置成okhttp3 compile ('com.gith

  • Glide4.6.1 GlideApp无法生成的问题的解决

    本文介绍了Glide4.6.1 GlideApp 无法生成的问题的解决,分享给大家,具体如下: 依赖配置如下 Glide 4.6.1的sdk build版本必须为>=27,必须保证所有的support 的版本为相同并且为27.1.0 compile 'com.github.bumptech.glide:glide:4.6.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1' compile 'com.android.s

  • 详解Glide4.0集成及使用注意事项

    Glide 4.0由Google的各种团队内部使用,4.0被认为是内部稳定的.但外部用户可能会发现内部尚未发现的问题.因此,将此作为RC发布.如果没有发现稳定性或API中的重大问题,预计不久之后就会发布非RC版本. 一.集成 1.project gradle repositories { mavenLocal() } 2.app gradle compile 'com.android.support:support-v4:25.3.1' compile 'com.github.bumptech.

  • Flyway详解及Springboot集成Flyway的详细教程

    Flayway是一款数据库版本控制管理工具,,支持数据库版本自动升级,Migrations可以写成sql脚本,也可以写在java代码里:不仅支持Command Line和java api ,也支持Build构建工具和Spring boot,也可以在分布式环境下能够安全可靠安全地升级数据库,同时也支持失败恢复. Flyway最核心的就是用于记录所有版本演化和状态的MetaData表,Flyway首次启动会创建默认名为SCHEMA_VERSION的元素局表. 表中保存了版本,描述,要执行的sql脚本

  • 详解Springboot2.3集成Spring security 框架(原生集成)

    0.pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0

  • 详解java如何集成swagger组件

    一:简介 Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.总体目标是使客户端和文件系统作为服务器以同样的速度来更新.文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步.Swagger 让部署管理和使用功能强大的API从未如此简单. 二:集成swagger 1.引入pom.xml文件包(导入4个jar包) 注意:jdk1.8以上才能运行swagger2 <!--swagger--> <dependency&g

  • 详解 swift3.0 可选绑定共用同一块内存空间的实例

    详解 swift3.0 可选绑定共用同一块内存空间的实例 示例代码: ljTempModel = UserModel.init(userName: "sww", userID: 12, phone: "123", email: "deew") ljTempModel?.ljArray.append("sww") print("可选绑定前:\(ljTempModel?.ljArray)") //可选绑定成功,

  • 详解vue3.0 diff算法的使用(超详细)

    前言:随之vue3.0beta版本的发布,vue3.0正式版本相信不久就会与我们相遇.尤玉溪在直播中也说了vue3.0的新特性typescript强烈支持,proxy响应式原理,重新虚拟dom,优化diff算法性能提升等等.小编在这里仔细研究了vue3.0beta版本diff算法的源码,并希望把其中的细节和奥妙和大家一起分享. 首先我们来思考一些大中厂面试中,很容易问到的问题: 1 什么时候用到diff算法,diff算法作用域在哪里? 2 diff算法是怎么运作的,到底有什么作用? 3 在v-f

  • 详解MySQL8.0​ 字典表增强

    MySQL中数据字典是数据库重要的组成部分之一,INFORMATION_SCHEMA首次引入于MySQL 5.0,作为一种从正在运行的MySQL服务器检索元数据的标准兼容方式.用于存储数据元数据.统计信息.以及有关MySQL server的访问信息(例如:数据库名或表名,字段的数据类型和访问权限等). 8.0之前: 1.元数据来自文件 2.采用MEMORY表引擎 3.frm文件 存放表结构信息 4.opt文件,记录了每个库的一些基本信息,包括库的字符集等信息 5..TRN,.TRG文件用于存放触

  • 详解MySQL8.0 密码过期策略

    MySQL8.0.16开始,可以设置密码的过期策略,今天针对这个小的知识点进行展开. 1.手工设置单个密码过期 MySQL8.0中,我们可以使用alter user这个命令来让密码过期. 首先我们创建账号yeyz,密码是yeyz [root@VM-0-14-centos ~]# /usr/local/mysql-8.0.19-el7-x86_64/bin/mysql -uyeyz -pyeyz -h127.0.0.1 -P4306 -e "select 1" mysql: [Warni

  • 详解Vue3.0 + TypeScript + Vite初体验

    项目创建 npm: $ npm init vite-app <project-name> $ cd <project-name> $ npm install $ npm run dev or yarn: $ yarn create vite-app <project-name> $ cd <project-name> $ yarn $ yarn dev 项目结构 main.js 在个人想法上,我觉得createApp()是vue应用的实例,createApp

  • 详解vite2.0配置学习(typescript版本)

    介绍 尤于溪的原话. vite与 Vue CLI 类似,vite 也是一个提供基本项目脚手架和开发服务器的构建工具. vite基于浏览器原生ES imports的开发服务器.跳过打包这个概念,服务端按需编译返回. vite速度比webpack快10+倍,支持热跟新, 但是出于处于测试阶段. 配置文件也支持热跟新!!! 创建 执行npm init @vitejs/app ,我这里选择的是vue-ts 版本 "vite": "^2.0.0-beta.48" alias别

随机推荐