詳解Glide4.0集成及使用注意事項
Glide 4.0由Google的各種團(tuán)隊內(nèi)部使用,4.0被認(rèn)為是內(nèi)部穩(wěn)定的。但外部用戶可能會發(fā)現(xiàn)內(nèi)部尚未發(fā)現(xiàn)的問題。因此,將此作為RC發(fā)布。如果沒有發(fā)現(xiàn)穩(wěn)定性或API中的重大問題,預(yù)計不久之后就會發(fā)布非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 另外一種設(shè)置緩存方式
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "GlideImgCache", 150 * 1024 * 1024));
//配置圖片緩存格式 默認(rèn)格式為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之后調(diào)用)。
public static void loadImg(Context context,String url, ImageView imageView){
GlideApp.with(context)
.asBitmap()
.load(url)
.placeholder(R.drawable.placeholder) //設(shè)置資源加載過程中的占位符
.into(imageView);
}
2、占位符.placeholder(R.drawable.placeholder)不能用.9圖,占位圖片和加載的目標(biāo)圖片會同時顯示,只是目標(biāo)圖片會先顯示縮略圖,然后顯示正常。fallback和error還沒測試過,有興趣的可以測試看看。
3、加載gif圖時,若調(diào)用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中我們可以發(fā)現(xiàn)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類的構(gòu)造中我們可以看到有一個gifDecoder的參數(shù),這個參數(shù)的解釋是解碼器用于解碼GIF數(shù)據(jù)(The decoder to use to decode GIF data)。繼續(xù)看這個構(gòu)造,發(fā)現(xiàn)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構(gòu)造中可以看到gifDecoder被傳遞到GifFrameLoader中賦值給私有屬性gifDecoder;,而GifFrameLoader又被傳入GifState中并被賦值給它的成員變量frameLoader,那要怎么獲取frameLoader?
從源碼中,可以看到GifDrawable提供了getConstantState()方法來獲取state變量(這個變量的類型就是GifState),但是GifState并沒有g(shù)et方法獲取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;
}
})
/*---------------------------------------------------------------------------------------------*/
/**
* 通過字段名從對象或?qū)ο蟮母割愔械玫阶侄蔚闹?
* @param object 對象實例
* @param fieldName 字段名
* @return 字段對應(yīng)的值
* @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) {
//這里甚么都不要做!并且這里的異常必須這樣寫,不能拋出去。
//如果這里的異常打印或者往外拋,則就不會執(zhí)行clazz = clazz.getSuperclass(),最后就不會進(jìn)入到父類中了
}
}
return null;
}
(3)設(shè)置gif循環(huán)播放次數(shù)
glide4.0中沒有GlideDrawableImageViewTarget類,那么怎么設(shè)置循環(huán)播放次數(shù)呢?
從glide3.7.0源碼可以發(fā)現(xiàn)GlideDrawableImageViewTarget是通過GlideDrawable的setLoopCount方法來設(shè)置循環(huán)播放次數(shù)的,查看setLoopCount具體實現(xiàn)地方是在GifDrawable,所以這里調(diào)用的其實是GifDrawable的setLoopCount方法。glide4.0中沒有GlideDrawable類和GlideDrawableImageViewTarget類,但是仍然有GifDrawable類,并且onResourceReady方法中第一個參數(shù)就是GifDrawable,所以可以直接調(diào)用GifDrawable的setLoopCount(loopCount)來設(shè)置播放次數(shù)。
//源碼
//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查看其實現(xiàn)
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 {
//設(shè)置循環(huán)播放次數(shù)為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、設(shè)置淡入淡出動畫
glide3.7.0
Glide.with(context)
.load(url)
.crossFade(100) //系統(tǒng)漸變動畫
.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:緩存原始數(shù)據(jù) //DiskCacheStrategy.RESULT:緩存變換(如縮放、裁剪等)后的資源數(shù)據(jù) //DiskCacheStrategy.NONE:什么都不緩存 //DiskCacheStrategy.ALL:緩存SOURC和RESULT //默認(rèn)采用DiskCacheStrategy.RESULT策略,對于download only操作要使用DiskCacheStrategy.SOURCE
glide4.0
//DiskCacheStrategy.ALL 使用DATA和RESOURCE緩存遠(yuǎn)程數(shù)據(jù),僅使用RESOURCE來緩存本地數(shù)據(jù)。 // DiskCacheStrategy.NONE 不使用磁盤緩存 // DiskCacheStrategy.DATA 在資源解碼前就將原始數(shù)據(jù)寫入磁盤緩存 // DiskCacheStrategy.RESOURCE 在資源解碼后將數(shù)據(jù)寫入磁盤緩存,即經(jīng)過縮放等轉(zhuǎn)換后的圖片資源。 // DiskCacheStrategy.AUTOMATIC 根據(jù)原始圖片數(shù)據(jù)和資源編碼策略來自動選擇磁盤緩存策略。 //默認(rèn)采用DiskCacheStrategy.AUTOMATIC策略 /*-------------------------------------------------------------------------------*/ //源碼 RequestOptions.java private DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.AUTOMATIC;
7、占位符、錯誤圖片設(shè)置
glide4.0 若into中設(shè)置的是target,占位符(placeholder、error)需要在回調(diào)中再次設(shè)置,否則無效。
public static void loadImg(String url, ImageView imageView) {
//into中用Target,占位符(placeholder、error)需要在回調(diào)中設(shè)置
GlideApp.with(FanhuanApplication.getInstance().getApplication())
.asBitmap()
.load(url)
.placeholder(drawbleId) //設(shè)置資源加載過程中的占位符
.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);
}
}
});
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
內(nèi)存泄露導(dǎo)致Android?中setVisibility()?失效原理
這篇文章主要介紹了內(nèi)存泄露導(dǎo)致Android?中setVisibility()?失效原理,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-07-07
Flutter持久化存儲之?dāng)?shù)據(jù)庫存儲(sqflite)詳解
這篇文章主要給大家介紹了關(guān)于Flutter持久化存儲之?dāng)?shù)據(jù)庫存儲的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Android關(guān)于SeekBar無法點擊到最大值問題解決方法記錄(推薦)
這篇文章主要介紹了Android關(guān)于SeekBar無法點擊到最大值問題解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
Android Insets相關(guān)知識總結(jié)
這篇文章主要介紹了Android Insets相關(guān)知識總結(jié),幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03
android開發(fā)socket編程之udp發(fā)送實例分析
這篇文章主要介紹了android開發(fā)socket編程之udp發(fā)送,實例分析了Android開發(fā)socket網(wǎng)絡(luò)編程中udp發(fā)送的相關(guān)技巧,非常具有實用價值,需要的朋友可以參考下2015-04-04

