详解Glide4.0集成及使用注意事项
作者:lmz14 发布时间:2021-12-28 00:09:06
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);
}
}
});
}
来源:https://www.jianshu.com/p/f1f17f816d1d
猜你喜欢
- 目录1、二分查找算法思想2、二分查找图示说明3、二分查找优缺点3、java代码实现3.1 使用递归实现3.1 不使用递归实现(while循环
- 前言服务消费者调用服务提供者的时候使用RestTemplate技术存在不便之处:拼接urlrestTmplate.getForObJect这
- SpringMVC异常处理机制(一)项目前准备首先参照文章Spring课程工程构建+SpringMVC简介及其快速入门搭建项目搭建好一个项目
- Java中的动态和静态编译实例详解首先,我们来说说动态和静态编译的问题。 Q: java和javascript有什么
- 我们先要记住三者的特征:String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线
- Java作为一面向对象的语言,具备面向对象的三大特征——继承,多态,封装。继承顾名思义,继任,承接,传承的意思。面向对象的语言有一个好处,就
- 当我们在做前后端分离的开发时,在使用fetch交换数据的时候,提示Access-Control-Allow-Origin跨域问题,解决方案跟
- StringString类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。这个
- 前言本文主要介绍下Spring事务中的传播行为。事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为。这是S
- WCF实例(带步骤) <xmlnamespace prefix ="o" ns ="urn:schema
- 相信大家在系统学习jvm的时候都会有遇到过这样的问题,散落的jvm知识点知道很多,但是真正在线上环境遇到一些莫名其妙的gc异常时候却无从下手
- 作为一位开发人员,都要有严格的代码规范。为此我总结了一些代码规范案例。目 录1. 前言2. 试用范围3. JAVA命名规范--3.1 公共约
- 前言本文的记录如何用CustomPaint、GestureDetector实现一个进度条控件。首先需要说明的是 flutter Materi
- 在intellij中忽略提交文件,分两种情况,文件没有纳入版本管理第一种方法文件还没有纳入版本管理,这种通过 svn的ignore配置ver
- Android中有两种主要方式使用Service,通过调用Context的startService方法或调用Context的bindServ
- bean 的生命周期对象创建实例化Bean对象,默认选择无参构造方法,如果只有一个有参构造那么调用有参构造,如果只有多个有参构造那么报错,除
- 前言翻看了下以前大学学习的一些小项目,突然发现有个项目比较有意思,觉得有必要把它分享出来。当然现在看来,里面有很多的不足之处,但因博主现在已
- 这篇文章主要介绍了JAVA如何定义构造函数过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可
- 最近在配置OpenCV的时候,由于使用的是VS2019,结果找不到Microsoft.Cpp.X64.user这个文件。导致每次新建项目都得
- 前言RefreshIndicator是Flutter里常见的下拉刷新组件,使用是比较方便的。但由于产品兄弟对其固定的刷新样式很是不满,而且代