Glide庫加載圖片
文章轉載於:http://blog.csdn.net/shangmingchao
一. 下載
在build.gradle中添加依賴:
- 1
- 1
需要support-v4庫的支持,如果你的項目沒有support-v4庫(項目默認已經添加了),還需要添加support-v4依賴:
- 1
- 1
然後配置混淆規則:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
其中第一個混淆規則表明不混淆所有的GlideModule
。
如果需要的話,還需添加相應的權限:
- 1
- 2
- 1
- 2
二. 集成網絡框架OkHttp(可選)
Glide的網絡請求部分可以使用當前最流行的網絡請求框架Volley或OkHttp,也可以通過Glide的ModelLoader接口自己寫網絡請求。
Glide默認使用HttpUrlConnection
進行網絡請求,爲了讓APP保持一致的網絡請求形式,可以讓Glide使用我們指定的網絡請求形式請求網絡資源,這裏我們選OkHttp (具有支持HTTP/2、利用連接池技術減少請求延遲、緩存響應結果等等優點),需要添加一個集成庫:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
注意:
1. OkHttp 2.x和OkHttp 3.x需使用不同的集成庫。
2. Gradle會自動將OkHttpGlideModule合併到應用的manifest文件中。
3. 如果你沒有對所有的GlideModule
配置混淆規則(即沒有使用-keep public class * implements com.bumptech.glide.module.GlideModule
),則需要把OkHttp的GlideModule
進行防混淆配置:
- 1
- 1
三. 使用
簡單使用:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
Glide.with()使用
- with(Context context). 使用Application上下文,Glide請求將不受Activity/Fragment生命週期控制。
- with(Activity activity).使用Activity作爲上下文,Glide的請求會受到Activity生命週期控制。
- with(FragmentActivity activity).Glide的請求會受到FragmentActivity生命週期控制。
- with(android.app.Fragment fragment).Glide的請求會受到Fragment 生命週期控制。
- with(android.support.v4.app.Fragment fragment).Glide的請求會受到Fragment生命週期控制。
返回關聯了相應上下文的RequestManager
實例。
requestManager.load()使用
Glide基本可以load任何可以拿到的媒體資源,如:
load SD卡資源:load("file://"+ Environment.getExternalStorageDirectory().getPath()+"/test.jpg")
load assets資源:load("file:///android_asset/f003.gif")
load raw資源:load("Android.resource://com.frank.glide/raw/raw_1")或load("android.resource://com.frank.glide/raw/"+R.raw.raw_1)
load drawable資源:load("android.resource://com.frank.glide/drawable/news")或load("android.resource://com.frank.glide/drawable/"+R.drawable.news)
load ContentProvider資源:load("content://media/external/images/media/139469")
load http資源:load("http://img.my.csdn.net/uploads/201508/05/1438760757_3588.jpg")
load https資源:load("https://img.alicdn.com/tps/TB1uyhoMpXXXXcLXVXXXXXXXXXX-476-538.jpg_240x5000q50.jpg_.webp")
當然,load不限於String類型,還可以:
load(Uri uri)
,load(File
file)
,load(Integer resourceId)
,load(URL
url)
,load(byte[] model)
,load(T
model)
,loadFromMediaStore(Uri uri)
。
load的資源也可以是本地視頻,如果想要load網絡視頻或更高級的操作可以使用VideoView
等其它控件完成。
而且可以使用自己的ModelLoader
進行資源加載:
using(ModelLoader<A, T> modelLoader, Class<T> dataClass)
,using(final
StreamModelLoader<T> modelLoader)
,using(StreamByteArrayLoader modelLoader)
,using(final
FileDescriptorModelLoader<T> modelLoader)
。
返回GenericRequestBuilder實例。
GenericRequestBuilder使用
GenericRequestBuilder<ModelType,DataType,ResourceType,TranscodeType>
是最頂層的Request Builder,用於處理選項設置和開始一般resource類型資源的加載。其中ModelType
是指代表資源的類型,如"http://img.my.csdn.net/uploads/201508/05/1438760757_3588.jpg"這個String就代表了一張圖片資源,所以這個ModelType就是String。DataType
是指ModelLoader提供的,可以被ResourceDecoder解碼的數據類型。ResourceType
是指將要加載的resource類型。TranscodeType
是指已解碼的資源將要被轉成的資源類型。
- thumbnail(float sizeMultiplier). 請求給定係數的縮略圖。如果縮略圖比全尺寸圖先加載完,就顯示縮略圖,否則就不顯示。係數sizeMultiplier必須在(0,1)之間,可以遞歸調用該方法。
- sizeMultiplier(float sizeMultiplier). 在加載資源之前給Target大小設置係數。
- diskCacheStrategy(DiskCacheStrategy strategy).設置緩存策略。DiskCacheStrategy.SOURCE:緩存原始數據,DiskCacheStrategy.RESULT:緩存變換(如縮放、裁剪等)後的資源數據,DiskCacheStrategy.NONE:什麼都不緩存,DiskCacheStrategy.ALL:緩存SOURC和RESULT。默認採用DiskCacheStrategy.RESULT策略,對於download only操作要使用DiskCacheStrategy.SOURCE。
- priority(Priority priority). 指定加載的優先級,優先級越高越優先加載,但不保證所有圖片都按序加載。枚舉Priority.IMMEDIATE,Priority.HIGH,Priority.NORMAL,Priority.LOW。默認爲Priority.NORMAL。
- dontAnimate(). 移除所有的動畫。
- animate(int animationId). 在異步加載資源完成時會執行該動畫。
- animate(ViewPropertyAnimation.Animator animator). 在異步加載資源完成時會執行該動畫。
- placeholder(int resourceId). 設置資源加載過程中的佔位Drawable。
- placeholder(Drawable drawable). 設置資源加載過程中的佔位Drawable。
- fallback(int resourceId). 設置model爲空時要顯示的Drawable。如果沒設置fallback,model爲空時將顯示error的Drawable,如果error的Drawable也沒設置,就顯示placeholder的Drawable。
- fallback(Drawable drawable).設置model爲空時顯示的Drawable。
- error(int resourceId).設置load失敗時顯示的Drawable。
- error(Drawable drawable).設置load失敗時顯示的Drawable。
- listener(RequestListener<? super ModelType, TranscodeType> requestListener). 監聽資源加載的請求狀態,可以使用兩個回調:
onResourceReady(R resource, T model, Target<R> target, boolean isFromMemoryCache, boolean isFirstResource)
和onException(Exception e, T model, Target<R> target, boolean isFirstResource)
,但不要每次請求都使用新的監聽器,要避免不必要的內存申請,可以使用單例進行統一的異常監聽和處理。 - skipMemoryCache(boolean skip). 設置是否跳過內存緩存,但不保證一定不被緩存(比如請求已經在加載資源且沒設置跳過內存緩存,這個資源就會被緩存在內存中)。
- override(int width, int height). 重新設置Target的寬高值(單位爲pixel)。
- into(Y target).設置資源將被加載到的Target。
- into(ImageView view). 設置資源將被加載到的ImageView。取消該ImageView之前所有的加載並釋放資源。
- into(int width, int height). 後臺線程加載時要加載資源的寬高值(單位爲pixel)。
- preload(int width, int height). 預加載resource到緩存中(單位爲pixel)。
- asBitmap(). 無論資源是不是gif動畫,都作爲Bitmap對待。如果是gif動畫會停在第一幀。
- asGif().把資源作爲GifDrawable對待。如果資源不是gif動畫將會失敗,會回調
.error()
。
技巧:
-
禁止內存緩存:
- 1
- 1
-
清除內存緩存:
- 1
- 2
- 1
- 2
-
禁止磁盤緩存:
- 1
- 1
-
清除磁盤緩存:
- 1
- 2
- 1
- 2
-
獲取緩存大小:
- 1
- 1
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
-
指定資源的優先加載順序:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
-
先顯示縮略圖,再顯示原圖:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
對圖片進行裁剪、模糊、濾鏡等處理:
推薦使用獨立的圖片處理庫:wasabeef/glide-transformations,使用也很簡單:- 1
- 1
之後我們就可以使用GenericRequestBuilder或其子類的
transform()
或bitmapTransform()
方法設置圖片轉換了:- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
可根據情況使用GenericRequestBuilder子類DrawableRequestBuilder的
bitmapTransform(Transformation<Bitmap>... bitmapTransformations)
,transform(BitmapTransformation... transformations)
,transform(Transformation<GifBitmapWrapper>... transformation)
,或其子類BitmapRequestBuilder的transform(BitmapTransformation... transformations)
,transform(Transformation<Bitmap>... transformations)
方法。
當然如果想自己寫Transformation:
最簡單的方式就是繼承BitmapTransformation
:- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
使用時只需使用
transform()
或bitmapTransform()
方法即可:- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
自定義圖片處理時Glide會自動計算View/Target大小,我們不需要傳View的寬高,當然你可以使用
override(int, int)
去改變這種行爲。自定義圖片處理時,爲了避免創建大量Bitmap以及減少GC,可以考慮重用Bitmap,這就需要
BitmapPool
,典型地就是,從Bitmap池中拿一個Bitmap,用這個Bitmap生成一個Canvas, 然後在這個Canvas上畫初始的Bitmap並使用Matrix、Paint、或者Shader處理這張圖片。
爲了有效並正確重用Bitmap需要遵循以下三條準則:- 永遠不要把
transform()
傳給你的原始resource或原始Bitmap給recycle()
了,更不要放回BitmapPool
,因爲這些都自動完成了。值得注意的是,任何從BitmapPool
取出的用於自定義圖片變換的輔助Bitmap,如果不經過transform()
方法返回,就必須主動放回BitmapPool
或者調用recycle()
回收。 - 如果你從BitmapPool拿出多個Bitmap或不使用你從BitmapPool拿出的一個Bitmap,一定要返回extras給BitmapPool。
- 如果你的圖片處理沒有替換原始resource(例如由於一張圖片已經匹配了你想要的尺寸,你需要提前返回), transform()`方法就返回原始resource或原始Bitmap。
如:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
也可以直接實現
Transformation
接口,進行更靈活的圖片處理,如進行簡單地圓角處理:- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
-
對請求狀態進行監聽:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
通過GenericRequestBuilder的
listener()
方法添加一個RequestListener
實現,但要注意,最好不要用匿名類,也不要每次都創建新的監聽器,要使用單例進行統一監聽處理,以避免不必要的內存申請和不必要的引用。方法最好返回false,以便Glide能繼續進行後續處理(如顯示error佔位符)。 - 對資源的下載進度進行監聽:
可以藉助OkHttp的攔截器進行進度監聽。OkHttp的攔截器官方Sample請移步這裏。我們可以利用這個攔截器進行監聽並處理,需要自定義ModelLoader和DataFetcher,具體請詳見我的Git:https://github.com/shangmingchao/ProgressGlide,歡迎Star啊,不過沒有太大必要告訴用戶圖片加載的進度(sjudd和TWiStErRob他們說的)。同時也可以看一下TWiStErRob大神的實現(自備梯子哈~.~)。
四. Glide有哪些“坑”?
- ImageView的setTag問題
問題描述:如果使用Glide
的into(imageView)
爲ImageView設置圖片的同時使用ImageView的setTag(final Object tag)
方法,將會導致java.lang.IllegalArgumentException: You must not call setTag() on a view Glide is targeting
異常。因爲Glide
的ViewTarget
中通過view.setTag(tag)
和view.getTag()
標記請求的,由於Android 4.0之前Tag存儲在靜態map裏,如果Glide使用setTag(int key, final Object tag)
方法標記請求則可能會導致內存泄露,所以Glide默認使用view.setTag(tag)
標記請求,你就不能重複調用了。
解決辦法:如果你需要爲ImageView設置Tag,必須使用setTag(int key, final Object tag)
及getTag(int key)
方法,其中key必須是合法的資源ID以確保key的唯一性,典型做法就是在資源文件中聲明type="id"的item資源。 - placeholder()導致的圖片變形問題
問題描述:使用.placeholder()
方法在某些情況下會導致圖片顯示的時候出現圖片變形的情況。這是因爲Glide默認開啓的crossFade動畫導致的TransitionDrawable繪製異常,詳細描述和討論可以看一下這個#363 issue。根本原因就是你的placeholder圖片和你要加載顯示的圖片寬高比不一樣,而Android的TransitionDrawable無法很好地處理不同寬高比的過渡問題,這的確是個Bug,是Android的也是Glide的。
解決辦法:使用.dontAnimate()方法禁用過渡動畫,或者使用animate()方法自己寫動畫,再或者自己修復TransitionDrawable的問題。 -
ImageView的資源回收問題
問題描述:默認情況下,Glide會根據with()
使用的Activity或Fragment的生命週期自動調整資源請求以及資源回收。但是如果有很佔內存的Fragment或Activity不銷燬而僅僅是隱藏視圖,那麼這些圖片資源就沒辦法及時回收,即使是GC的時候。
解決辦法:可以考慮使用WeakReference
,如:- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
- ImageView的scaleType問題
scaleType
默認爲fitCenter
模式,如果你想設置成centerInside
,不好意思,3.x還沒有這個方法,參見這個#591 issue,折中的解決辦法就是放棄使用centerInside
,或者結合android:scaleType="centerInside"
和.dontTransform()
使用以禁止Glide對資源進行轉換。
如果你想要ImageView的寬高根據圖片資源的大小而定(即使用wrap_comtent
),那麼你就必須明確告訴Glide我想加載原始資源:使用android:scaleType="center"
,或者.dontTransform()
,或者.override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
。
不推薦使用fitXY
,因爲這樣Glide會加載全尺寸圖像到內存中而造成不必要的內存佔用。 -
異步線程完成後加載圖片的崩潰問題
問題描述:通常情況下,異步線程會被約束在Activity生命週期內,所以異步線程完成後使用Glide加載圖片是沒有問題的。但如果你的異步線程在Activity銷燬時沒有取消掉,那麼異步線程完成後就Glide就無法爲一個已銷燬的Activity加載圖片資源,拋出的異常如下(在with()方法中就進行判斷並拋出異常):- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
解決辦法:正確管理Background Threads(異步線程),當Activity停止或銷燬時,停止所有相關的異步線程,停止所有後續的UI操作。或者加載前使用
isFinishing()
或isDestroyed()
進行限制(不建議這種處理方式)。 - 由於Bitmap複用導致的在某些設備上圖片錯亂的問題
問題描述: Glide默認使用BitmapPool的方式對應用中用到的Bitmap進行復用,以減少頻繁的內存申請和內存回收,而且默認使用的Bitmap模式爲RGB565以減少內存開銷。但在某些設備上(通常在Galaxy系列5.X設備上很容易復現)某些情況下會出現圖片加載錯亂的問題,具體詳見這個#601 issue。原因初步確定是OpenGL紋理渲染異常。
解決辦法:GlideModule
使用PREFER_ARGB_8888
(Glide4.X已經默認使用該模式了),雖然內存佔用比RGB565更多一點,但可以更好地處理有透明度Bitmap的複用問題。或者禁用Bitmap複用setBitmapPool(new BitmapPoolAdapter())
來修復這個問題(不推薦這種處理方式)。