Android高级技术第三方扩展图片

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

2017-09-09  本文已影响803人  lmz14

一、集成

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);
                        }
                    }
                });
    }
上一篇下一篇

猜你喜欢

热点阅读