Glide范例源码分析

Glide最简单的使用应该就是下边的代码:

Glide.with(Context).load("url").into(ImageView);

开发者使用起来非常简单,但里面大有文章。
之前看了Retrofit、Volley、OkHttp库源码都感觉自己看下来没有太大问题。难度不算大。但Glide的源码如果你有能力不需要其他资源能看下来的确能力很了得。因为Glide的源码复杂程度绝对在上面说的几个框架之上。
在根据上边范例代码看源码过程中我画了个UML图。基本上都不够画,只能在into部分以最简单的方式呈现出来。上图:

Glide范例源码分析_第1张图片
GlideUML.png

大致说一下这个图的三大部分就是:

  • .with(Context)
  • .load("url")
  • .into(ImageView)

.with(Context)

Glide范例源码分析_第2张图片
Paste_Image.png

我们开始讲解with部分:

Glide范例源码分析_第3张图片
Paste_Image.png

Glide的with有5个重载方法。其实都可以归类为当在非主线程调用with(),通通走with(Context);而在主线程调用就看调用哪个一般两步走:

Glide范例源码分析_第4张图片
Paste_Image.png

哪两步:就是红色长方形框部分get(FragmentActivity)或get(Activity);
而红色圈圈其实最终走getApplicationManager(context)

getApplicationManager(context)

该方法属于工作线程调用with或with里面的参数是Application的Context

Glide范例源码分析_第5张图片
Paste_Image.png

先讲这个方法是因为它比较简单就是创建一个ResquestManager,传进两个参数:ApplicationLifecycle和EmptyRequestManagerTreeNode

  • ApplicationLifecycle 其实看名字大概就知道它是监听Application的
  • EmptyRequestManagerTreeNode这个因为是Application所以其实它是什么也没有做。
Glide范例源码分析_第6张图片
Paste_Image.png

get((Activity) context);

Glide范例源码分析_第7张图片
Paste_Image.png
Glide范例源码分析_第8张图片
Paste_Image.png

只看截图应该知道大概干了什么事情那吧:

  • 创建RequestManagerFragment
  • 在该fragment获取lifecycle,和RequestManagerTreeNode

其实get(FragmentActivity activity)也是大致做这样的事情:

Glide范例源码分析_第9张图片
Paste_Image.png

为什么要分开做主要是因为在android中有两种fragment,一种是v4和另外一种是非v4包的。

Lifecycle和RequestManagerTreeNode分别干什么

  • ActivityFragmentLifecycle 实现 Lifecycle接口
    主要就是管理所有实现Lifecycle的接口。
Glide范例源码分析_第10张图片
Paste_Image.png

将所有Lifecycle的接口放进WeakHashMap上,然后分不同情况调用接口的onStart、onStop、onDestroy

  • RequestManagerTreeNode
    RequestManagerTreeNode有两个实现它接口的类:SupportFragmentRequestManagerTreeNode和FragmentRequestManagerTreeNode
    其实作用都是一样就是收集它们层级的RequestManager。因为我们在每个Activity或Fragment调用Glide.with(this);就会创建一个RequestManager。而RequestManagerTreeNode的作用就是获取RequestManager的集合Set。
Glide范例源码分析_第11张图片
Paste_Image.png

RequestManager的创建过程:

Glide范例源码分析_第12张图片
Paste_Image.png
Glide范例源码分析_第13张图片
Paste_Image.png

添加ActivityFragmentLifecycle、RequestManagerTreeNode、RequestTracker、ConnectivityMonitorFactory;
如果在主线程直接把RequestManager放进ActivityFragmentLifecycle,非主线程通过Handler post进去。为了就是和生命周期同步。
这里有两个类:RequestTracker和ConnectivityMonitorFactory

  • RequestTracker其实就是有点和ActivityFragmentLifecycle的功能类似就是将Request收集起来,进行统一管理。
Glide范例源码分析_第14张图片
Paste_Image.png
Glide范例源码分析_第15张图片
Paste_Image.png
Glide范例源码分析_第16张图片
Paste_Image.png
  • ConnectivityMonitorFactory 的主要作用就是监听网络情况。而监听网络主要通过广播。
    ConnectivityMonitorFactory会根据Manifest文件中是否有添加userPermission对网络进行监听;
Glide范例源码分析_第17张图片
Paste_Image.png
Glide范例源码分析_第18张图片
Paste_Image.png
Glide范例源码分析_第19张图片
Paste_Image.png

整个with阶段Glide主要就是做了这些东西:

  • 创建一个空的Fragment对生命周期进行监听,在onStart执行request.onStart();
  • 通过TreeNode对每个Fragment都进行管理;
  • 通过RequestTracker对每个Request进行管理;
  • 网络监听,通过广播对网络进行监听;
  • Glide对象的创建,会在后面交待清楚;
    缓存方面的操作在本文中不打算分析,以后有机会在学习。
    下面看看load(url)部分

.load(url)

Glide范例源码分析_第20张图片
Paste_Image.png

RequestManager的load()方法:

Glide范例源码分析_第21张图片
Paste_Image.png

我们就看load(string)

Glide范例源码分析_第22张图片
Paste_Image.png

返回一个DrawableTypeRequest

  • fromString()
Glide范例源码分析_第23张图片
Paste_Image.png
Glide范例源码分析_第24张图片
Paste_Image.png

可以看到最终是new一个DrawableTypeRequest
这里开始有点复杂了,开始分析源码的时候,我就是卡在这里。后面大概看了两篇博文,解决了我一点疑惑。最后我会贴上两篇博文的链接出来。

这里留意一下泛型,后面会出现四个泛型的对象,到时更晕

Paste_Image.png

这里的泛型T为String类型。当然主要看它传入什么类型。现在本博文以String为例;
这里有个ModelLoader对象,后面代码会经常出现。

现在返回来看看Glide对象的构建:

Glide范例源码分析_第25张图片
Paste_Image.png
Glide范例源码分析_第26张图片
Paste_Image.png
Glide范例源码分析_第27张图片
Paste_Image.png
Glide范例源码分析_第28张图片
Paste_Image.png

代码有点多,前面截图的第一张就不说了,就是准备一些缓存的和BitmapPool,这里Glide创建了一堆loader、transcoder等东西,其实就是为load()方法做铺垫的。Glide框架这么强大,就源于这堆loader、transcoder;当然还有其他的功能;
注册了这么多loader或transcoder怎么管理呢?
主要通过GenericLoaderFactory和ModelLoader

Glide范例源码分析_第29张图片
Paste_Image.png
Glide范例源码分析_第30张图片
Paste_Image.png

这里截了一小部分,有兴趣的研究可以看看源码。其实主要就是通过
ModelLoader里面的两个泛型做key值获取到对应的ModelLoader具体对象。

  • 现在看回去刚才的loadGeneric方法:

ModelLoader streamModelLoader = Glide.buildStreamModelLoader(modelClass, context);

Glide范例源码分析_第31张图片
Paste_Image.png

一路跟下去最终就是调用刚才说的GenericLoaderFactory的buildModelLoader。这里我们可以根据泛型知道对应的ModelLoader:String InputStream

Paste_Image.png

这里需要一直跟下去,其实你就会发现最终是
HttpUrlGlideUrlLoader 非截图的StreamStringLoader;

相同的方法就会发现

ModelLoader fileDescriptorModelLoader =
Glide.buildFileDescriptorModelLoader(modelClass, context);

fileDescriptorModelLoader 是 FileDescriptorUriLoader

最后是new DrawableTypeRequest对象
optionsApplier.apply其实也是返回DrawableTypeRequest,所以看DrawableTypeRequest对象构建:

Glide范例源码分析_第32张图片
Paste_Image.png

这里传了一大堆参数其实,很多参数是我们之前讲过的,这里就不介绍了。这里主要看buildProvider方法:
它是返回一个FixedLoadProvider,
这里buildProvider主要看:下面部分

Glide范例源码分析_第33张图片
Paste_Image.png

transcoder为 GlideBitmapDrawableTranscoder
modelLoader为 ImageVideoModelLoader 通过 HttpUrlGlideUrlLoader和 FileDescriptorUriLoader转换出来的。
dataLoadProvider为 ImageVideoGifDrawableLoadProvider

这里记录一下DrawableTypeRequest各个Loader、Provider待会会用得到:

  • LoadProvider:FixedLoadProvider
  • streamModelLoader :HttpUrlGlideUrlLoader
  • fileDescriptorModelLoader:FileDescriptorUriLoader
  • ResourceTranscoder : GlideBitmapDrawableTranscoder
  • DataLoadProvider : ImageVideoGifDrawableLoadProvider
  • modelLoader : ImageVideoModelLoader

基本上load()方法就是根据ModelLoader和传入在Glide注册的Loader,Provider选取各种需要的Loader和Provider。

into(ImageView)

Glide范例源码分析_第34张图片
Paste_Image.png
  • DrawableTypeRequest.into(ImageView)
    DrawableTypeRequest并没有into方法,它的父类DrawableRequestBuilder有:
Glide范例源码分析_第35张图片
Paste_Image.png

继续看父类GenericRequestBuilder的into方法:

Glide范例源码分析_第36张图片
Paste_Image.png

switch语句是设置ImageVIew的ScaleType,看最后一句:

into(glide.buildImageViewTarget(view, transcodeClass));

返回一句Target,Target是一个接口;
先看看glide.buildImageViewTarget(view,transcodeClass);这句代码;
这里的transcodeClass是 GlideDrawable.class

Glide范例源码分析_第37张图片
Paste_Image.png

glide.buildImageViewTarget(view, transcodeClass)

Glide范例源码分析_第38张图片
Paste_Image.png

代码一直跟落去会看到上面截图;
返回Target;
这里其实你一直跟落去GlideDrawableImageViewTarget会发现是通过view获取宽高等信息。关键是看泛型是什么东西:
其实不难发现泛型Z就是:GlideDrawable.class
这里不多作解释;
即返回的Target为:GlideDrawableImageViewTarget
那么into最终其实就是into(GlideDrawableImageViewTarget);

Glide范例源码分析_第39张图片
Paste_Image.png
Glide范例源码分析_第40张图片
Paste_Image.png

关键看红色框框的调用,前面是一些非空判断和清空资源重用资源;

Request request = buildRequest(target);

Glide范例源码分析_第41张图片
Paste_Image.png
Glide范例源码分析_第42张图片
Paste_Image.png

第二个截图是GenericRequestBuilder.buildRequestRecursive()方法调用因为前面 代码是对缩略图的处理可以忽略;
看最后一句:

return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);

private Request obtainRequest(Target target, float sizeMultiplier, Priority priority,
            RequestCoordinator requestCoordinator) {
        return GenericRequest.obtain(
                loadProvider,
                model,
                signature,
                context,
                priority,
                target,
                sizeMultiplier,
                placeholderDrawable,
                placeholderId,
                errorPlaceholder,
                errorId,
                fallbackDrawable,
                fallbackResource,
                requestListener,
                requestCoordinator,
                glide.getEngine(),
                transformation,
                transcodeClass,
                isCacheable,
                animationFactory,
                overrideWidth,
                overrideHeight,
                diskCacheStrategy);
    }

其实最终就是构建一个Request

Glide范例源码分析_第43张图片
Paste_Image.png

Request是什么其实就是加载图片的请求;前面的RequestManager就是负责管理Request的;
太多参数只能截部分图;
你会看到四个泛型分别是

Glide范例源码分析_第44张图片
Paste_Image.png

从我们的例子里,

  • A String
  • T ImageVideoWrapper
  • Z GifBitmapWrapper
  • R GlideDrawable

继续往下看

Glide范例源码分析_第45张图片
Paste_Image.png

Request创建完成后就用target set Request;
然后设置监听器
最后用RequestTracker运行Request;

Glide范例源码分析_第46张图片
Paste_Image.png

看Request.begin()
上面已经说了真正实现Request的是GenericRequest
所以看GenericRequest的begin()

Glide范例源码分析_第47张图片
Paste_Image.png

主要看

onSizeReady(overrideWidth, overrideHeight);

所有谜底都在这里

  • 包括线程;
  • 网络执行
  • 回调
Glide范例源码分析_第48张图片
Paste_Image.png
Glide范例源码分析_第49张图片
Paste_Image.png

前面的取宽高其实是缩略图的宽高;
主要看下面的代码:

ModelLoader modelLoader = loadProvider.getModelLoader();
final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height);

这里的modelLoader 即 ImageVideoModelLoader
dataFetcher 即 ImageVideoFetcher

Glide范例源码分析_第50张图片
Paste_Image.png

这个截图是上面load(url)阶段时候记录的。现在用得上啦吧。说Glide源码复杂就在这里,太多这些loader,provider的东西了。

继续....

ResourceTranscoder transcoder = loadProvider.getTranscoder();

transcoder 这里的是 GlideBitmapDrawableTranscoder;上面也提到过;

最后阶段了

loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);

  public  LoadStatus load(Key signature, int width, int height, DataFetcher fetcher,
            DataLoadProvider loadProvider, Transformation transformation, ResourceTranscoder transcoder,
            Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
        Util.assertMainThread();
        long startTime = LogTime.getLogTime();

        //这里的id其实就是url地址
        final String id = fetcher.getId();
        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());

        EngineResource cached = loadFromCache(key, isMemoryCacheable);
        if (cached != null) {
            cb.onResourceReady(cached);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from cache", startTime, key);
            }
            return null;
        }

        EngineResource active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
            //为下次加载做准备,
            cb.onResourceReady(active);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from active resources", startTime, key);
            }
            return null;
        }

        EngineJob current = jobs.get(key);
        if (current != null) {
            current.addCallback(cb);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Added to existing load", startTime, key);
            }
            return new LoadStatus(cb, current);
        }

        //工作线程
        EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
        DecodeJob decodeJob = new DecodeJob(key, width, height, fetcher, loadProvider, transformation,
                transcoder, diskCacheProvider, diskCacheStrategy, priority);
        EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
        jobs.put(key, engineJob);
        engineJob.addCallback(cb);
        engineJob.start(runnable);

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            logWithTimeAndKey("Started new load", startTime, key);
        }
        return new LoadStatus(cb, engineJob);
    }

大Boss来了,这里就是核心部分,什么网络加载图片,编码解码的工作都在这里完成。

由于这博文不说缓存部分所以很多代码可以忽略;
主要看:

Glide范例源码分析_第51张图片
Paste_Image.png

其实看名字都大概知道干什么的;
创建EnginJob主要处理Runnable和回调
DecodeJob就是解码
最终new LoadStatus就是更新Request的状态

这里主要看看Runnable的代码:

Glide范例源码分析_第52张图片
Paste_Image.png

上面截图是EnginRunnable的run方法

Glide范例源码分析_第53张图片
Paste_Image.png
Glide范例源码分析_第54张图片
Paste_Image.png
Glide范例源码分析_第55张图片
Paste_Image.png
Glide范例源码分析_第56张图片
Paste_Image.png

这里的fetcher即DataFetcher,实现DataFetcher是 ImageVideoModelLoader,而ImageVideoModelLoader的loadData();

Glide范例源码分析_第57张图片
Paste_Image.png
Glide范例源码分析_第58张图片
Paste_Image.png

这里的streamFetcher 就是HttpUrlFetcher

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map headers)
            throws IOException {
        if (redirects >= MAXIMUM_REDIRECTS) {
            throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
        } else {
            // Comparing the URLs using .equals performs additional network I/O and is generally broken.
            // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
            try {
                if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
                    throw new IOException("In re-direct loop");
                }
            } catch (URISyntaxException e) {
                // Do nothing, this is best effort.
            }
        }
        urlConnection = connectionFactory.build(url);
        for (Map.Entry headerEntry : headers.entrySet()) {
          urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
        }
        urlConnection.setConnectTimeout(2500);
        urlConnection.setReadTimeout(2500);
        urlConnection.setUseCaches(false);
        urlConnection.setDoInput(true);

        // Connect explicitly to avoid errors in decoders if connection fails.
        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());
        }
    }

加载图片资源由HttpUrlFetcher的loadData完成。
最终返回 ImageVideoFetcher
往下看:

Glide范例源码分析_第59张图片
Paste_Image.png

解码部分:loadProvider即ImageVideoGifDrawableLoadProvider
不明白怎么找可以看前面解释;
最终你可以跟到是GifBitmapWrapperResourceDecoder的decode方法:

Glide范例源码分析_第60张图片
Paste_Image.png
Glide范例源码分析_第61张图片
Paste_Image.png
Glide范例源码分析_第62张图片
Paste_Image.png

GifBitmapWrapperResourceDecoder这个类就是解释图片的。

Glide范例源码分析_第63张图片
Paste_Image.png

这里看注解,大概意思是通过输入流的开始部分的2048大小就可以判断这张图是gif还是其他格式的。
如果不是gif就通过ImageVideoDataLoadProvider继续完成解码工作;至于为什么是ImageVideoDataLoadProvider你可以通过之前找provider或dataloader的方法找。

Glide范例源码分析_第64张图片
Paste_Image.png

真正解码是通过该类完成。

Glide范例源码分析_第65张图片
Paste_Image.png

这里getStream就是从网络获取到的InputSteam。
最终你可以找到StreamBitmapDecoder的decode()

Glide范例源码分析_第66张图片
Paste_Image.png

downsampler.decode()代码有点多就贴出来了:

 @Override
    public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
        final ByteArrayPool byteArrayPool = ByteArrayPool.get();
        final byte[] bytesForOptions = byteArrayPool.getBytes();
        final byte[] bytesForStream = byteArrayPool.getBytes();
        final BitmapFactory.Options options = getDefaultOptions();

        // Use to fix the mark limit to avoid allocating buffers that fit entire images.
        RecyclableBufferedInputStream bufferedStream = new RecyclableBufferedInputStream(
                is, bytesForStream);
        // Use to retrieve exceptions thrown while reading.
        // TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a way to determine
        // if a Bitmap is partially decoded, consider removing.
        ExceptionCatchingInputStream exceptionStream =
                ExceptionCatchingInputStream.obtain(bufferedStream);
        // Use to read data.
        // Ensures that we can always reset after reading an image header so that we can still attempt to decode the
        // full image even when the header decode fails and/or overflows our read buffer. See #283.
        MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
        try {
            exceptionStream.mark(MARK_POSITION);
            int orientation = 0;
            try {
                orientation = new ImageHeaderParser(exceptionStream).getOrientation();
            } catch (IOException e) {
                if (Log.isLoggable(TAG, Log.WARN)) {
                    Log.w(TAG, "Cannot determine the image orientation from header", e);
                }
            } finally {
                try {
                    exceptionStream.reset();
                } catch (IOException e) {
                    if (Log.isLoggable(TAG, Log.WARN)) {
                        Log.w(TAG, "Cannot reset the input stream", e);
                    }
                }
            }

            options.inTempStorage = bytesForOptions;

            final int[] inDimens = getDimensions(invalidatingStream, bufferedStream, options);
            final int inWidth = inDimens[0];
            final int inHeight = inDimens[1];

            final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
            final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);

            final Bitmap downsampled =
                    downsampleWithSize(invalidatingStream, bufferedStream, options, pool, inWidth, inHeight, sampleSize,
                            decodeFormat);

            // BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
            // and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
            // we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
            final Exception streamException = exceptionStream.getException();
            if (streamException != null) {
                throw new RuntimeException(streamException);
            }

            Bitmap rotated = null;
            if (downsampled != null) {
                rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);

                if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
                    downsampled.recycle();
                }
            }

            return rotated;
        } finally {
            byteArrayPool.releaseBytes(bytesForOptions);
            byteArrayPool.releaseBytes(bytesForStream);
            exceptionStream.release();
            releaseOptions(options);
        }
    }

是Downsampler的decode方法
最终如果是非gif格式的图片返回:GifBitmapWrapper

最终获取到非gif格式的bitmap放入Imageview上。

由于看Glide源码之前我是看了Volley、Retrofit、OkHttp这三个的源码感觉还好,所以就直接在没有看其他资源的基础上看Glide的源码。但看到with阶段就有困难,卡在Glide是通过什么监听Activity的生命周期,然后看了看郭霖的介绍Glide的第二篇开始部分 Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程知道是通过构建空的fragment实现的。因为是第一次接触通过这样的方法实现监听Activity生命周期,所以我觉得这个是Glide的一大优点。值得我们学习。
看到load(url)阶段卡住,因为当时没有发现Glide对象的创建注册了很多provider、loader,decoder之类。主要是看源码不够细心。如是看了此篇文章Glide加载图片流程(Part One),这里其实主要看表格部分,它给了我启发。之后,我细致地看Glide对象构建就知道是通过一个接口加对象管理一大堆provider、loader,decoder。

最后想说看源码一定要:

  • 细心
  • 像郭霖大神所说要有目的地看源码,不然会走进了森林,走不出来。
  • 耐心: 人家郭大神看了一个月Glide的源码,所以你懂得~~~哈哈

题外话一下AOSP带给我们很多值得学习的地方如:

  • Volley将请求分为缓存队列和网络队列管理;
  • Retrofit通过注解、集合对Factory的整理、建造者模式、动态代理实现高度解耦
  • OkHttp库的递归迭代实现各个拦截的功能独立,连续调用;静态代码块实现对旧版本代码的调用

你可能感兴趣的:(Glide范例源码分析)