Glide - Module 實例:接受自簽名證書的 HTTPS
來自: http://mrfu.me/2016/02/28/Glide_Module_Example_Accepting_Self-Signed_HTTPS_Certificates/
在上篇博客中,你已經學會了 GlideModules 的基礎。它們提供了一個易于訪問的一些 Glide 的核心的基礎功能。你通過實現和聲明 GlideModules 可以快速修改 Glide 的行為。上周我們也將圖像的質量改的更高了通過使用 applyOptions() 方法。這周,我們會用其他方法: registerComponents() ,去修改 Glide 網絡棧從 self-signed HTTPS 服務器的接收連接并得到圖片。
Glide 系列預覽
用 GlideModule 修改 Glide
在繼續閱讀前,請確保你已經閱讀并理解了 之前的博客 關于 GlideModule 的。我們不會在這個博客中繼續說它的基礎知識。相反,我們要跳過這個問題。所以確保你已經更新了你的 GlideModule 的基礎知識。
你已經知道 GlideModule 提供給你兩個方法去改變行為。上周,我們看了第一個方法 applyOptions() 。這周我們會用另外一個方法 registerComponents() ,去設置不同的網絡庫。默認情況下,Glide 內部使用了標準的 HTTPURLConnection 去下載圖片。Glide 也提供了兩個 集合庫 。這三個都一個非常楊格的安全設置,這很好。唯一的缺點可能是當你的圖片從服務端獲取時,是使用 HTTPS ,且是自簽名的(self-signed)。這時 Glide 不會下載或顯示圖片了,因為自簽名的證書被認為是一個安全的問題。
不安全的 OKHttpClient
因此,你需要去實現自己的網絡棧,它接受自簽名證書。幸運的是,我們 之前 已經實現了一個“不安全” 的 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 去鏈接數據模型到一個具體的數據類型。在我們的實例中,我們要去創建一個 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 整合庫 中給了我們一個很好的模板:
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 的緩存行為。
</div>