一、概述
前面說(shuō)的都是如何使用Glide
提供的接口來(lái)展示圖片資源,今天這篇,我們來(lái)講一下如何改變Glide
的配置。
二、定義GlideModule
2.1 步驟
首先,我們需要一個(gè)實(shí)現(xiàn)了GlideModule
接口的類(lèi),重寫(xiě)其中的方法來(lái)改變Glide
的配置,然后讓Glide
在構(gòu)造實(shí)例的過(guò)程中,讀取這個(gè)類(lèi)中的配置信息。
- 第一步:實(shí)現(xiàn)
GlideModule
接口
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//通過(guò)builder.setXXX進(jìn)行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通過(guò)glide.register進(jìn)行配置.
}
}
- 第二步:在
AndroidManifest.xml
中的<application>
標(biāo)簽下定義<meta-data>
,這樣Glide
才能知道我們定義了這么一個(gè)類(lèi),其中android:name
是我們自定義的GlideModule
的完整路徑,而android:value
就固定寫(xiě)死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í)可以讀取我們?cè)趦蓚€(gè)回調(diào)中配置的信息,我們來(lái)看一下Glide
是如何使用這個(gè)自定義的類(lèi)的,它的整個(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
的過(guò)程分為三步:
- 第一步:去
AndroidManifest
中查找meta-data
為GlideModule
的類(lèi),然后通過(guò)反射實(shí)例化它。 - 第二步:之后
Glide
會(huì)新建一個(gè)GlideBuilder
對(duì)象,它會(huì)先調(diào)用我們自定義的GlideModule
的applyOptions
方法,并把自己傳進(jìn)去,這樣,自定義的GlideModule
就可以通過(guò)GlideBuilder
提供的接口來(lái)設(shè)置它內(nèi)部的參數(shù),在builder.createGlide()
的過(guò)程中就會(huì)根據(jù)它內(nèi)部的參數(shù)來(lái)構(gòu)建Glide
,假如我們沒(méi)有設(shè)置相應(yīng)的參數(shù),那么在createGlide
時(shí),就會(huì)采取默認(rèn)的實(shí)現(xiàn),下面就是memoryCache
的例子。
//我們?cè)赼pplyOptions中,可以通過(guò)GlideBuilder的這個(gè)方法來(lái)設(shè)定自己的memoryCache.
public GlideBuilder setMemoryCache(MemoryCache memoryCache) {
this.memoryCache = memoryCache;
return this;
}
Glide createGlide() {
//如果builder中沒(méi)有設(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í)例來(lái)讓使用者注冊(cè)自己的組件,其實(shí)在Glide
實(shí)例化的過(guò)程中已經(jīng)注冊(cè)了默認(rèn)的組件,如果用戶(hù)定義了相同的組件,那么就會(huì)替換之前的。
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
//Glide默認(rèn)注冊(cè)的組件.
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());
}
通俗的來(lái)說(shuō),注冊(cè)組件的目的就是告訴Glide
,當(dāng)我們調(diào)用load(xxxx)
方法時(shí),應(yīng)該用什么方式來(lái)獲取這個(gè)xxxx
所指向的資源。因此,我們可以看到register
的第一個(gè)參數(shù)就是我們load(xxxx)
的類(lèi)型,第二個(gè)參數(shù)是對(duì)應(yīng)的輸入流,而第三個(gè)參數(shù)就是定義獲取資源的方式。
我們也就分兩個(gè)部分,在第三、四節(jié),我們分兩部分討論這兩個(gè)回調(diào)函數(shù)的用法:applyOptions/registerComponents
。
2.3 注意事項(xiàng)
- 由于
Glide
是通過(guò)反射的方法來(lái)實(shí)例化GlideModule
對(duì)象的,因此自定義的GlideModule
只能有一個(gè)無(wú)參的構(gòu)造方法。 - 可以看到,上面是支持配置多個(gè)
GlideModule
的,但是GlideModule
的讀取順序并不能保證,因此,不要在多個(gè)GlideModule
對(duì)同一個(gè)屬性進(jìn)行不同的配置。
三、applyOptions(Context context, GlideBuilder builder)
方法詳解
在第二節(jié)中,我們已經(jīng)解釋過(guò),這個(gè)回調(diào)方法的目的就是為了讓使用者能通過(guò)builder
定義自己的配置,而所支持的配置也就是GlideBuilder
的setXXX
方法,它們包括:
setBitmapPool(BitmapPool bitmapPool)
設(shè)置Bitmap
的緩存池,用來(lái)重用Bitmap
,需要實(shí)現(xiàn)BitmapPool
接口,它的默認(rèn)實(shí)現(xiàn)是LruBitmapPool
setMemoryCache(MemoryCache memoryCache)
設(shè)置內(nèi)存緩存,需要實(shí)現(xiàn)MemoryCache
接口,默認(rèn)實(shí)現(xiàn)是LruResourceCache
。setDiskCache(DiskCache.Factory diskCacheFactory)
設(shè)置磁盤(pán)緩存,需要實(shí)現(xiàn)DiskCache.Factory
,默認(rèn)實(shí)現(xiàn)是InternalCacheDiskCacheFactory
setResizeService(ExecutorService service)
當(dāng)資源不在緩存中時(shí),需要通過(guò)這個(gè)Executor
發(fā)起請(qǐng)求,默認(rèn)是實(shí)現(xiàn)是FifoPriorityThreadPoolExecutor
。setDiskCacheService(ExecutorService service)
讀取磁盤(pán)緩存的服務(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
相對(duì)來(lái)說(shuō)就復(fù)雜了很多,它主要和三個(gè)接口有關(guān):
ModelLoaderFactory
ModelLoader
DataFetcher
為了便于理解,我們先通過(guò)它內(nèi)部一個(gè)默認(rèn)Module
的實(shí)現(xiàn)來(lái)看一下源碼是如何實(shí)現(xiàn)的。
我們選取是通用的加載普通圖片的url
的例子,它對(duì)應(yīng)的注冊(cè)方法是下面這句:
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
//注冊(cè)加載網(wǎng)絡(luò)url的組件.
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
}
4.1 源碼分析
4.1.1 HttpUrlGlideUrlLoader.Factory
首先看一下HttpUrlGlideUrlLoader
的內(nèi)部工廠類(lè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ù)類(lèi)型。
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ì)去請(qǐng)求這個(gè)抽象模型所表示的數(shù)據(jù):
-
T
:模型的類(lèi)型。 -
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
方法中,通過(guò)傳入的url
發(fā)起請(qǐng)求,最終返回一個(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請(qǐng)求.
}
@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é)
對(duì)上面做個(gè)總結(jié),整個(gè)流程如下:通過(guò)register
傳入一個(gè)ModelLoaderFactory<T, Y>
工廠類(lèi),該工廠生產(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
下面的例子來(lái)自于這篇文章:
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í)例,讓它通過(guò)OkHttpClient
發(fā)起請(qǐng)求。
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
中注冊(cè)這個(gè)組件:
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//通過(guò)builder.setXXX進(jìn)行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通過(guò)glide.register進(jìn)行配置.
glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory());
}
}
接著我們發(fā)起一次請(qǐng)求,通過(guò)斷點(diǎn)可以發(fā)現(xiàn),調(diào)用的是OkHttpClient
來(lái)進(jìn)行數(shù)據(jù)的拉取:
4.3 自定義處理的Module
上面我們分析了如何定義ModuleLoader
來(lái)關(guān)聯(lián)已有的Module
和最終的數(shù)據(jù)類(lèi)型,下面我們介紹一些如何定義自己的Model
,也就是前面在基礎(chǔ)介紹中,所說(shuō)的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) {
//通過(guò)builder.setXXX進(jìn)行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//通過(guò)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
的方法,也就是說(shuō),我們?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
選擇不同類(lèi)型的ModuleLoader
了。
五、小結(jié)
這也是我們Glide
學(xué)習(xí)的最后一章,所有的源碼都可以從下面的鏈接中找到: