官方使用建議:
// For a simple view:
@Override
public void onCreate(Bundle savedInstanceState) {
...
ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
}
// For a simple image list:
@Override
public View getView(int position, View recycled, ViewGroup container) {
final ImageView myImageView;
if (recycled == null) {
myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false);
} else {
myImageView = (ImageView) recycled;
}
String url = myUrls.get(position);
Glide
.with(myFragment)
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.into(myImageView);
return myImageView;
}
官方指導(dǎo)RecyclerView使用時加載圖片
有時在使用 RecyclerView時休溶,View 可能被重用且保持了前一個位置的尺寸,但在當(dāng)前位置會發(fā)生改變扰她。為了處理這種場景兽掰,你可以創(chuàng)建一個新的 ViewTarget 并為 waitForLayout() 方法傳入 true:
@Override
public void onBindViewHolder(VH holder, int position) {
Glide.with(fragment)
.load(urls.get(position))
.into(new DrawableImageViewTarget(holder.imageView, /*waitForLayout=*/ true));
優(yōu)化性能指導(dǎo)
清理
當(dāng)你完成了對資源(Bitmap,Drawable 等)的使用時徒役,及時清理(clear)你創(chuàng)建的這些 Target 是一個好的實踐孽尽。即使你認為你的請求已經(jīng)完成了,也應(yīng)該使用 clear() 以使 Glide 可以重用被這次加載使用的任何資源 (特別是 Bitmap )忧勿。未調(diào)用 clear() 會浪費 CPU 和內(nèi)存杉女,阻塞更重要的加載,甚至如果你在同一個 surface (View, Notification, RPC 等) 上有兩個 Target鸳吸,可能會引發(fā)圖片顯示錯誤熏挎。對于像 SimpleTarget這種無法從一個新實例里跟蹤前一個請求的 Target 來說,及時清理尤為重要晌砾。
Glide.with(fragment)
.load(url)
.into(imageView);
// Some time in the future:
Glide.with(fragment).clear(imageView);
動畫資源和定制目標(biāo)
Glide.with(fragment)
.asGif()
.load(url)
.into(new SimpleTarget<>() {
@Override
public void onResourceReady(GifDrawable resource, Transition<GifDrawable> transition) {
resource.start();
// Set the resource wherever you need to use it.
}
});
如果你加載的是 Bitmap 或 GifDrawable坎拐,你可以判斷這個可繪制對象是否實現(xiàn)了 Animatable:
Glide.with(fragment)
.load(url)
.into(new SimpleTarget<>() {
@Override
public void onResourceReady(Drawable resource, Transition<GifDrawable> transition) {
if (resource instanceof Animatable) {
resource.start();
}
// Set the resource wherever you need to use it.
}
});
注意
Android中的動畫代價是比較大的,尤其是同時開始大量動畫的時候养匈。 交叉淡入和其他涉及 alpha 變化的動畫顯得尤其昂貴哼勇。 此外,動畫通常比圖片解碼本身還要耗時呕乎。在列表和網(wǎng)格中濫用動畫可能會讓圖像的加載顯得緩慢而卡頓猴蹂。為了提升性能,請在使用 Glide 向 ListView , GridView, 或 RecyclerView 加載圖片時考慮避免使用動畫楣嘁,尤其是大多數(shù)情況下磅轻,你希望圖片被盡快緩存和加載的時候。作為替代方案逐虚,請考慮預(yù)加載聋溜,這樣當(dāng)用戶滑動到具體的 item 的時候,圖片已經(jīng)在內(nèi)存中了叭爱。
對占位符和透明圖片交叉淡入【官方提醒】
雖然禁用交叉淡入通常是一個比較好的默認行為撮躁,當(dāng)待加載的圖片包含透明像素時仍然可能造成問題。當(dāng)占位符比實際加載的圖片要大买雾,或者圖片部分為透明時把曼,禁用交叉淡入會導(dǎo)致動畫完成后占位符在圖片后面仍然可見杨帽。如果你在加載透明圖片時使用了占位符,你可以啟用交叉淡入嗤军,具體辦法是調(diào)整 DrawableCrossFadeFactory 里的參數(shù)并將結(jié)果傳到 transition() 中:
DrawableCrossFadeFactory factory =
new DrawableCrossFadeFactory.Builder().setCrossFadeEnabled(true).build();
GlideApp.with(context)
.load(url)
.transition(withCrossFade(factory))
.diskCacheStrategy(DiskCacheStrategy.ALL)
.placeholder(R.color.placeholder)
.into(imageView);
以前屏幕比較小注盈,列表圖片和一些圖片加載展現(xiàn)的尺寸不大,所以就算有圖片疊加問題不留意也看不出來叙赚。
但是最近的網(wǎng)絡(luò)電視流行老客。圖片加載尺寸越來越大這個問題就有可能比較嚴重了。尤其進行TV開發(fā)者留意
應(yīng)用程序(Applications)和程序庫 (Libraries)使用建議
應(yīng)用程序(Applications)如果希望使用集成庫和/或 Glide 的 API 擴展震叮,則需要:
恰當(dāng)?shù)靥砑右粋€ AppGlideModule 實現(xiàn)胧砰。
(可選)添加一個或多個 LibraryGlideModule 實現(xiàn)。
給上述兩種實現(xiàn)添加 @GlideModule 注解苇瓣。
添加對 Glide 的注解解析器的依賴尉间。
在 proguard 中,添加對 AppGlideModules 的 keep 击罪。
@GlideModule
public class FlickrGlideModule extends AppGlideModule {
@Override
public void registerComponents(Context context, Registry registry) {
registry.append(Photo.class, InputStream.class, new FlickrModelLoader.Factory());
}
}
程序庫如果需要注冊定制組件乌妒,例如 ModelLoader,可按以下步驟執(zhí)行:
添加一個或多個 LibraryGlideModule 實現(xiàn)外邓,以注冊新的組件撤蚊。
為每個 LibraryGlideModule 實現(xiàn),添加 @GlideModule 注解损话。
添加 Glide 的注解處理器的依賴侦啸。
一個 [LibraryGlideModule] 的例子,在 Glide 的OkHttp 集成庫 中:
@GlideModule
public final class OkHttpLibraryGlideModule extends LibraryGlideModule {
@Override
public void registerComponents(Context context, Glide glide, Registry registry) {
registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
}
避免在程序庫中使用 AppGlideModule
程序庫一定 不要 包含 AppGlideModule 實現(xiàn)丧枪。這么做將會阻止依賴該庫的任何應(yīng)用程序管理它們的依賴光涂,或配置諸如 Glide 緩存大小和位置之類的選項。
此外拧烦,如果兩個程序庫都包含 AppGlideModule忘闻,應(yīng)用程序?qū)o法在同時依賴兩個庫的情況下通過編譯,而不得不在二者之中做出取舍恋博。
這確實意味著程序庫將無法使用 Glide 的 generated API齐佳,但是使標(biāo)準的 RequestBuilder 和 RequestOptions 加載仍然有效(可以在 選項 頁找到例子)
沖突
應(yīng)用程序可能依賴多個程序庫,而它們每一個都可能包含一個或更多的 LibraryGlideModules 债沮。在極端情況下炼吴,這些 LibraryGlideModules 可能定義了相互沖突的選項,或者包含了應(yīng)用程序希望避免的行為疫衩。應(yīng)用程序可以通過給他們的 AppGlideModule 添加一個 @Excludes 注解來解決這種沖突硅蹦,或避免不需要的依賴。
例如,如果你依賴了一個庫童芹,它有一個 LibraryGlideModule 叫做com.example.unwanted.GlideModule涮瞻,而你不想要它:
@Excludes(com.example.unwanted.GlideModule)
@GlideModule
public final class MyAppGlideModule extends AppGlideModule { }
你也可以排除多個模塊:
@Excludes({com.example.unwanted.GlideModule, com.example.conflicing.GlideModule})
@GlideModule
public final class MyAppGlideModule extends AppGlideModule { }
@Excludes 注解不僅可用于排除 LibraryGlideModules 。如果你還在從 Glide v3
到新版本的遷移過程中假褪,你還可以用它來排除舊的署咽,廢棄的 GlideModule 實現(xiàn)
【省流量模式】僅從緩存加載圖片
某些情形下,你可能希望只要圖片不在緩存中則加載直接失斒燃邸(比如省流量模式)。如果要完成這個目標(biāo)幕庐,你可以在單個請求的基礎(chǔ)上使用 onlyRetrieveFromCache 方法:
Glide.with(fragment)
.load(url)
.onlyRetrieveFromCache(true)
.into(imageView);
如果圖片在內(nèi)存緩存或在磁盤緩存中久锥,它會被展示出來。否則只要這個選項被設(shè)置為 true 异剥,這次加載會視同失敗瑟由。
【圖片驗證碼】跳過緩存
如果你想確保一個特定的請求跳過磁盤和/或內(nèi)存緩存(比如,圖片驗證碼 )冤寿,Glide 也提供了一些替代方案歹苦。
僅跳過內(nèi)存緩存,請使用 skipMemoryCache() :
Glide.with(fragment)
.load(url)
.skipMemoryCache(true)
.into(view);
僅跳過磁盤緩存督怜,請使用 DiskCacheStrategy.NONE :
Glide.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(view);
這兩個選項可以同時使用:
Glide.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(view);
從源碼解析的時候發(fā)現(xiàn)的使用方式粗略有25種組合
* @see Glide#with(android.app.Activity)
* @see Glide#with(androidx.fragment.app.FragmentActivity)
* @see Glide#with(android.app.Fragment)
* @see Glide#with(androidx.fragment.app.Fragment)
* @see Glide#with(Context)
*
//源碼使用方式一
FutureTarget<Bitmap> target =
Glide.with(app)
.asBitmap()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.skipMemoryCache(true)
.override(targetSize)
.load(resourceId)
.listener(
new RequestListener<Bitmap>() {
@Override
public boolean onLoadFailed(
@Nullable GlideException e,
Object model,
Target<Bitmap> target,
boolean isFirstResource) {
return false;
}
@Override
public boolean onResourceReady(
Bitmap resource,
Object model,
Target<Bitmap> target,
DataSource dataSource,
boolean isFirstResource) {
dataSourceRef.set(dataSource);
return false;
}
})
.submit();
....
Glide.with(app).clear(target);
//用法二
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical);
byte[] data = concurrency.get(Glide.with(context).as(byte[].class).load(bitmap).submit());
//三
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResourceIds.raw.canonical);
byte[] data =
concurrency.get(
Glide.with(context)
.as(byte[].class)
.load(new BitmapDrawable(context.getResources(), bitmap))
.submit());
//4
byte[] data =concurrency.get(Glide.with(context).as(byte[].class).load(ResourceIds.raw.video).submit());
//5
Glide.with(context).as(byte[].class).load(writeVideoToFile()).submit()
//6
byte[] data =
concurrency.get(
Glide.with(context)
.as(byte[].class)
.load(writeVideoToFile().getAbsolutePath())
.submit());
//7
byte[] data =
concurrency.get(
Glide.with(context).as(byte[].class).load(Uri.fromFile(writeVideoToFile())).submit());
//8
Glide.with((Context) ApplicationProvider.getApplicationContext()).clear(imageView);
//9
Bitmap bitmap =
concurrency.get(
Glide.with(context).asBitmap().load(getDataUriString(CompressFormat.JPEG)).submit());
Bitmap bitmap =
concurrency.get(
Glide.with(context).asBitmap().load(getDataUriString(CompressFormat.PNG)).submit());
Bitmap bitmap =
concurrency.get(
Glide.with(context).asBitmap().load(getDataUri(CompressFormat.JPEG)).submit());
...
private Uri getDataUri(CompressFormat format) {
return Uri.parse(getDataUriString(format));
}
private String getDataUriString(CompressFormat format) {
String bytes = getBase64BitmapBytes(format);
String imageType;
switch (format) {
case PNG:
imageType = "png";
break;
case JPEG:
imageType = "jpeg";
break;
case WEBP:
imageType = "webp";
break;
default:
throw new IllegalArgumentException("Unrecognized format: " + format);
}
String mimeType = "image/" + imageType;
return "data:" + mimeType + ";base64," + bytes;
}
...
private String getBase64BitmapBytes(CompressFormat format) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Drawable drawable =
Preconditions.checkNotNull(ContextCompat.getDrawable(context, ResourceIds.raw.canonical));
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
bitmap.compress(format, 100, bos);
byte[] data = bos.toByteArray();
return Base64.encodeToString(data, /*flags=*/ 0);
}
//10
Drawable colorDrawable = new ColorDrawable(Color.RED);
Drawable result =
Glide.with(context)
.load(colorDrawable)
.apply(new RequestOptions().optionalCenterCrop())
.submit()
.get();
=====================
Drawable result =
Glide.with(context)
.load(colorDrawable)
.apply(new RequestOptions().centerCrop())
.submit(100, 100)
.get();
//11
Glide.with(context).load(ResourceIds.raw.canonical).listener(requestListener).submit()
//12
Drawable drawable = concurrency.get(Glide.with(context).load(raw.canonical).submit());
//13
Glide.with(app)
.load(loadStep.getModel(beforeData))
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(Target.SIZE_ORIGINAL)
.submit()
.get(15, TimeUnit.SECONDS);
//14
Glide.with(app)
.as(Baz.class)
.load(model)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE));
//15
Glide.with(getContext()).asDrawable().load(android.R.drawable.ic_menu_rotate).into(this);
//16
byte[] data = getLargeImageBytes();
Bitmap bitmap = concurrency.get(Glide.with(context).asBitmap().load(data).submit());
//17
ByteBuffer buffer = ByteBuffer.wrap(getLargeImageBytes());
Bitmap bitmap = concurrency.get(Glide.with(context).asBitmap().load(buffer).submit());
//18
Glide.with(context).load(canonicalBytes).apply(skipMemoryCacheOf(false))
//19
Glide.with(context).asDrawable().load(canonicalBytes).apply(skipMemoryCacheOf(false)
//20
Drawable frame = concurrency.get(Glide.with(context).load(ResourceIds.raw.video).submit());
//21
Drawable frame =
concurrency.get(Glide.with(context).load(new Integer(ResourceIds.raw.video)).submit());
//22
Bitmap frame =
concurrency.get(Glide.with(context).asBitmap().load(ResourceIds.raw.video).submit());
//23
Drawable drawable =
Glide.with(context)
.load(android.R.drawable.star_big_off)
.apply(centerCropTransform())
.submit()
.get();
//24
Drawable drawable =
Glide.with(context)
.load(ResourceIds.drawable.shape_drawable)
.apply(bitmapTransform(new RoundedCorners(10)))
.submit(100, 200)
.get();
Bitmap bitmap =
Glide.with(context)
.asBitmap()
.load(ResourceIds.drawable.shape_drawable)
.submit(100, 200)
.get();
Bitmap bitmap =
Glide.with(context)
.asBitmap()
.load(ResourceIds.drawable.shape_drawable)
.apply(centerCropTransform())
.submit(100, 200)
.get();
//25
Bitmap bitmap = Glide.with(context).asBitmap().load(uri).submit().get();
Bitmap bitmap = Glide.with(context).asBitmap().apply(centerCropTransform()).load(uri).submit().get();
Drawable drawable = Glide.with(context).load(uri).submit().get();
豐富的gilde使用方式殴瘦,總有一款適合你
總結(jié)
- 使用上非常簡潔 glide陪伴著android系統(tǒng)一起成長。
- 其中的設(shè)計理念和設(shè)計實踐都很值得App開發(fā)者學(xué)習(xí)和借鑒
- 后期將從設(shè)計模式号杠、算法蚪腋、測試的幾個角度深度解析Glide源碼工程。
- 如果發(fā)現(xiàn)很好的實踐將會單獨摘出來說明使用環(huán)境和條件
更多設(shè)計模式解讀
單例模式
Glide設(shè)計模式之單例模式
空對象模式
Glide設(shè)計模式之空對象模式【EmptyModelLoader】【EmptyList<E>
建造者模式
Glide多種組合使用方式記錄--沒有全部親測姨蟋,大家可以根據(jù)實際需要選用
Glide設(shè)計模式之建造者(builder)模式1【GlideBuilder】
Glide設(shè)計模式之建造者(builder)模式2【RequestBuilder】
Glide設(shè)計模式之建造者(builder)模式3【RequestOptions】【BaseRequestOptions】
Glide設(shè)計模式之建造者(builder)模式4總結(jié)【MemorySizeCalculator】【GlideExecutor】【PreFillType】【LazyHeaders】