Glide 网络请求分析一(3.7.0为例 2)

Glide 加载图片,一般都是一行代码就搞定了,比如说在 Activity 中,简化一点的代码 Glide.with(this).load("http://p1.pstatp.com/large/166200019850062839d3").into(iv);这里面有三个方法,with() 方法上一章讲了一个例子,现在重点看下 load() 的方法,Glide.with(this) 返回的对象是 RequestManager,它的方法代码为

    public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
    }

这里有两个方法,一个是 fromString(),另外一个是 load(string),我们先看第一个,这里面是逻辑的重点
    

    public DrawableTypeRequest<String> fromString() {
        return loadGeneric(String.class);
    }
    
    private <T> DrawableTypeRequest<T> loadGeneric(Class<T> modelClass) {
        ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);
        ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader =
                Glide.buildFileDescriptorModelLoader(modelClass, context);
        if (modelClass != null && streamModelLoader == null && fileDescriptorModelLoader == null) {
            throw new IllegalArgumentException("Unknown type " + modelClass + ". You must provide a Model of a type for"
                    + " which there is a registered ModelLoader, if you are using a custom model, you must first call"
                    + " Glide#register with a ModelLoaderFactory for your custom model class");
        }

        return optionsApplier.apply(
                new DrawableTypeRequest<T>(modelClass, streamModelLoader, fileDescriptorModelLoader, context,
                        glide, requestTracker, lifecycle, optionsApplier));
    }

loadGeneric(Class<T> modelClass) 中的形参是 String 类型的 Class,注意它的第一行代码返回的对象 ModelLoader,这是个接口

public interface ModelLoader<T, Y> {
    DataFetcher<Y> getResourceFetcher(T model, int width, int height);
}

由于加载图片由比较多的种类,比如从 drawable 文件、asset 文件、网络、手机SDK 等等,所以Glide中抽象了具体的功能, ModelLoader 泛型 <T, Y> ,表示传进去一个类型,返回的是另外一种,就像从网络获取图片,传进去的是 String, 返回的是流,然后转换为Drawable或Bitmap等等;我们看看 ModelLoader 是怎么创建的,看Glide中的 buildStreamModelLoader() 方法
    

    public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
        return buildModelLoader(modelClass, InputStream.class, context);
    }

    public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass, Context context) {
        return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass);
    }  

    private GenericLoaderFactory getLoaderFactory() {
        return loaderFactory;
    }   
    private final GenericLoaderFactory loaderFactory;


    
这里注意,最终调用的是 GenericLoaderFactory 的 buildModelLoader(modelClass, resourceClass) 方法,此时 modelClass 是 String.class,resourceClass 是 InputStream.class 类型; GenericLoaderFactory 这个类不复杂,就是一个缓存数据的类,它把数据进行了分类,装到了 Map 集合中,由于 Glide 是单例全局的,所以在Glide中的 loaderFactory 对象,也是全局的,我把GenericLoaderFactory的代码简化下

public class GenericLoaderFactory {

    private final Map<Class, Map<Class, ModelLoaderFactory>> modelClassToResourceFactories = new HashMap();
    private final Map<Class, Map<Class, ModelLoader>> cachedModelLoaders = new HashMap();

    public synchronized <T, Y> ModelLoaderFactory<T, Y> register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
        ...

        Map<Class, ModelLoaderFactory> resourceToFactories = modelClassToResourceFactories.get(modelClass);
        if (resourceToFactories == null) {
            resourceToFactories = new HashMap<>();
            modelClassToResourceFactories.put(modelClass, resourceToFactories);
        }
        ModelLoaderFactory previous = resourceToFactories.put(resourceClass, factory);

        ...
        return previous;
    }

     public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass, Context context) {
        return buildModelLoader(modelClass, resourceClass);
    }  
    
    public synchronized <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass) {
        // 从缓存中取,如果有,直接返回
        ...

        final ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass);
        if (factory != null) {
            result = factory.build(context, this);
            cacheModelLoader(modelClass, resourceClass, result);
        } else {
            cacheNullLoader(modelClass, resourceClass);
        }
        return result;
    }

    // 缓存数据,ModelLoader
    private <T, Y> void cacheModelLoader(Class<T> modelClass, Class<Y> resourceClass, ModelLoader<T, Y> modelLoader) {
        Map<Class, ModelLoader> resourceToLoaders = cachedModelLoaders.get(modelClass);
        if (resourceToLoaders == null) {
            resourceToLoaders = new HashMap();
            cachedModelLoaders.put(modelClass, resourceToLoaders);
        }
        resourceToLoaders.put(resourceClass, modelLoader);
    }

    private <T, Y> ModelLoaderFactory<T, Y> getFactory(Class<T> modelClass, Class<Y> resourceClass) {
        Map<Class, ModelLoaderFactory> resourceToFactories = modelClassToResourceFactories.get(modelClass);
        ModelLoaderFactory result = null;
        if (resourceToFactories != null) {
            result = resourceToFactories.get(resourceClass);
        }

        if (result == null) {
            for (Class<? super T> registeredModelClass : modelClassToResourceFactories.keySet()) {
                if (registeredModelClass.isAssignableFrom(modelClass)) {
                    Map<Class, ModelLoaderFactory> currentResourceToFactories = modelClassToResourceFactories.get(registeredModelClass);
                    if (currentResourceToFactories != null) {
                        result = currentResourceToFactories.get(resourceClass);
                        if (result != null) {
                            break;
                        }
                    }
                }
            }
        }

        return result;
    }   
         

}

看它里面的 buildModelLoader() 方法,其实注意两行就行了, getFactory(modelClass, resourceClass) 和 factory.build(context, this), getFactory() 是获取 ModelLoader的工厂类,也是先从集合中获取,如果为null,则会找 modelClass 的父类对应的 ModelLoaderFactory,那么 modelClassToResourceFactories 集合中的数据是怎么来的呢? Glide 的构造方法中会先添加一些封装好的数据

    Glide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat) {
        ...

        register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
        register(File.class, InputStream.class, new StreamFileLoader.Factory());
        register(int.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
        register(int.class, InputStream.class, new StreamResourceLoader.Factory());
        register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
        register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
        register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
        register(String.class, InputStream.class, new StreamStringLoader.Factory());
        register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
        register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
        register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
        register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory());
        register(byte[].class, InputStream.class, new StreamByteArrayLoader.Factory());

        ...
    }

    public <T, Y> void register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
        ModelLoaderFactory<T, Y> removed = loaderFactory.register(modelClass, resourceClass, factory);
        if (removed != null) {
            removed.teardown();
        }
    }

Glide 默认已经提供了一系列的工厂类。ModelLoaderFactory<T, Y> factory = getFactory(modelClass, resourceClass) 方法中,根据两个形参类型,可以确定 factory 对应 register(String.class, InputStream.class, new StreamStringLoader.Factory()) 中的  StreamStringLoader.Factory(),然后亮点来了,result = factory.build(context, this) 这行代码比较有意思,里面会形成一条串串,看看 StreamStringLoader.Factory() 的代码

public class StreamStringLoader extends StringLoader<InputStream> implements StreamModelLoader<String> {

    public static class Factory implements ModelLoaderFactory<String, InputStream> {
        @Override
        public ModelLoader<String, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new StreamStringLoader(factories.buildModelLoader(Uri.class, InputStream.class));
        }

            ...
    }

        ...

    public StreamStringLoader(ModelLoader<Uri, InputStream> uriLoader) {
        super(uriLoader);
    }
}

到这里,看似是结束了,build() 返回的是 StreamStringLoader 对象;实际上网络加载图片时,会调用 StreamStringLoader 的 getResourceFetcher() 方法,在它的父类中,为什么会调用,下篇文章再分析,这里先记住这一点,看它父类的方法

public class StringLoader<T> implements ModelLoader<String, T> {
    private final ModelLoader<Uri, T> uriLoader;

    public StringLoader(ModelLoader<Uri, T> uriLoader) {
        this.uriLoader = uriLoader;
    }

    @Override
    public DataFetcher<T> getResourceFetcher(String model, int width, int height) {
        ...
        return uriLoader.getResourceFetcher(uri, width, height);
    }
    ...
}

这里调用的是 uriLoader 的 getResourceFetcher(uri, width, height) 方法,uriLoader 是由 StreamStringLoader 传进来的,是工厂类 build()方法中的 factories.buildModelLoader(Uri.class, InputStream.class) 返回的对象,看到这,又返回到了 GenericLoaderFactory 类中,再次 getFactory(modelClass, resourceClass) 和 factory.build(context, this) 的逻辑,这次两个形参类型是 Uri.class、InputStream.class,我们找找 Glide 构造方法中对应的工厂类 StreamUriLoader.Factory

public class StreamUriLoader extends UriLoader<InputStream> implements StreamModelLoader<Uri> {

    public static class Factory implements ModelLoaderFactory<Uri, InputStream> {

        @Override
        public ModelLoader<Uri, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new StreamUriLoader(context, factories.buildModelLoader(GlideUrl.class, InputStream.class));
        }
        ...
    }

    public StreamUriLoader(Context context, ModelLoader<GlideUrl, InputStream> urlLoader) {
        super(context, urlLoader);
    }

    ...
}

StringLoader 中的 uriLoader.getResourceFetcher(uri, width, height) 对应的就是 StreamUriLoader 的 getResourceFetcher(uri, width, height) 方法,在它的父类中

public abstract class UriLoader<T> implements ModelLoader<Uri, T> {
    private final Context context;
    private final ModelLoader<GlideUrl, T> urlLoader;

    public UriLoader(Context context, ModelLoader<GlideUrl, T> urlLoader) {
        this.context = context;
        this.urlLoader = urlLoader;
    }

    @Override
    public final DataFetcher<T> getResourceFetcher(Uri model, int width, int height) {
        final String scheme = model.getScheme();

        DataFetcher<T> result = null;
        if (isLocalUri(scheme)) {
            ...
        } else if (urlLoader != null && ("http".equals(scheme) || "https".equals(scheme))) {
            result = urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height);
        }

        return result;
    }

    ...
}

看到这,仍然是调用了 urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height) 这个方法,那么 urlLoader 是什么?仍然是 StreamUriLoader 传进来的,对象是 factories.buildModelLoader(GlideUrl.class, InputStream.class),好吧,继续去 Glide 的构造方法中对应的值,发现是 HttpUrlGlideUrlLoader.Factory ,看看代码

public class HttpUrlGlideUrlLoader implements ModelLoader<GlideUrl, InputStream> {

    private final ModelCache<GlideUrl, GlideUrl> modelCache;

    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
        private final ModelCache<GlideUrl, GlideUrl> modelCache = new ModelCache<GlideUrl, GlideUrl>(500);

        @Override
        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new HttpUrlGlideUrlLoader(modelCache);
        }
        ...
    }

    public HttpUrlGlideUrlLoader() {
        this(null);
    }

    public HttpUrlGlideUrlLoader(ModelCache<GlideUrl, GlideUrl> modelCache) {
        this.modelCache = modelCache;
    }

    @Override
    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
        GlideUrl url = model;
        if (modelCache != null) {
            url = modelCache.get(model, 0, 0);
            if (url == null) {
                modelCache.put(model, 0, 0, model);
                url = model;
            }
        }
        return new HttpUrlFetcher(url);
    }
}

这次是返回了 HttpUrlGlideUrlLoader 对象,里面没有嵌套了,urlLoader.getResourceFetcher(new GlideUrl(model.toString()), width, height) 中的 urlLoader 是 HttpUrlGlideUrlLoader 类型的,它的 getResourceFetcher() 方法,是获取 Uri 的 toString() 值,然后封装到 GlideUrl 对象中,作为参数传入,我们看下 HttpUrlGlideUrlLoader 中的 getResourceFetcher(GlideUrl model, int width, int height),发返回了个 HttpUrlFetcher 对象,看下代码

public class HttpUrlFetcher implements DataFetcher<InputStream> {
    private static final String TAG = "HttpUrlFetcher";
    private static final int MAXIMUM_REDIRECTS = 5;
    private static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();

    private final GlideUrl glideUrl;
    private final HttpUrlConnectionFactory connectionFactory;

    private HttpURLConnection urlConnection;
    private InputStream stream;
    private volatile boolean isCancelled;

    public HttpUrlFetcher(GlideUrl glideUrl) {
        this(glideUrl, DEFAULT_CONNECTION_FACTORY);
    }

    HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {
        this.glideUrl = glideUrl;
        this.connectionFactory = connectionFactory;
    }

    @Override
    public InputStream loadData(Priority priority) throws Exception {
        return loadDataWithRedirects(glideUrl.toURL(), 0 , null , glideUrl.getHeaders());
    }

    private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
            throws IOException {
        ...
        urlConnection = connectionFactory.build(url);
        for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
            urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
        }
        urlConnection.setConnectTimeout(2500);
        urlConnection.setReadTimeout(2500);
        urlConnection.setUseCaches(false);
        urlConnection.setDoInput(true);

        urlConnection.connect();
        if (isCancelled) {
            return null;
        }
        final int statusCode = urlConnection.getResponseCode();
        if (statusCode / 100 == 2) {
            return getStreamForSuccessfulRequest(urlConnection);
        } else if (statusCode / 100 == 3) {
            String redirectUrlString = urlConnection.getHeaderField("Location");
            if (TextUtils.isEmpty(redirectUrlString)) {
                throw new IOException("Received empty or null redirect url");
            }
            URL redirectUrl = new URL(url, redirectUrlString);
            return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
        } else {
            if (statusCode == -1) {
                throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
            }
            throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
        }
    }

    private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
            throws IOException {
        if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
            int contentLength = urlConnection.getContentLength();
            stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
        } else {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
            }
            stream = urlConnection.getInputStream();
        }
        return stream;
    }

    ...
}

这个类中,很明显可以看出,使用了 HttpURLConnection 来请求图片url,然后以IO流的形式接受;也就是说,从 StreamStringLoader 到 HttpUrlGlideUrlLoader,最终是用的是 HttpUrlFetcher,而源头,则是 RequestManager 中的 loadGeneric(Class<T> modelClass) 方法, Glide.buildStreamModelLoader(modelClass, context) 代码返回的对象 streamModelLoader 是 StreamStringLoader 类型,到这一步还没有执行上面分析的一串,它是在 into() 方法中执行的,后面再分析。

加载网络图片,传入 String 类型,最后会被转成 GlideUrl 类型,然后使用 HttpUrlFetcher 来请求图片,如果我们想用自定义的网络怎么办?只要想办法把 Glide 构造中 register(GlideUrl.class, InputStream.class, new HttpUrlGlideUrlLoader.Factory()) 中 GlideUrl 和 InputStream 对应的 HttpUrlGlideUrlLoader.Factory 替换掉即可,这样,通过 GlideUrl.class 和 InputStream.class 找工厂类就找到自定的,这时候返回自己的网络加载图片类就可以了,那么在哪里替换呢?Glide 是单例,看看它的方法

    public static Glide get(Context context) {
        if (glide == null) {
            synchronized (Glide.class) {
                if (glide == null) {
                    Context applicationContext = context.getApplicationContext();
                    List<GlideModule> modules = new ManifestParser(applicationContext).parse();

                    GlideBuilder builder = new GlideBuilder(applicationContext);
                    for (GlideModule module : modules) {
                        module.applyOptions(applicationContext, builder);
                    }
                    glide = builder.createGlide();
                    for (GlideModule module : modules) {
                        module.registerComponents(applicationContext, glide);
                    }
                }
            }
        }

        return glide;
    }

ManifestParser 是解析配置清单中 <application /> 节点下面的 <meta-data /> 数据的,并且指定了 android:value="GlideModule",根据value获取到key的配置,然后通过反射创建一个对象,这个对象必须实现 GlideModule 接口,glide = builder.createGlide() 创建了 Glide 之后,会遍历 modules 集合,把 glide 作为参数穿进去,调用 registerComponents() 方法,我们可以在这个方法中重新 register() 自定义的网络。 以 okhttp 为例,代码如下,比较粗糙

<application>

    <meta-data
        android:name="com.test.cn.OkHttpGlideModule"
        android:value="GlideModule"/>

</application>

public class OkHttpGlideModule 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(selfOkHttpClient)); // selfOkHttpClient 是自己定义的Okhttp
    }
}

public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {

    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
        private static volatile OkHttpClient defClient;
        private                 OkHttpClient client;

        private static OkHttpClient getClient() {
            if (defClient == null) {
                synchronized (Factory.class) {
                    if (defClient == null) {
                        defClient = new OkHttpClient();
                    }
                }
            }
            return defClient;
        }

        public Factory() {
            this(getClient());
        }
        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() {
        }
    }

    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);
    }
}

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();
        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) {
            responseBody.close();
        }
    }

    @Override
    public String getId() {
        return url.getCacheKey();
    }

    @Override
    public void cancel() {
    }
}

其实关于Okhttp获取网络IO流,有现成三方库,在项目gradle中直接配置 implementation 'com.github.bumptech.glide:okhttp3-integration:1.5.0@aar' 即可,1.5.0@aar 是版本号,可以根据自己的项目调整。


 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值