一、概述
前面說的都是如何使用Glide提供的接口來展示圖片資源,今天這篇,我們來講一下如何改變Glide的配置。
二、定義GlideModule
2.1 步驟
首先,我們需要一個(gè)實(shí)現(xiàn)了GlideModule接口的類,重寫其中的方法來改變Glide的配置,然后讓Glide在構(gòu)造實(shí)例的過程中,讀取這個(gè)類中的配置信息。
- 第一步:實(shí)現(xiàn)
GlideModule接口
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//通過builder.setXXX進(jìn)行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通過glide.register進(jìn)行配置.
}
}
- 第二步:在
AndroidManifest.xml中的<application>標(biāo)簽下定義<meta-data>,這樣Glide才能知道我們定義了這么一個(gè)類,其中android:name是我們自定義的GlideModule的完整路徑,而android:value就固定寫死GlideModule。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.example.lizejun.repoglidelearn.CustomGlideModule"
android:value="GlideModule"/>
</application>
2.2 源碼分析
上面2.1所做的工作都是為了在Glide創(chuàng)建時(shí)可以讀取我們在兩個(gè)回調(diào)中配置的信息,我們來看一下Glide是如何使用這個(gè)自定義的類的,它的整個(gè)流程在Glide的get方法中:
public static Glide get(Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
Context applicationContext = context.getApplicationContext();
//第一步
List<GlideModule> modules = new ManifestParser(applicationContext).parse();
//第二步
GlideBuilder builder = new GlideBuilder(applicationContext);
for (GlideModule module : modules) {
//在builder構(gòu)造出glide之前,讀取使用者自定義的配置.
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
//第三步
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
可以看到,整個(gè)實(shí)例化Glide的過程分為三步:
- 第一步:去
AndroidManifest中查找meta-data為GlideModule的類,然后通過反射實(shí)例化它。 - 第二步:之后
Glide會(huì)新建一個(gè)GlideBuilder對象,它會(huì)先調(diào)用我們自定義的GlideModule的applyOptions方法,并把自己傳進(jìn)去,這樣,自定義的GlideModule就可以通過GlideBuilder提供的接口來設(shè)置它內(nèi)部的參數(shù),在builder.createGlide()的過程中就會(huì)根據(jù)它內(nèi)部的參數(shù)來構(gòu)建Glide,假如我們沒有設(shè)置相應(yīng)的參數(shù),那么在createGlide時(shí),就會(huì)采取默認(rèn)的實(shí)現(xiàn),下面就是memoryCache的例子。
//我們在applyOptions中,可以通過GlideBuilder的這個(gè)方法來設(shè)定自己的memoryCache.
public GlideBuilder setMemoryCache(MemoryCache memoryCache) {
this.memoryCache = memoryCache;
return this;
}
Glide createGlide() {
//如果builder中沒有設(shè)定memoryCache,那么采用默認(rèn)的實(shí)現(xiàn).
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
- 第三步:在
Glide實(shí)例化完畢之后,調(diào)用自定義GlideModule的registerComponents,并傳入當(dāng)前的Glide實(shí)例來讓使用者注冊自己的組件,其實(shí)在Glide實(shí)例化的過程中已經(jīng)注冊了默認(rèn)的組件,如果用戶定義了相同的組件,那么就會(huì)替換之前的。
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
//Glide默認(rèn)注冊的組件.
register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
register(File.class, InputStream.class, new StreamFileLoader.Factory());
register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(int.class, InputStream.class, new StreamResourceLoader.Factory());
register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
register(String.class, InputStream.class, new StreamStringLoader.Factory());
register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());
}
通俗的來說,注冊組件的目的就是告訴Glide,當(dāng)我們調(diào)用load(xxxx)方法時(shí),應(yīng)該用什么方式來獲取這個(gè)xxxx所指向的資源。因此,我們可以看到register的第一個(gè)參數(shù)就是我們load(xxxx)的類型,第二個(gè)參數(shù)是對應(yīng)的輸入流,而第三個(gè)參數(shù)就是定義獲取資源的方式。
我們也就分兩個(gè)部分,在第三、四節(jié),我們分兩部分討論這兩個(gè)回調(diào)函數(shù)的用法:applyOptions/registerComponents。
2.3 注意事項(xiàng)
- 由于
Glide是通過反射的方法來實(shí)例化GlideModule對象的,因此自定義的GlideModule只能有一個(gè)無參的構(gòu)造方法。 - 可以看到,上面是支持配置多個(gè)
GlideModule的,但是GlideModule的讀取順序并不能保證,因此,不要在多個(gè)GlideModule對同一個(gè)屬性進(jìn)行不同的配置。
三、applyOptions(Context context, GlideBuilder builder)方法詳解
在第二節(jié)中,我們已經(jīng)解釋過,這個(gè)回調(diào)方法的目的就是為了讓使用者能通過builder定義自己的配置,而所支持的配置也就是GlideBuilder的setXXX方法,它們包括:
setBitmapPool(BitmapPool bitmapPool)
設(shè)置Bitmap的緩存池,用來重用Bitmap,需要實(shí)現(xiàn)BitmapPool接口,它的默認(rèn)實(shí)現(xiàn)是LruBitmapPoolsetMemoryCache(MemoryCache memoryCache)
設(shè)置內(nèi)存緩存,需要實(shí)現(xiàn)MemoryCache接口,默認(rèn)實(shí)現(xiàn)是LruResourceCache。setDiskCache(DiskCache.Factory diskCacheFactory)
設(shè)置磁盤緩存,需要實(shí)現(xiàn)DiskCache.Factory,默認(rèn)實(shí)現(xiàn)是InternalCacheDiskCacheFactorysetResizeService(ExecutorService service)
當(dāng)資源不在緩存中時(shí),需要通過這個(gè)Executor發(fā)起請求,默認(rèn)是實(shí)現(xiàn)是FifoPriorityThreadPoolExecutor。setDiskCacheService(ExecutorService service)
讀取磁盤緩存的服務(wù),默認(rèn)實(shí)現(xiàn)是FifoPriorityThreadPoolExecutor。setDecodeFormat(DecodeFormat decodeFormat)
用于控制Bitmap解碼的清晰度,DecodeFormat可選的值有PREFER_ARGB_8888/PREFER_RGB_565,默認(rèn)為PREFER_RGB_565。
四、registerComponents(Context context, Glide glide)方法詳解
registerComponents相對來說就復(fù)雜了很多,它主要和三個(gè)接口有關(guān):
ModelLoaderFactoryModelLoaderDataFetcher
為了便于理解,我們先通過它內(nèi)部一個(gè)默認(rèn)Module的實(shí)現(xiàn)來看一下源碼是如何實(shí)現(xiàn)的。
我們選取是通用的加載普通圖片的url的例子,它對應(yīng)的注冊方法是下面這句:
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
//注冊加載網(wǎng)絡(luò)url的組件.
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
}
4.1 源碼分析
4.1.1 HttpUrlGlideUrlLoader.Factory
首先看一下HttpUrlGlideUrlLoader的內(nèi)部工廠類,它實(shí)現(xiàn)了ModelLoaderFactory<T, Y>接口
public interface ModelLoaderFactory<T, Y> {
ModelLoader<T, Y> build(Context context, GenericLoaderFactory factories);
void teardown();
}
它要求我們返回一個(gè)ModelLoader,我們看一下HttpUrlGlideUrlLoader.Factory是怎么做的,可以看到,它返回了HttpUrlGlideUrlLoader,而它的兩個(gè)泛型參數(shù)就是我們register中指定的前兩個(gè)參數(shù)類型。
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<GlideUrl, GlideUrl>(500);
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new HttpUrlGlideUrlLoader(modelCache);
}
@Override
public void teardown() {}
}
4.1.2 HttpUrlGlideUrlLoader
HttpUrlGlideUrlLoader實(shí)現(xiàn)了ModelLoader接口:
public interface ModelLoader<T, Y> {
DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}
ModelLoader提供了一個(gè)DataFetcher,它會(huì)去請求這個(gè)抽象模型所表示的數(shù)據(jù):
-
T:模型的類型。 -
Y:一個(gè)可以被ResourceDecoder解析出數(shù)據(jù)的表示。
GlideUrl的實(shí)現(xiàn)如下:
public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
private final ModelCache<GlideUrl, GlideUrl> modelCache;
public HttpUrlGlideUrlLoader() {
this(null);
}
public HttpUrlGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {
this.modelCache = modelCache;
}
//最主要的方法,它決定了我們獲取數(shù)據(jù)的方式.
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
return new HttpUrlFetcher(url);
}
}
4.1.3 HttpUrlFetcher
DataFetcher就是我們讀取數(shù)據(jù)的方式,它的關(guān)鍵方法是loadData,該loadData的返回值就是我們register的第二個(gè)參數(shù):
public interface DataFetcher<T> {
T loadData(Priority priority) throws Exception;
void cleanup();
String getId();
void cancel();
}
HttpUrlFetcher實(shí)現(xiàn)了DataFetcher接口,在它的loadData方法中,通過傳入的url發(fā)起請求,最終返回一個(gè)InputStream。
public class HttpUrlFetcher implements DataFetcher<InputStream> {
private static final String TAG = "HttpUrlFetcher";
private static final int MAXIMUM_REDIRECTS = 5;
private static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();
private final GlideUrl glideUrl;
private final HttpUrlConnectionFactory connectionFactory;
private HttpURLConnection urlConnection;
private InputStream stream;
private volatile boolean isCancelled;
public HttpUrlFetcher(GlideUrl glideUrl) {
this(glideUrl, DEFAULT_CONNECTION_FACTORY);
}
HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {
this.glideUrl = glideUrl;
this.connectionFactory = connectionFactory;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers) throws IOException {
//就是調(diào)用HttpUrlConnection請求.
}
@Override
public void cleanup() {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignore
}
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
@Override
public String getId() {
return glideUrl.getCacheKey();
}
@Override
public void cancel() {
isCancelled = true;
}
}
4.1.4 小結(jié)
對上面做個(gè)總結(jié),整個(gè)流程如下:通過register傳入一個(gè)ModelLoaderFactory<T, Y>工廠類,該工廠生產(chǎn)的是ModelLoader<T, Y>,而這個(gè)ModelLoader會(huì)根據(jù)T返回一個(gè)DataFetcher<Y>,在DataFetcher<Y>中,我們?nèi)カ@取數(shù)據(jù)。(在上面的例子中T就是GlideUrl,Y就是InputStream)
4.2 自定義ModuleLoader示例:用OkHttpClient替換HttpURLConnection
下面的例子來自于這篇文章:
https://futurestud.io/tutorials/glide-module-example-accepting-self-signed-https-certificates
- 第一步:定義
ModelLoader和ModelLoader.Factory
public class OkHttpGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new OkHttpGlideUrlLoader(getOkHttpClient());
}
@Override
public void teardown() {}
}
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
return new OkHttpGlideUrlFetcher(mOkHttpClient, model);
}
}
- 第二步:
ModelLoader的getResourceFetcher返回一個(gè)DataFetcher,我們給它傳入一個(gè)OkHttpClient實(shí)例,讓它通過OkHttpClient發(fā)起請求。
public class OkHttpGlideUrlFetcher implements DataFetcher<InputStream> {
public OkHttpGlideUrlFetcher(OkHttpClient client, GlideUrl url) {
this.client = client;
this.url = url;
}
@Override
public InputStream loadData(Priority priority) throws Exception {
Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
String key = headerEntry.getKey();
requestBuilder.addHeader(key, headerEntry.getValue());
}
Request request = requestBuilder.build();
Response response = client.newCall(request).execute();
responseBody = response.body();
if (!response.isSuccessful()) {
throw new IOException("Request failed with code: " + response.code());
}
long contentLength = responseBody.contentLength();
stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
return stream;
}
}
第三步:在CustomGlideModule中注冊這個(gè)組件:
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//通過builder.setXXX進(jìn)行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通過glide.register進(jìn)行配置.
glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory());
}
}
接著我們發(fā)起一次請求,通過斷點(diǎn)可以發(fā)現(xiàn),調(diào)用的是OkHttpClient來進(jìn)行數(shù)據(jù)的拉?。?br>

4.3 自定義處理的Module
上面我們分析了如何定義ModuleLoader來關(guān)聯(lián)已有的Module和最終的數(shù)據(jù)類型,下面我們介紹一些如何定義自己的Model,也就是前面在基礎(chǔ)介紹中,所說的load(Module)方法。
- 第一步:定義
Module的接口
public interface AutoSizeModel {
String requestSizeUrl(int width, int height);
}
- 第二步:實(shí)現(xiàn)
Module接口
public class AutoSizeModelImpl implements AutoSizeModel {
String mUrl;
public AutoSizeModelImpl(String url) {
mUrl = url;
}
@Override
public String requestSizeUrl(int width, int height) {
return mUrl;
}
}
- 第三步:定義
ModuleLoader和ModuleLoader.Factory
public class AutoSizeModelLoader extends BaseGlideUrlLoader<AutoSizeModel> {
public static class Factory implements ModelLoaderFactory<AutoSizeModel, InputStream> {
@Override
public ModelLoader<AutoSizeModel, InputStream> build(Context context, GenericLoaderFactory factories) {
return new AutoSizeModelLoader(context);
}
@Override
public void teardown() {}
}
public AutoSizeModelLoader(Context context) {
super(context);
}
@Override
protected String getUrl(AutoSizeModel model, int width, int height) {
return model.requestSizeUrl(width, height);
}
}
- 第四步:在
CustomGlideModule中進(jìn)行關(guān)聯(lián):
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//通過builder.setXXX進(jìn)行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通過glide.register進(jìn)行配置.
glide.register(AutoSizeModel.class, InputStream.class, new AutoSizeModelLoader.Factory());
}
}
- 第五步:調(diào)用
public void loadCustomModule(View view) {
AutoSizeModelImpl autoSizeModel = new AutoSizeModelImpl("http://i.imgur.com/DvpvklR.png");
Glide.with(this)
.load(autoSizeModel)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(mImageView);
}
4.4 動(dòng)態(tài)指定ModelLoader
在上面的例子中,我們是在自定義的CustomGlideModule中指定了Model和ModuleLoader的關(guān)聯(lián),當(dāng)然,我們也可以采用動(dòng)態(tài)指定ModelLoader的方法,也就是說,我們?nèi)サ?code>4.3中的第四步,并把第五步改成下面這樣:
public void loadDynamicModule(View view) {
AutoSizeModelImpl autoSizeModel = new AutoSizeModelImpl("http://i.imgur.com/DvpvklR.png");
AutoSizeModelLoader autoSizeModelLoader = new AutoSizeModelLoader(this);
Glide.with(this)
.using(autoSizeModelLoader)
.load(autoSizeModel)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(mImageView);
}
使用using方法,我們就可以在運(yùn)行時(shí)根據(jù)情況為同一個(gè)Module選擇不同類型的ModuleLoader了。
五、小結(jié)
這也是我們Glide學(xué)習(xí)的最后一章,所有的源碼都可以從下面的鏈接中找到: