Glide加载https图片

本文介绍了如何解决使用Glide加载自签名HTTPS图片无法显示的问题。通过定制Glide的OkHttp网络层,创建一个跳过证书检查的OkHttpClient,实现了加载HTTPS图片的功能。详细步骤包括添加依赖、定制GlideModule以及使用忽略证书检查的OkHttpClient。

        最近公司将项目的图片地址都改为了自签名的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>

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就可以完成下载动作了呢?”(重要的事再强调一遍)。


二、Glide定制忽略证书的OkHttpClient

        上面已经整体走完了“com.github.bumptech.glide:okhttp-integration:1.4.0@aar”中的3个类是怎样完成okhttp定制的流程,其中细节不必关注太多,最后留下的问题点就是如何将忽略证书检查的okhttpclient整合进去,上面已找到client的最初入口在SimpleGlideModuleregisterComponents()方法内,于是带着问题百度了一把,不得不说运气还是不错的,输入GlideModule网上就有着大把文章来介绍这个类的使用,其中更多的是去介绍定制applyOptions方法来处理图片质量和缓存等,这里就不过多介绍了。而registerComponents()就是用来定制网络库的方法,也就是上面我们找到的client的最初入口,这时是不是有一种豁然开朗的感觉。

使用:

        在项目中创建SimpleGlideModule类(类名可改),在registerComponents()方法中传入忽略证书检查的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证书问题,这**顿时我就不爽了,从头到尾按套路走下来,没毛病啊!可它就是除了问题抓狂


三、最终方案
        遇到这样蛋疼的问题后,最后决定用蛋疼的方法来解决它,整个okhttp的整合流程下来不过就3个类吗,SimpleGlideModule,OkHttpUrlLoader,OkHttpStreamFetcher,我决定把它的源码全复制出来跑,于是我在项目中创建了这3个类,删除了build.gradle 中的依赖:
<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没能关联进去。


流程总结:

        1、在build.gradle中添加依赖:
<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>

        2、在项目中创建对应的3个类,将一面源码贴入,并在SimpleGlideModule的registerComponents()方法中传入已忽略证书的OkHttpClient;
        3、删除在build.gradle中依赖:'
compile 'com.github.bumptech.glide:okhttp-integration:1.4.0@aar';
        4、在Android.Manifest中配置:
<span style="font-size:14px;"><strong><meta-data
            android:name="com.***.***.***.SimpleGlideModule"
            android:value="GlideModule" /</strong></span>
        整个流程比较简单,只是有些细节在开始没注意,最后花了一天时间终于搞定了,希望对有这个问题的同学有所帮助。
        最后附上demo地址:http://download.csdn.net/detail/a3100293282/9841023



评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值