最近公司将项目的图片地址都改为了自签名的https地址,导致之前用Glide加载的图片不能正常显示。于是花了一天时间在网上找相关方法解决该问题。
首先我们知道Glide支持网络请求库的定制,Glide默认使用HttpConnection来执行网络请求,同时支持OkHttp和Volley的定制。
Glide网络库的定制非常简单方便,只需要在build.gradle 中添加对应依赖就行,Gradle 会自动合并必要的 GlideModule 到你的Android.Manifest。Glide 会认可在 manifest 中的存在,然后使用对应网库做到所有的网络连接。
OkHttp定制:
<span style="font-size:14px;">compile 'com.squareup.okhttp:okhttp:2.7.2'
compile 'com.github.bumptech.glide:glide:3.6.1'
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'</span>
<span style="font-size:14px;">compile 'com.squareup.okhttp:okhttp:2.7.2'
compile 'com.github.bumptech.glide:glide:3.6.1'
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'</span>
Volley定制:
<span style="font-size:14px;">compile 'com.github.bumptech.glide:glide:3.6.1'
compile 'com.github.bumptech.glide:volley-integration:1.3.1@aar'
compile 'com.mcxiaoke.volley:library:1.0.8'</span>下面开始进入主题:Glide定制可加载https的okhttpclient,由于我们项目使用okhttp来完成网络请求,所以这里拿okhttp来介绍流程。
一、定制okhttp网络层
根据上面介绍,在build.gradle 中添加okhttp对应依赖。
定制过用okhttp来执行网络后,再用Glide加载https图片,图片依然不能正常显示,将Glide加载的错误日志打印出来(Glide.with(context).load("").listener(lstn)),如下:
<span style="font-size:14px;">javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:381)
at com.squareup.okhttp.internal.io.RealConnection.connectTls(RealConnection.java:188)
at com.squareup.okhttp.internal.io.RealConnection.connectSocket(RealConnection.java:145)
at com.squareup.okhttp.internal.io.RealConnection.connect(RealConnection.java:108)
at com.squareup.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:184)
at com.squareup.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:126)
at com.squareup.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:95)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:281)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:224)
at com.squareup.okhttp.Call.getResponse(Call.java:286)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:243)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:205)
at com.squareup.okhttp.Call.execute(Call.java:80)
at com.bumptech.glide.integration.okhttp.OkHttpStreamFetcher.loadData(OkHttpStreamFetcher.java:42)
at com.bumptech.glide.integration.okhttp.OkHttpStreamFetcher.loadData(OkHttpStreamFetcher.java:19)
at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:70)
at com.bumptech.glide.load.model.ImageVideoModelLoader$ImageVideoFetcher.loadData(ImageVideoModelLoader.java:53)
at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:170)
at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128)
at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:856)
at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)</span>很明显,这个问题是由https引起的,3-14行都是okhttp报出的错误,重点看第15行错误日志,
<span style="font-size:14px;"><span style="background-color: rgb(255, 0, 0);">at com.bumptech.glide.integration.okhttp.OkHttpStreamFetcher.loadData(OkHttpStreamFetcher.java:42)</span></span>,这正是刚引入的aar包中的一个类OkHttpStreamFetcher:
<span style="font-size:14px;font-weight: normal;"><span style="font-weight: normal;">package com.bumptech.glide.integration.okhttp;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* Fetches an {@link InputStream} using the okhttp library.
*/
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
}
}</span></span>找到42行报错代码(排版问题,上面的50行):Response response = client.newCall(request).execute();
用过okhttp的同学应该能看出,这就是一个okhttp的网络请求操作,到这里有个猜想:“那是不是可以用一个跳过所有证书检查的okhttpClient来替换掉这个client就可以完成下载动作了呢?”,带着这个疑问,向上继续找这个client的来源,在OkHttpStreamFetcher中client由25行构造方法传入,继续搜索OkHttpStreamFetcher这个类在哪创建,可以找到OkHttpUrlLoader:
<span style="font-size:14px;font-weight: normal;">package com.minghao.cgkx.service;
import android.content.Context;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.GenericLoaderFactory;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.squareup.okhttp.OkHttpClient;
import java.io.InputStream;
/**
* A simple model loader for fetching media over http/https using 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 = new OkHttpClient();
}
}
}
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);
}
}</span>在68行getResourceFetcher()方法中找到client传入OkHttpStreamfetcher,那该方法的client又从哪来呢,向上可以找到client由OkHttpUrlLoader的内部类Factory的52行build()方法传入,build方法内的client是OkHttpUrlLoader.Factory的一个局部变量,OkHttpUrlLoader.Factory有2个构造方法39行、46行可以为该client赋值,其中39行空构造方法,由getInternalClient()创建了一个单例client,46行由外部传入client。显然getInternalClient()创建的单例client不能满足忽略证书检查,继续查找OkHttpUrlLoader.Factory的构造方法在哪被调用,是否有传入指定的client。最后可找到SimpleGlideModule:
<span style="font-size:14px;font-weight: normal;">package com.minghao.cgkx.service;
import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.GlideModule;
import java.io.InputStream;
/**
* A {@link com.bumptech.glide.module.GlideModule} implementation to replace Glide's default
* {@link java.net.HttpURLConnection} based {@link com.bumptech.glide.load.model.ModelLoader} with an OkHttp based
* {@link com.bumptech.glide.load.model.ModelLoader}.
*
* <p>
* If you're using gradle, you can include this module simply by depending on the aar, the module will be merged
* in by manifest merger. For other build systems or for more more information, see
* {@link com.bumptech.glide.module.GlideModule}.
* </p>
*/
public class SimpleGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Do nothing.
}
@Override
public void registerComponents(Context context, Glide glide) {
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
}</span>
看到31行registerComponents()内部,构造了OkHttpUrlLoader.Factory,并且未传入client参数,也就是说默认在OkHttpUrlLoader.Factory内创建了一个单例okhttpclient来完成我们的网络请求过程。到这里就逆向的走完了Glide定制okhttp的流程,然而我们的问题还是没有得到解决,因为OkHttpUrlLoader.Factory内创建的单例okhttpclient并不能帮我们下载到https下的图片,但是我们留下了一个重要的猜想:“那是不是可以用一个跳过所有证书检查的okhttpClient来替换掉这个client就可以完成下载动作了呢?”(重要的事再强调一遍)。
<span style="font-size:14px;font-weight: normal;">package com.minghao.cgkx.service;
import android.content.Context;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.GlideModule;
import java.io.InputStream;
/**
* A {@link com.bumptech.glide.module.GlideModule} implementation to replace Glide's default
* {@link java.net.HttpURLConnection} based {@link com.bumptech.glide.load.model.ModelLoader} with an OkHttp based
* {@link com.bumptech.glide.load.model.ModelLoader}.
*
* <p>
* If you're using gradle, you can include this module simply by depending on the aar, the module will be merged
* in by manifest merger. For other build systems or for more more information, see
* {@link com.bumptech.glide.module.GlideModule}.
* </p>
*/
public class SimpleGlideModule implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Do nothing.
}
@Override
public void registerComponents(Context context, Glide glide) {
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
}</span>
二、Glide定制忽略证书的OkHttpClient
使用:
<span style="font-size:14px;"> public void registerComponents(Context context, Glide glide) {
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(HttpManager.getInstance().getClient()));
}</span>其中HttpManager.getInstance().getClient()为我们项目中的已忽略证书的okhttpclient(这里就不贴详细代码了),然后按照网上提供的方法在Android.Manifest中配置我们的GlideModule:<span style="font-size:14px;"><meta-data
android:name="com.***.***.***.SimpleGlideModule"
android:value="GlideModule" /></span>其中“com.***.***.***.SimpleGlideModule”为上面创建的SimpleGlideModule的全类名,是不是很简单,这样配置后Glide就会自动找到我们的SimpleGlideModule。到这里我们也实现了我们的猜想,是时候来看效果了。我关掉了我的编程必备程序“YD词典”,信心满满的运行了一把程序。结果不用说,从上面的语气能知道,图片还是没加载出来,Glide打印出的错误日志还是显示的SSL证书问题,这**顿时我就不爽了,从头到尾按套路走下来,没毛病啊!可它就是除了问题
。三、最终方案
<span style="font-size:14px;">compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'</span>在这3个类中加入日志后果断运行!运行过程中的半分钟还真的长,对结果的期待也就不用多说了。程序跑起来后图片果断展示了出来

。好了,问题也就可以说是解决了,至于为什么只定制SimpleGlideModule无法加载出来的原因还真不知道,感觉就是添加aar文件后,Glide总会去aar中去找SimpleGlideModule运行,而不是运行自己写的SimpleGlideModule,导致忽略正是的OkHttpClient没能关联进去。流程总结:
<span style="font-size:14px;">compile 'com.squareup.okhttp:okhttp:2.7.2'
compile 'com.github.bumptech.glide:glide:3.6.1'
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'</span><span style="font-size:14px;"><strong></strong><pre name="code" class="html"><strong>拿出'com.github.bumptech.glide:okhttp-integration:1.4.0@aar'里面3个类SimpleGlideModule,OkHttpUrlLoader,OkHttpStreamFetcher的源码;</strong></span>
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar';<span style="font-size:14px;"><strong><meta-data
android:name="com.***.***.***.SimpleGlideModule"
android:value="GlideModule" /</strong></span>
本文介绍了如何解决使用Glide加载自签名HTTPS图片无法显示的问题。通过定制Glide的OkHttp网络层,创建一个跳过证书检查的OkHttpClient,实现了加载HTTPS图片的功能。详细步骤包括添加依赖、定制GlideModule以及使用忽略证书检查的OkHttpClient。

5566

被折叠的 条评论
为什么被折叠?



