前面說的都是如何使用Glide
提供的接口來展現圖片資源,今天這篇,咱們來說一下如何改變Glide
的配置。android
GlideModule
首先,咱們須要一個實現了GlideModule
接口的類,重寫其中的方法來改變Glide
的配置,而後讓Glide
在構造實例的過程當中,讀取這個類中的配置信息。git
GlideModule
接口public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//經過builder.setXXX進行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//經過glide.register進行配置.
}
}
複製代碼
AndroidManifest.xml
中的<application>
標籤下定義<meta-data>
,這樣Glide
才能知道咱們定義了這麼一個類,其中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.1
所作的工做都是爲了在Glide
建立時能夠讀取咱們在兩個回調中配置的信息,咱們來看一下Glide
是如何使用這個自定義的類的,它的整個流程在Glide
的get
方法中:github
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構造出glide以前,讀取使用者自定義的配置.
module.applyOptions(applicationContext, builder);
}
glide = builder.createGlide();
//第三步
for (GlideModule module : modules) {
module.registerComponents(applicationContext, glide);
}
}
}
}
return glide;
}
複製代碼
能夠看到,整個實例化Glide
的過程分爲三步:緩存
AndroidManifest
中查找meta-data
爲GlideModule
的類,而後經過反射實例化它。Glide
會新建一個GlideBuilder
對象,它會先調用咱們自定義的GlideModule
的applyOptions
方法,並把本身傳進去,這樣,自定義的GlideModule
就能夠經過GlideBuilder
提供的接口來設置它內部的參數,在builder.createGlide()
的過程當中就會根據它內部的參數來構建Glide
,假如咱們沒有設置相應的參數,那麼在createGlide
時,就會採起默認的實現,下面就是memoryCache
的例子。//咱們在applyOptions中,能夠經過GlideBuilder的這個方法來設定本身的memoryCache.
public GlideBuilder setMemoryCache(MemoryCache memoryCache) {
this.memoryCache = memoryCache;
return this;
}
Glide createGlide() {
//若是builder中沒有設定memoryCache,那麼採用默認的實現.
if (memoryCache == null) {
memoryCache = new LruResourceCache(calculator.getMemoryCacheSize());
}
return new Glide(engine, memoryCache, bitmapPool, context, decodeFormat);
}
複製代碼
Glide
實例化完畢以後,調用自定義GlideModule
的registerComponents
,並傳入當前的Glide
實例來讓使用者註冊本身的組件,其實在Glide
實例化的過程當中已經註冊了默認的組件,若是用戶定義了相同的組件,那麼就會替換以前的。Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
//Glide默認註冊的組件.
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
,當咱們調用load(xxxx)
方法時,應該用什麼方式來獲取這個xxxx
所指向的資源。所以,咱們能夠看到register
的第一個參數就是咱們load(xxxx)
的類型,第二個參數是對應的輸入流,而第三個參數就是定義獲取資源的方式。 咱們也就分兩個部分,在第3、四節,咱們分兩部分討論這兩個回調函數的用法:applyOptions/registerComponents
。bash
Glide
是經過反射的方法來實例化GlideModule
對象的,所以自定義的GlideModule
只能有一個無參的構造方法。GlideModule
的,可是GlideModule
的讀取順序並不能保證,所以,不要在多個GlideModule
對同一個屬性進行不一樣的配置。applyOptions(Context context, GlideBuilder builder)
方法詳解在第二節中,咱們已經解釋過,這個回調方法的目的就是爲了讓使用者能經過builder
定義本身的配置,而所支持的配置也就是GlideBuilder
的setXXX
方法,它們包括:網絡
setBitmapPool(BitmapPool bitmapPool)
設置Bitmap
的緩存池,用來重用Bitmap
,須要實現BitmapPool
接口,它的默認實現是LruBitmapPool
app
setMemoryCache(MemoryCache memoryCache)
設置內存緩存,須要實現MemoryCache
接口,默認實現是LruResourceCache
。ide
setDiskCache(DiskCache.Factory diskCacheFactory)
設置磁盤緩存,須要實現DiskCache.Factory
,默認實現是InternalCacheDiskCacheFactory
函數
setResizeService(ExecutorService service)
當資源不在緩存中時,須要經過這個Executor
發起請求,默認是實現是FifoPriorityThreadPoolExecutor
。源碼分析
setDiskCacheService(ExecutorService service)
讀取磁盤緩存的服務,默認實現是FifoPriorityThreadPoolExecutor
。
setDecodeFormat(DecodeFormat decodeFormat)
用於控制Bitmap
解碼的清晰度,DecodeFormat
可選的值有PREFER_ARGB_8888/PREFER_RGB_565
,默認爲PREFER_RGB_565
。
registerComponents(Context context, Glide glide)
方法詳解registerComponents
相對來講就複雜了不少,它主要和三個接口有關:
ModelLoaderFactory
ModelLoader
DataFetcher
爲了便於理解,咱們先經過它內部一個默認Module
的實現來看一下源碼是如何實現的。
咱們選取是通用的加載普通圖片的url
的例子,它對應的註冊方法是下面這句:
Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
//註冊加載網絡url的組件.
register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
}
複製代碼
HttpUrlGlideUrlLoader.Factory
首先看一下HttpUrlGlideUrlLoader
的內部工廠類,它實現了ModelLoaderFactory<T, Y>
接口
public interface ModelLoaderFactory<T, Y> {
ModelLoader<T, Y> build(Context context, GenericLoaderFactory factories);
void teardown();
}
複製代碼
它要求咱們返回一個ModelLoader
,咱們看一下HttpUrlGlideUrlLoader.Factory
是怎麼作的,能夠看到,它返回了HttpUrlGlideUrlLoader
,而它的兩個泛型參數就是咱們register
中指定的前兩個參數類型。
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() {}
}
複製代碼
HttpUrlGlideUrlLoader
HttpUrlGlideUrlLoader
實現了ModelLoader
接口:
public interface ModelLoader<T, Y> {
DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}
複製代碼
ModelLoader
提供了一個DataFetcher
,它會去請求這個抽象模型所表示的數據:
T
:模型的類型。Y
:一個能夠被ResourceDecoder
解析出數據的表示。GlideUrl
的實現以下:
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;
}
//最主要的方法,它決定了咱們獲取數據的方式.
@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);
}
}
複製代碼
HttpUrlFetcher
DataFetcher
就是咱們讀取數據的方式,它的關鍵方法是loadData
,該loadData
的返回值就是咱們register
的第二個參數:
public interface DataFetcher<T> {
T loadData(Priority priority) throws Exception;
void cleanup();
String getId();
void cancel();
}
複製代碼
HttpUrlFetcher
實現了DataFetcher
接口,在它的loadData
方法中,經過傳入的url
發起請求,最終返回一個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 {
//就是調用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;
}
}
複製代碼
對上面作個總結,整個流程以下:經過register
傳入一個ModelLoaderFactory<T, Y>
工廠類,該工廠生產的是ModelLoader<T, Y>
,而這個ModelLoader
會根據T
返回一個DataFetcher<Y>
,在DataFetcher<Y>
中,咱們去獲取數據。(在上面的例子中T
就是GlideUrl
,Y
就是InputStream
)
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
返回一個DataFetcher
,咱們給它傳入一個OkHttpClient
實例,讓它經過OkHttpClient
發起請求。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
中註冊這個組件:
public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//經過builder.setXXX進行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//經過glide.register進行配置.
glide.register(GlideUrl.class, InputStream.class, new OkHttpGlideUrlLoader.Factory());
}
}
複製代碼
接着咱們發起一次請求,經過斷點能夠發現,調用的是OkHttpClient
來進行數據的拉取:
Module
上面咱們分析瞭如何定義ModuleLoader
來關聯已有的Module
和最終的數據類型,下面咱們介紹一些如何定義本身的Model
,也就是前面在基礎介紹中,所說的load(Module)
方法。
Module
的接口public interface AutoSizeModel {
String requestSizeUrl(int width, int height);
}
複製代碼
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
中進行關聯:public class CustomGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
//經過builder.setXXX進行配置.
}
@Override
public void registerComponents(Context context, Glide glide) {
//經過glide.register進行配置.
glide.register(AutoSizeModel.class, InputStream.class, new AutoSizeModelLoader.Factory());
}
}
複製代碼
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);
}
複製代碼
ModelLoader
在上面的例子中,咱們是在自定義的CustomGlideModule
中指定了Model
和ModuleLoader
的關聯,固然,咱們也能夠採用動態指定ModelLoader
的方法,也就是說,咱們去掉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
方法,咱們就能夠在運行時根據狀況爲同一個Module
選擇不一樣類型的ModuleLoader
了。
這也是咱們Glide
學習的最後一章,全部的源碼均可以從下面的連接中找到: