在上篇博客中,你已經學會了 `GlideModules` 的基礎。它們提供了一個易于訪問的一些 Glide 的核心的基礎功能。你通過實現和聲明 `GlideModules` 可以快速修改 Glide 的行為。上周我們也將圖像的質量改的更高了通過使用 `applyOptions()` 方法。這周,我們會用其他方法:`registerComponents()`,去修改 Glide 網絡棧從 self-signed HTTPS 服務器的接收連接并得到圖片。
**用 GlideModule 修改 Glide?**
在繼續閱讀前,請確保你已經閱讀并理解了[之前的博客](https://futurestud.io/blog/glide-customize-glide-with-modules) 關于 `GlideModule` 的。我們不會在這個博客中繼續說它的基礎知識。相反,我們要跳過這個問題。所以確保你已經更新了你的 `GlideModule` 的基礎知識。
你已經知道 `GlideModule` 提供給你兩個方法去改變行為。上周,我們看了第一個方法 `applyOptions()`。這周我們會用另外一個方法 `registerComponents()`,去設置不同的網絡庫。默認情況下,Glide 內部使用了標準的 HTTPURLConnection 去下載圖片。Glide 也提供了兩個[集合庫](https://futurestud.io/blog/glide-module-example-accepting-self-signed-https-certificates)。這三個都一個非常楊格的安全設置,這很好。唯一的缺點可能是當你的圖片從服務端獲取時,是使用 `HTTPS`,且是自簽名的(self-signed)。這時 Glide 不會下載或顯示圖片了,因為自簽名的證書被認為是一個安全的問題。
**不安全的 OKHttpClient**?
因此,你需要去實現自己的網絡棧,它接受自簽名證書。幸運的是,我們[之前](https://futurestud.io/blog/picasso-customizing-picasso-with-picasso-builder)已經實現了一個“不安全” 的 OKHttpClient。我們主要復制粘貼這個類。因為它給了我們一個常規的 `OkHttpClient`,我們這樣子來集成:
~~~
public class UnsafeOkHttpClient {
public static OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setSslSocketFactory(sslSocketFactory);
okHttpClient.setProtocols(Arrays.asList(Protocol.HTTP_1_1));
okHttpClient.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
~~~
創建 `OkHttpClient` 禁用掉所有的 SSL 證書檢查。
**整合到 Glide?**
我們的優勢是,OkHttp 整合庫為 Glide 做了幾乎相同的事情,所以我們可以跟著他們走。首先,我們需要在 `GlideModule` 中聲明我們的定制。正如你所期待的,我們要在 `registerComponents()` 方法中去做適配。我們可以調用 `.register()` 方法去改變 Glide 的基本部件。Glide 使用一個 [GlideLoader](http://bumptech.github.io/glide/javadocs/360/com/bumptech/glide/load/model/ModelLoader.html) 去鏈接數據模型到一個具體的數據類型。在我們的實例中,我們要去創建一個 `ModeLoader`,連接傳入的 URL,通過 `GlideUrl` 類來代表一個 `InputStream`。Glide 要能創建一個我們的新的 `ModeLoader`,所以我們要在 `.register()` 方法中傳遞一個工廠。
~~~
public class UnsafeOkHttpGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
}
@Override
public void registerComponents(Context context, Glide glide) {
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
}
~~~
前兩個參數是 model 類,和連接的資源類。最后一個參數是 `ModelLoaderFactory`。因此,我們不能直接設置一個 `UnsafeOkHttpClient` 實例,我們需要去創建一個 `ModelLoaderFactory`,它用 `UnsafeOkHttpClient` 來提供了一個 URL 和輸入流之前的連接。
再說一次,在 [OkHttp 整合庫](https://github.com/bumptech/glide/wiki/Integration-Libraries) 中給了我們一個很好的模板:
~~~
public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
/**
* The default factory for {@link OkHttpUrlLoader}s.
*/
public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
private static volatile OkHttpClient internalClient;
private OkHttpClient client;
private static OkHttpClient getInternalClient() {
if (internalClient == null) {
synchronized (Factory.class) {
if (internalClient == null) {
internalClient = UnsafeOkHttpClient.getUnsafeOkHttpClient();
}
}
}
return internalClient;
}
/**
* Constructor for a new Factory that runs requests using a static singleton client.
*/
public Factory() {
this(getInternalClient());
}
/**
* Constructor for a new Factory that runs requests using given client.
*/
public Factory(OkHttpClient client) {
this.client = client;
}
@Override
public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
return new OkHttpUrlLoader(client);
}
@Override
public void teardown() {
// Do nothing, this instance doesn't own the client.
}
}
private final OkHttpClient client;
public OkHttpUrlLoader(OkHttpClient client) {
this.client = client;
}
@Override
public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
return new OkHttpStreamFetcher(client, model);
}
}
~~~
在這個類中,你可以看到 `ModelLoaderFactory` 的內部構造是怎樣的。對我們來說,重要的代碼是創建 internalClient 對象:`internalClient = UnsafeOkHttpClient.getUnsafeOkHttpClient();`。
不幸的是,我們仍然需要用我們的不安全的 OKHttpClient 去連接 URL 激活輸入流。因此,我們需要另外一個類去從一個 URL 中拉取返回的輸入流:
~~~
public class OkHttpStreamFetcher implements DataFetcher<InputStream> {
private final OkHttpClient client;
private final GlideUrl url;
private InputStream stream;
private ResponseBody responseBody;
public OkHttpStreamFetcher(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;
}
@Override
public void cleanup() {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignored
}
}
if (responseBody != null) {
try {
responseBody.close();
} catch (IOException e) {
// Ignored.
}
}
}
@Override
public String getId() {
return url.getCacheKey();
}
@Override
public void cancel() {
// TODO: call cancel on the client when this method is called on a background thread. See #257
}
}
~~~
不需要知道在這個類中所有的細節。然而,你應該對于這個系統有一個大概的理解,Glide 能去替換內部的工廠組件。
**Outlook**?
在這篇博客中,你看到了對于 Glide 工作方式的一個不同的使用場景。我們已經實現了一個 “不安全” 的網絡棧,并用 `GlideModule` 中的 `registerComponents()` 方法將它集成到了 Glide 中。但這只是 Glide 配置的冰山一角而已。
下周,我們將看到 `GlideModule` 另外一個選項去改變 Glide 的緩存行為。
- 前言
- 一開始
- 二加載進階
- 三ListAdapter(ListView, GridView)
- 四占位符 和 漸現動畫
- 五圖片重設大小 和 縮放
- 六顯示 Gif 和 Video
- 七緩存基礎
- 八請求優先級
- 九縮略圖
- 十回調:SimpleTarget 和 ViewTarget 用于自定義視圖類
- 十一加載圖片到通知欄和應用小部件中
- 十二異常:調試和錯誤處理
- 十三自定義轉換
- 十四用 animate() 自定義動畫
- 十五集成網絡棧
- 十六用 Module 自定義
- 十七Module 實例:接受自簽名證書的 HTTPS
- 十八Module 實例:自定義緩存
- 十九Module 實例:用自定義尺寸優化加載的圖片
- 二十動態使用 Model Loader
- 二十一如何旋轉圖像
- 二十二系列綜述