详解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); } } }); }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。