在上篇博客中,你已经学会了 `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
- 二十一如何旋转图像
- 二十二系列综述