详解Glide4.0集成及使用注意事项
Glide4.0由Google的各种团队内部使用,4.0被认为是内部稳定的。但外部用户可能会发现内部尚未发现的问题。因此,将此作为RC发布。如果没有发现稳定性或API中的重大问题,预计不久之后就会发布非RC版本。
一、集成
1、projectgradle
repositories{
mavenLocal()
}
2、appgradle
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
-keeppublicclass*implementscom.bumptech.glide.module.GlideModule
-keeppublicclass*extendscom.bumptech.glide.AppGlideModule
-keeppublicenumcom.bumptech.glide.load.resource.bitmap.ImageHeaderParser$**{
**[]$VALUES;
public*;
}
#forDexGuardonly
-keepresourcexmlelementsmanifest/application/meta-data@value=GlideModule
#从glide4.0开始,GifDrawable没有提供getDecoder()方法,
#需要通过反射获取gifDecoder字段值,所以需要保持GifFrameLoader和GifState类不被混淆
-keepclasscom.bumptech.glide.load.resource.gif.GifDrawable$GifState{*;}
-keepclasscom.bumptech.glide.load.resource.gif.GifFrameLoader{*;}
4、在4.0中不用像3.X需要在AndroidManifest.xml配置GlideModule,而是通过注解继承AppGlideModule的子类来配置。
@GlideModule
publicclassGlideConfigurationextendsAppGlideModule{
@Override
publicvoidapplyOptions(Contextcontext,GlideBuilderbuilder){
//自定义缓存目录,磁盘缓存给150M另外一种设置缓存方式
builder.setDiskCache(newInternalCacheDiskCacheFactory(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
publicbooleanisManifestParsingEnabled(){
returnfalse;
}
}
二、使用注意事项
1、使用GlideApp代替Glide,asBitmap、asGif、asDrawable、asFile都要放到load之前(glide3.7.0都是要在load之后调用)。
publicstaticvoidloadImg(Contextcontext,Stringurl,ImageViewimageView){
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播放一次的动画时长。
glide3.7.0你可以这样获取动画时长:
publicvoidloadGif(Contextcontext,ImageViewmIvGif,inturl){
Glide.with(context)
.load(url)
.listener(newRequestListener(){
@Override
publicbooleanonException(Exceptione,Integermodel,Targettarget,booleanisFirstResource){
returnfalse;
}
@Override
publicbooleanonResourceReady(GlideDrawableresource,Integermodel,Targettarget,booleanisFromMemoryCache,booleanisFirstResource){
try{
intduration=0;
GifDrawablegifDrawable=(GifDrawable)resource;
GifDecoderdecoder=gifDrawable.getDecoder();
for(inti=0;i
glide4.0中GifDrawable没有提供getDecoder()方法并且还去掉了decoder这个成员变量。除此之外,glide4.0还去掉了GlideDrawableImageViewTarget类,那我们该如何来计算gif播放一次的时长呢?只能从源码中找答案了。
(1)寻找decoder
glide3.7.0GifDrawable中我们可以发现decoder最终于会传入GifFrameLoader类中并赋值给gifDecoder变量。
//源码
//glide3.7.0GifDrawable.java
GifDrawable(GifStatestate){
if(state==null){
thrownewNullPointerException("GifStatemustnotbenull");
}
this.state=state;
this.decoder=newGifDecoder(state.bitmapProvider);
this.paint=newPaint();
decoder.setData(state.gifHeader,state.data);
frameLoader=newGifFrameLoader(state.context,this,decoder,state.targetWidth,state.targetHeight);
frameLoader.setFrameTransformation(state.frameTransformation);
}
/*---------------------------------------------------------------------------------------------------*/
//glide3.7.0GifFrameLoader.java
privatefinalGifDecodergifDecoder;//私有属性
publicGifFrameLoader(Contextcontext,FrameCallbackcallback,GifDecodergifDecoder,intwidth,intheight){
this(callback,gifDecoder,null,
getRequestBuilder(context,gifDecoder,width,height,Glide.get(context).getBitmapPool()));
}
GifFrameLoader(FrameCallbackcallback,GifDecodergifDecoder,Handlerhandler,
GenericRequestBuilderrequestBuilder){
if(handler==null){
handler=newHandler(Looper.getMainLooper(),newFrameLoaderCallback());
}
this.callback=callback;
//看这里
this.gifDecoder=gifDecoder;
this.handler=handler;
this.requestBuilder=requestBuilder;
}
glide4.0GifDrawable类的构造中我们可以看到有一个gifDecoder的参数,这个参数的解释是解码器用于解码GIF数据(ThedecodertousetodecodeGIFdata)。继续看这个构造,发现gifDecoder最终也是被传到GifFrameLoader类中并赋值给gifDecoder变量。所以glide3.7.0中的decoder其实就是4.0中的gifDecoder。
//源码
//glide4.0GifDrawable.java
privatefinalGifStatestate;
/*
*@paramgifDecoderThedecodertousetodecodeGIFdata.
*@paramfirstFrameThedecodedandtransformedfirstframeofthisGIF.
*@see#setFrameTransformation(com.bumptech.glide.load.Transformation,android.graphics.Bitmap)
*/
publicGifDrawable(Contextcontext,GifDecodergifDecoder,BitmapPoolbitmapPool,
TransformationframeTransformation,inttargetFrameWidth,inttargetFrameHeight,
BitmapfirstFrame){
this(
newGifState(
bitmapPool,
newGifFrameLoader(
//TODO(b/27524013):FactoroutthiscalltoGlide.get()
Glide.get(context),
gifDecoder,
targetFrameWidth,
targetFrameHeight,
frameTransformation,
firstFrame)));
}
/*---------------------------------------------------------------------------------------------*/
//glide4.0GifFrameLoader.java
privatefinalGifDecodergifDecoder;//私有属性
publicGifFrameLoader(
Glideglide,
GifDecodergifDecoder,
intwidth,
intheight,
Transformationtransformation,
BitmapfirstFrame){
this(
glide.getBitmapPool(),
Glide.with(glide.getContext()),
gifDecoder,
null/*handler*/,
getRequestBuilder(Glide.with(glide.getContext()),width,height),
transformation,
firstFrame);
}
@SuppressWarnings("PMD.ConstructorCallsOverridableMethod")
GifFrameLoader(
BitmapPoolbitmapPool,
RequestManagerrequestManager,
GifDecodergifDecoder,
Handlerhandler,
RequestBuilderrequestBuilder,
Transformationtransformation,
BitmapfirstFrame){
this.requestManager=requestManager;
if(handler==null){
handler=newHandler(Looper.getMainLooper(),newFrameLoaderCallback());
}
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.0GifDrawable.java
privatefinalGifStatestate;
@Override
publicConstantStategetConstantState(){
returnstate;
}
staticclassGifStateextendsConstantState{
staticfinalintGRAVITY=Gravity.FILL;
finalBitmapPoolbitmapPool;
finalGifFrameLoaderframeLoader;
publicGifState(BitmapPoolbitmapPool,GifFrameLoaderframeLoader){
this.bitmapPool=bitmapPool;
this.frameLoader=frameLoader;
}
@Override
publicDrawablenewDrawable(Resourcesres){
returnnewDrawable();
}
@Override
publicDrawablenewDrawable(){
returnnewGifDrawable(this);
}
@Override
publicintgetChangingConfigurations(){
return0;
}
}
通过反射来获取获取decoder
.listener(newRequestListener(){
@Override
publicbooleanonLoadFailed(@NullableGlideExceptione,Objectmodel,Targettarget,booleanisFirstResource){
returnfalse;
}
@Override
publicbooleanonResourceReady(GifDrawablegifDrawable,Objectmodel,Targettarget,DataSourcedataSource,booleanisFirstResource){
try{
intduration=0;
//计算动画时长
//GifDecoderdecoder=gifDrawable.getDecoder();//4.0开始没有这个方法了
Drawable.ConstantStatestate=gifDrawable.getConstantState();
if(state!=null){
//不能混淆GifFrameLoader和GifState类
ObjectgifFrameLoader=getValue(state,"frameLoader");
if(gifFrameLoader!=null){
Objectdecoder=getValue(gifFrameLoader,"gifDecoder");
if(decoder!=null&&decoderinstanceofGifDecoder){
for(inti=0;iclazz=object.getClass();
for(;clazz!=Object.class;clazz=clazz.getSuperclass()){
try{
field=clazz.getDeclaredField(fieldName);
field.setAccessible(true);
returnfield.get(object);
}catch(Exceptione){
//这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。
//如果这里的异常打印或者往外抛,则就不会执行clazz=clazz.getSuperclass(),最后就不会进入到父类中了
}
}
returnnull;
}
(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.0GlideDrawableImageViewTarget.java
publicGlideDrawableImageViewTarget(ImageViewview,intmaxLoopCount){
super(view);
this.maxLoopCount=maxLoopCount;
}
@Override
publicvoidonResourceReady(GlideDrawableresource,GlideAnimationanimation){
if(!resource.isAnimated()){
//TODO:Trytogeneralizethistoothersizes/shapes.
//Thisisadirtyhackthattriestomakeloadingsquarethumbnailsandthensquarefullimageslesscostly
//byforcingboththesmallerthumbandthelargerversiontohaveexactlythesameintrinsicdimensions.
//IfadrawableisreplacedinanImageViewbyanotherdrawablewithdifferentintrinsicdimensions,
//theImageViewrequestsalayout.Scrollingrapidlywhilereplacingthumbswithlargerimagestriggers
//lotsofthesecallsandcausessignificantamountsofjank.
floatviewRatio=view.getWidth()/(float)view.getHeight();
floatdrawableRatio=resource.getIntrinsicWidth()/(float)resource.getIntrinsicHeight();
if(Math.abs(viewRatio-1f)<=SQUARE_RATIO_MARGIN
&&Math.abs(drawableRatio-1f)<=SQUARE_RATIO_MARGIN){
resource=newSquaringDrawable(resource,view.getWidth());
}
}
super.onResourceReady(resource,animation);
this.resource=resource;
//********看这里******
//androidstudio可以通过快捷键Ctrl+Alt+B查看其实现
resource.setLoopCount(maxLoopCount);
resource.start();
}
glide4.0计算gif一次播放时长代码:
publicstaticvoidloadGifImg(Contextcontext,Stringurl,ImageViewimageView){
GlideApp.with(context)
.asGif()
.load(url)
.placeholder(R.drawable.placeholder)
.fallback(R.drawable.fallback)
.listener(newRequestListener(){
@Override
publicbooleanonLoadFailed(@NullableGlideExceptione,Objectmodel,Targettarget,booleanisFirstResource){
returnfalse;
}
@Override
publicbooleanonResourceReady(GifDrawablegifDrawable,Objectmodel,Targettarget,DataSourcedataSource,booleanisFirstResource){
try{
//设置循环播放次数为1次
gifDrawable.setLoopCount(1);
//计算动画时长
intduration=0;
//GifDecoderdecoder=gifDrawable.getDecoder();//4.0开始没有这个方法了
Drawable.ConstantStatestate=gifDrawable.getConstantState();
if(state!=null){
//不能混淆GifFrameLoader和GifState类
ObjectgifFrameLoader=getValue(state,"frameLoader");
if(gifFrameLoader!=null){
Objectdecoder=getValue(gifFrameLoader,"gifDecoder");
if(decoder!=null&&decoderinstanceofGifDecoder){
for(inti=0;i
注意:因为用了反射获取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策略,对于downloadonly操作要使用DiskCacheStrategy.SOURCE
glide4.0
//DiskCacheStrategy.ALL使用DATA和RESOURCE缓存远程数据,仅使用RESOURCE来缓存本地数据。
//DiskCacheStrategy.NONE不使用磁盘缓存
//DiskCacheStrategy.DATA在资源解码前就将原始数据写入磁盘缓存
//DiskCacheStrategy.RESOURCE在资源解码后将数据写入磁盘缓存,即经过缩放等转换后的图片资源。
//DiskCacheStrategy.AUTOMATIC根据原始图片数据和资源编码策略来自动选择磁盘缓存策略。
//默认采用DiskCacheStrategy.AUTOMATIC策略
/*-------------------------------------------------------------------------------*/
//源码RequestOptions.java
privateDiskCacheStrategydiskCacheStrategy=DiskCacheStrategy.AUTOMATIC;
7、占位符、错误图片设置
glide4.0若into中设置的是target,占位符(placeholder、error)需要在回调中再次设置,否则无效。
publicstaticvoidloadImg(Stringurl,ImageViewimageView){
//into中用Target,占位符(placeholder、error)需要在回调中设置
GlideApp.with(FanhuanApplication.getInstance().getApplication())
.asBitmap()
.load(url)
.placeholder(drawbleId)//设置资源加载过程中的占位符
.fallback(drawbleId)
.error(drawbleId)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(newSimpleTarget(){
@Override
publicvoidonResourceReady(Bitmapresource,Transitiontransition){
imageView.setImageBitmap(resource);
}
@Override
publicvoidonLoadFailed(@NullableDrawableerrorDrawable){
super.onLoadFailed(errorDrawable);
if(errorDrawable!=null){
imageView.setImageDrawable(errorDrawable);
}
}
@Override
publicvoidonLoadStarted(@NullableDrawableplaceholder){
super.onLoadStarted(placeholder);
if(placeholder!=null){
imageView.setImageDrawable(placeholder);
}
}
});
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。