//該死的拖延癥瞒御,總是要學習做筆記想虎,紙上得來終覺淺。
一购城、簡介应结、使用刨疼。
1.1簡介
略,見 主頁
1.2基本使用
Glide.with(imageView.getContext())
.load(url)
.placeholder(defaultImage)
.error(failImage) .diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
其中 load()重載方法摊趾。load也支持多參數(shù)币狠,這里我們可以傳入的有Uri游两、File砾层、byte[]、Object贱案、Bitmap肛炮、String、Drawable宝踪、Integer侨糟、URl,
DiskCacheStrategy是緩存策略瘩燥,有以下幾個參數(shù)秕重,并詳細說明該策略的保存信息。
1) ALL 網絡資源執(zhí)行DATA厉膀、RESOURCE溶耘,本地資源執(zhí)行RESOURCE
2) NONE 不緩存任何內容
3) DATA 緩存原始圖片
4) RESOURCE 緩存解碼后的圖片
5) AUTOMATIC 根據(jù)圖片資源智能地選擇使用哪一種緩存策略(默認選項)
1.3 其他用法
1.3.1
listener 這里我們可以對加載圖片進行處理二拐,加載成功和加載失敗兩個方法
1.3.2
addListener 和listener不同的是,listener是清空了監(jiān)聽凳兵,把當前監(jiān)聽加入進去百新,addListener是不清空監(jiān)聽,既不影響其他地方調用的監(jiān)聽庐扫,僅僅是把當前的監(jiān)聽加入進去饭望,建議使用addListener,其他和listener相同
1.3.3
.centerInside() .fitCenter() .centerCrop() 這三種圖片處理方式形庭,centerInside等比例縮小顯示铅辞,在橫向或豎向上相等,不會進行放大碘勉。fitCenter等比例放大或縮小巷挥。centerCrop等比例放大。
1.3.4
override(int w,int h)這個方法會將圖片以制定的高寬顯示在ImageView上
1.3.5
apply(RequestOptions.bitmapTransform(new RoundedCorners(dp2px(60)))) 這個方法可以設置為圓形圖了验靡。
.optionalFitCenter() 同樣可以圓形
二倍宾、原理分析
2.1原理
緩存:
先去LruCache中尋找圖片,如果LruCache中有胜嗓,則直接取出來使用高职,如果LruCache中沒有,則去WeakReference中尋找辞州,如果WeakReference中有怔锌,則從WeakReference中取出圖片使用,同時將圖片重新放回到LruCache中变过,如果WeakReference中也沒有圖片埃元,則去文件系統(tǒng)中尋找,如果有則取出來使用媚狰,同時將圖片添加到LruCache中岛杀,如果沒有,則連接網絡從網上下載圖片崭孤。圖片下載完成后类嗤,將圖片保存到文件系統(tǒng)中,然后放到LruCache中辨宠。
Glide的緩存只有兩個模塊遗锣,一個是內存緩存,一個是磁盤緩存嗤形。其中內存緩存又分為Lru算法的緩存和弱引用緩存精偿。
LruCache算法,Least Recently Used,又稱為近期最少使用算法笔咽。主要算法原理就是把最近所使用的對象的強引用存儲在LinkedHashMap上墓阀,并且,把最近最少使用的對象在緩存池達到預設值之前從內存中移除拓轻。
讀取的順序是:Lru算法緩存斯撮、弱引用緩存、磁盤緩存
寫入的順序是:弱引用緩存扶叉、Lru算法緩存勿锅、磁盤緩存(不準確)
2.2 代碼順序
2.2.1 with()
with()方法重載的代碼都非常簡單,都是先調用RequestManagerRetriever的靜態(tài)get()方法得到一個RequestManagerRetriever對象枣氧,這個靜態(tài)get()方法就是一個單例實現(xiàn)溢十。然后再調用RequestManagerRetriever的實例get()方法,去獲取RequestManager對象达吞。
RequestManagerRetriever類中看似有很多個get()方法的重載张弛,實際上只有兩種情況而已,即傳入Application類型的參數(shù)酪劫,和傳入非Application類型的參數(shù)吞鸭。
a.傳入Application參數(shù)
如果在Glide.with()方法中傳入的是一個Application對象,那么這里就會調用帶有Context參數(shù)的get()方法重載覆糟,調用getApplicationManager()方法來獲取一個RequestManager對象刻剥。其實這是最簡單的一種情況,因為Application對象的生命周期即應用程序的生命周期滩字,因此它自動就是和應用程序的生命周期是同步的造虏,如果應用程序關閉的話,Glide的加載也會同時終止麦箍。
b.非Application參數(shù)的情況漓藕。
最終的流程都是一樣的,那就是會向當前的Activity當中添加一個隱藏的Fragment挟裂。因為Glide需要知道加載的生命周期享钞。Fragment的生命周期和Activity是同步的,如果Activity被銷毀了话瞧,F(xiàn)ragment是可以監(jiān)聽到的嫩与,這樣Glide就可以捕獲這個事件并停止圖片加載了寝姿。
2.2.2 load()方法交排。
根據(jù)傳入的參數(shù)類型作為泛型,創(chuàng)建一個圖片請求對象DrawableTypeRequest.
DrawableTypeRequest的父類是DrawableRequestBuilder饵筑,DrawableRequestBuilder中有很多個方法埃篓,這些方法其實就是Glide絕大多數(shù)的API比如說placeholder()方法、error()方法根资、diskCacheStrategy()方法架专、override()方法等同窘。
RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
this.context = context.getApplicationContext();
this.lifecycle = lifecycle;//與頁面生命周期綁定的lifecycle對象
/*treeNode:
*提供了可以訪問當前上下文中所有的RequestManager,也就是說基于Context層次結構建立了RequestManager的層次結構部脚,
*而上下文的層次結構是在Activity / Fragment中嵌套的想邦。
*不過,注意委刘,如果當前上下文是Application Context 丧没,只能訪問當前上下文的RequestManager. 總而言之就是更方便的管理所有的RequestManager
*/
this.treeNode = treeNode;
this.requestTracker = requestTracker;//請求管理控制類,start锡移、pause呕童、resume、clear淆珊、restart夺饲、addRequest...
this.glide = Glide.get(context);//保存有一個單例的glide對象
this.optionsApplier = new OptionsApplier();//optionsApplier的apply方法返回一個圖片請求對象DrawableTypeRequest
/*網絡狀態(tài)的監(jiān)聽接口,實現(xiàn)了LifecycleListener 施符,與Activity/Fragment 生命周期綁定在一起往声。
*具體實現(xiàn)DefaultConnectivityMonitor 以及NullConnectivityMonitor ,其中前者是一個網絡監(jiān)聽的具體實現(xiàn)戳吝,
*后者是一個空實現(xiàn)烁挟,類似于Null Object Pattern ,可以避免空指針異常
*/
ConnectivityMonitor connectivityMonitor = factory.build(context, new RequestManagerConnectivityListener(requestTracker));
if (Util.isOnBackgroundThread()) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);
}
});
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);
}
/**
* 根據(jù)傳入的參數(shù)類型作為泛型骨坑,創(chuàng)建一個圖片請求對象DrawableTypeRequest
* DrawableTypeRequest中存有 glide撼嗓、requestTracker、lifecycle等的引用
* 還有ModelLoader欢唾,load方法傳入的數(shù)據(jù)不一定是url且警,傳入的數(shù)據(jù)通過ModelLoader處理后轉為真正的圖片數(shù)據(jù)源
*/
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
public DrawableTypeRequest<String> fromString() {
return loadGeneric(String.class);
}
private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(modelClass, context);
if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
+ " which there is a registered ModelLoader, if you are using a custom model, you must first call"
+ " Glide#register with a ModelLoaderFactory for your custom model class");
}
return optionsApplier.apply(new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context, glide, requestTracker, lifecycle, optionsApplier));
}
//DrawableTypeRequest的load方法只是把傳入的model持有,并未真正加載
public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
this.model = model;
isModelSet = true;
return this;
}
2.2.3 into()
into()方法的具體邏輯都是在DrawableRequestBuilder的父類當中了礁遣。
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
DrawableRequestBuilder的父類是GenericRequestBuilder斑芜,在這里根據(jù)顯示拉伸類型,調用了glide.buildImageViewTarget()方法祟霍,這個方法會構建出一個Target對象杏头,Target對象則是用來最終展示圖片用的。其實是調用的為ImageViewTargetFactory的buildTarget()方法沸呐。然后傳遞給GenericRequestBuilder另一個接收Target對象的into()方法當中了醇王。
public class ImageViewTargetFactory {
@SuppressWarnings("unchecked")
public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
if (GlideDrawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new GlideDrawableImageViewTarget(view);
} else if (Bitmap.class.equals(clazz)) {
return (Target<Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (Target<Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException("Unhandled class: " + clazz
+ ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
在buildTarget()方法中會根據(jù)傳入的class參數(shù)來構建不同的Target對象,這個class參數(shù)其實基本上只有兩種情況崭添,如果你在使用Glide加載圖片的時候調用了asBitmap()方法寓娩,那么這里就會構建出BitmapImageViewTarget對象,否則的話構建的都是GlideDrawableImageViewTarget對象。
接上文第一次into(imageview)通過imageViewTargetFactory.buildTarget(imageView, transcodedClass)生成GlideDrawableImageViewTarget對象有傳遞給第二個into(),第二個into對象
public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
2.2.4 request
可以看出來調用buildRequest()方法構建出了一個Request對象棘伴,然后執(zhí)行這個Request寞埠。Request是用來發(fā)出加載圖片請求的,buildRequestRecursive實則調用了buildRequestRecursive函數(shù)焊夸。
順序為如果配置了thumbnail(縮略圖)請求仁连,則構建一個ThumbnailRequestCoordinator(包含了FullRequest和ThumbnailRequest)請求,否則簡單的構建一個Request阱穗,調用了obtainRequest函數(shù)怖糊。至此請求對象創(chuàng)建成功。
然后執(zhí)行runrequest
public void runRequest(Request request) {
this.requests.add(request);
//添加request對象到集合中颇象,requests是一個set伍伤,Set是一個不包含重復元素的 collection,在這里是無序的遣钳。
if(!this.isPaused) {
request.begin();//如果當前狀態(tài)是非暫停的扰魂,調用begin方法發(fā)送請求
} else {
this.pendingRequests.add(request);
//將請求加入到掛起的請求集合,RequestTracker在RequestManager類中初始化蕴茴,并且跟蹤著生命周期
}
}
begin函數(shù)劝评,首先做了幾個判斷,驗證寬高是否合法倦淀,加載占位圖等蒋畜,然后執(zhí)行onsizeReady()方法中l(wèi)oad() ,進入Engine邏輯
this.engine.load(this.signature, width, height, dataFetcher, this.loadProvider, this.transformation, transcoder, this.priority, this.isMemoryCacheable, this.diskCacheStrategy, this);
load的策略,據(jù)調用loadFromCache從內存加載撞叽,若返回值為空再次從活動的資源中加載姻成,若再次為空查看jobs是否提交過任務,若沒有提交則創(chuàng)建EngineRunnable愿棋,并將任務提交到engineJob中科展。
Transformation類負責處理資源,這里面出現(xiàn)BitmapPool類糠雨,達到Bitmap復用才睹。
先從cache中尋找資源,如果找到則將其從cache中移除并放入activeResources中甘邀,否則從activeResources中尋找琅攘。cache是LruResourceCache對象,作為資源的LRU緩存松邪;activeResources是以弱引用為值的Map坞琴,用于緩存使用中的資源。比一般內存緩存額外多一級緩存的意義在于测摔,當內存不足時清理cache中的資源時置济,不會對使用中的Bitmap造成影響。
3.1
with(Context context) - 需要上下文锋八,這里還可以使用 Activity浙于、FragmentActivity、android.support.v4.app.Fragment挟纱、android.app.Fragment 的對象羞酗。將 Activity/Fragment 對象作為參數(shù)的好處是,圖片的加載會和 Activity/Fragment 的生命周期保持一致紊服,例如:onPaused 時暫停加載檀轨,onResume 時又會自動重新加載。所以在傳參的時候建議使用 Activity/Fragment 對象欺嗤,而不是 Context参萄。
3.2Module
Glide 的 Module 是一個可以全局改變 Glide 的行為的東西,為了定制 Glide 的行為我們要去實現(xiàn) interface GlideModule 來寫我們自己的代碼煎饼。
public class ExampleModule implements GlideModule{
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// todo
}
@Override
public void registerComponents(Context context, Glide glide) {
// todo
}
}
可以看到 GlideModule 為我們提供了兩個方法讹挎,這里我們主要使用的是 applyOptions(Context context, GlideBuilder builder) , 我們自己的需要重新定義的代碼寫在該方法里就可以了吆玖。然后我們還需要去 AndroidManifest.xml 中使用 meta 聲明我們上面實現(xiàn)的 Module
<application>
<meta-data
android:name="com.xxx.ExampleModule"
android:value="GlideModule" />
...
</application>
applyOptions(Context context, GlideBuilder builder) 中有兩個參數(shù)筒溃, 我們通過使用 GlideBuilder 來實現(xiàn)我們的需求。先看看 GlideBuilder 中可用的方法
.setMemoryCache(MemoryCache memoryCache)
.setBitmapPool(BitmapPool bitmapPool)
.setDiskCache(DiskCache.Factory diskCacheFactory)
.setDiskCacheService(ExecutorService service)
.setResizeService(ExecutorService service)
.setDecodeFormat(DecodeFormat decodeFormat)
3.3Transformations篇
圖片進行處理操作沾乘,比如:圖片切圓角怜奖,灰階處理等等;這些需求我們通過 Transformations 操作 bitmap 來實現(xiàn)翅阵,我們可以修改圖片的任意屬性:尺寸歪玲,范圍,顏色掷匠,像素位置等等读慎。其實我們之前已經提到過兩個 Transformation 了,即 fitCenter 和 centerCrop 槐雾,這兩個是 Glide 已經實現(xiàn)的夭委。
怎么樣來實現(xiàn)自己的 Transformation ,我們需要創(chuàng)建一個類去實現(xiàn) Transformation 接口募强,但是要實現(xiàn)這個方法還是比較復雜的株灸,接口中 transform 方法提供的參數(shù) Resource<T> resource 不是那么好處理的。如果你只是想要對圖片(不是 Gif 和 video)做常規(guī)的 bitmap 轉換擎值,我們推薦你使用抽象類 BitmapTransformation慌烧。它簡化了很多的實現(xiàn),這應該能覆蓋 95% 的應用場景啦鸠儿。
下面的代碼實現(xiàn)了對圖片切圓角的操作屹蚊,其中 getId() 方法描述了這個 Transformation 的唯一標識厕氨,為避免意外我們需要確保它是唯一的。
public class RoundTransformation extends BitmapTransformation {
private float radius = 0f;
public RoundTransformation(Context context) {
this(context, 4);
}
public RoundTransformation(Context context, int px) {
super(context);
this.radius = px;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return roundCrop(pool, toTransform);
}
private Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null)
return null;
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
@Override
public String getId() {
return getClass().getName() + Math.round(radius);
}
}
使用
調用 .transform() 方法汹粤,將自定義的 Transformation 的對象作為參數(shù)傳遞進去就可以使用你的 Transformation 了命斧,這里也可以使用 .bitmaoTransform() 但是它只能用于 bitmap 的轉換。
Glide.with(context)
.load(mUrl)
.transform(new RoundTransformation(context , 20))
//.bitmapTransform( new RoundTransformation(context , 20) )
.into(mImageView);