Glide源码解析之加载流程

前言

Glide 源码解析系列到目前为止刚好写了10篇,而本篇做为收篇之作终于要完成了。一开始决定写这个系列是因为面试被问到源码比较多,虽然也看过别人写的博客,但是大多数都是流程走一遍,很多细节的东西并没有掌握,一被问到就懵逼。我们做技术的不像其他行业,不懂就是真的不懂,想吹也没法吹。所以还是决定亲自撸一遍源码,不仅为了面试,而且对自己能力的提升也有好处。

开始加载

在 Glide源码解析之RequestBuilder 中我们讲了 SingleRequest 的创建,并且当生命周期为 onStart() 的时候会调用 begin() 来开始执行加载请求。

在 begin() 里首先会对当前的状态进行检查,然后判断宽高是否有设置或者能获取到。由于 ImageView 是需要经过 onLayout() 后才能确定自身宽高的,所以在这里我们先要去获取到 ImageView 的宽高。

    public synchronized void begin() {

        //防止同时执行
        if (status == Status.RUNNING) {
            throw new IllegalArgumentException("Cannot restart a running request");
        }

        //如果已经完成的则直接处理
        if (status == Status.COMPLETE) {
            onResourceReady(resource, DataSource.MEMORY_CACHE);
            return;
        }

        //等待能获取到 ImageView 的宽高
        status = Status.WAITING_FOR_SIZE;
        
        // overrideWidth 和 overrideHeight 默认为-1,所以这里为 false
        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            onSizeReady(overrideWidth, overrideHeight);     //执行点
        } else {
            target.getSize(this);
        }

        if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
                && canNotifyStatusChanged()) {
            target.onLoadStarted(getPlaceholderDrawable()); //设置占位图
        }
    }

获取宽高

由于 View 的宽高是要在 onLayout() 之后才能确定下来,而 onDraw() 是在 onLayout() 之后执行的,所以这里通过向 ViewTreeObserver 添加 OnPreDrawListener ,这样在 onDraw() 之前就能获取到宽高。

    //ViewTarget
    public void getSize(@NonNull SizeReadyCallback cb) {
        sizeDeterminer.getSize(cb);
    }
    
    //SizeDeterminer
    void getSize(@NonNull SizeReadyCallback cb) {
        int currentWidth = getTargetWidth();
        int currentHeight = getTargetHeight();
        if (isViewStateAndSizeValid(currentWidth, currentHeight)) {
            cb.onSizeReady(currentWidth, currentHeight);
            return;
        }

        if (!cbs.contains(cb)) {
            cbs.add(cb);
        }
        
        if (layoutListener == null) {
            ViewTreeObserver observer = view.getViewTreeObserver();
            layoutListener = new SizeDeterminerLayoutListener(this);    //关注点
            observer.addOnPreDrawListener(layoutListener);              //执行点
        }
    }
    
    //SizeDeterminerLayoutListener
    public boolean onPreDraw() {
        SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
        if (sizeDeterminer != null) {
            sizeDeterminer.checkCurrentDimens();    //执行点
        }
        return true;
    }
    
    //SizeDeterminer
    void checkCurrentDimens() {
        if (cbs.isEmpty()) {
            return;
        }

        int currentWidth = getTargetWidth();
        int currentHeight = getTargetHeight();
        if (!isViewStateAndSizeValid(currentWidth, currentHeight)) {
            return;
        }

        notifyCbs(currentWidth, currentHeight);     //执行点
        clearCallbacksAndListener();                //移除监听
    }
    
    private void notifyCbs(int width, int height) {
        for (SizeReadyCallback cb : new ArrayList<>(cbs)) {
            cb.onSizeReady(width, height);  //回调给 SingleRequest
        }
    }
    

启动加载

在获取到宽高之后就将加载的工作交给 Engine 来完成,它首先会去 ActiveResources 中获取缓存,对 ActiveResources 不了解的可以看下 Glide源码解析之ActiveResources。

如果 ActiveResources 没有缓存则接着去 MemoryCache 中获取缓存,对 MemoryCache 不了解的可以看下 Glide源码解析之MemoryCache。

如果没有内存缓存,则将获取数据的工作交给 DecodeJob 去执行。

PS:很多人都写着 Glide 有三级缓存,分别是内存、磁盘、网络。我估计很多人都没有亲自看过源码,看着当初第一个人这样写,结果都跟着写,误导了一大片人。Glide 的确有三级缓存,但是前两个都是内存缓存,就是 ActiveResources 和 MemoryCache ,最后一级是磁盘缓存 ,是 DiskCache 。

    //SingleRequest
    public synchronized void onSizeReady(int width, int height) {
        if (status != Status.WAITING_FOR_SIZE) {
            return;
        }
        status = Status.RUNNING;

        float sizeMultiplier = requestOptions.getSizeMultiplier();
        this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
        this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

        loadStatus =
                engine.load(
                        glideContext,
                        model,
                        requestOptions.getSignature(),
                        this.width,
                        this.height,
                        requestOptions.getResourceClass(),
                        transcodeClass,
                        priority,
                        requestOptions.getDiskCacheStrategy(),
                        requestOptions.getTransformations(),
                        requestOptions.isTransformationRequired(),
                        requestOptions.isScaleOnlyOrNoTransform(),
                        requestOptions.getOptions(),
                        requestOptions.isMemoryCacheable(),
                        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
                        requestOptions.getUseAnimationPool(),
                        requestOptions.getOnlyRetrieveFromCache(),
                        this,
                        callbackExecutor);  //执行点

        if (status != Status.RUNNING) {
            loadStatus = null;
        }
    }

    //Engine
    public synchronized  LoadStatus load(
            GlideContext glideContext,
            Object model,
            Key signature,
            int width,
            int height,
            Class resourceClass,
            Class transcodeClass,
            Priority priority,
            DiskCacheStrategy diskCacheStrategy,
            Map, Transformation> transformations,
            boolean isTransformationRequired,
            boolean isScaleOnlyOrNoTransform,
            Options options,
            boolean isMemoryCacheable,
            boolean useUnlimitedSourceExecutorPool,
            boolean useAnimationPool,
            boolean onlyRetrieveFromCache,
            ResourceCallback cb,
            Executor callbackExecutor) {

        EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
                resourceClass, transcodeClass, options);

        //获取 ActiveResources 缓存
        EngineResource active = loadFromActiveResources(key, isMemoryCacheable);     //执行点
        if (active != null) {
            cb.onResourceReady(active, DataSource.MEMORY_CACHE);
            return null;
        }

        //获取 MemoryCache 缓存
        EngineResource cached = loadFromCache(key, isMemoryCacheable);               //执行点
        if (cached != null) {
            cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
            return null;
        }

        // jobs 是缓存 EngineJob 的
        EngineJob current = jobs.get(key, onlyRetrieveFromCache);
        if (current != null) {
            current.addCallback(cb, callbackExecutor);
            return new LoadStatus(cb, current);
        }

        EngineJob engineJob =
                engineJobFactory.build(
                        key,
                        isMemoryCacheable,
                        useUnlimitedSourceExecutorPool,
                        useAnimationPool,
                        onlyRetrieveFromCache);

        //从磁盘或者数据源(比如网络)中获取资源,并进行转换和转码。
        DecodeJob decodeJob =
                decodeJobFactory.build(
                        glideContext,
                        model,
                        key,
                        signature,
                        width,
                        height,
                        resourceClass,
                        transcodeClass,
                        priority,
                        diskCacheStrategy,
                        transformations,
                        isTransformationRequired,
                        isScaleOnlyOrNoTransform,
                        onlyRetrieveFromCache,
                        options,
                        engineJob);

        jobs.put(key, engineJob);

        engineJob.addCallback(cb, callbackExecutor);    //关注点
        engineJob.start(decodeJob);                     //执行点

        return new LoadStatus(cb, engineJob);
    }

从 ActiveResources 获取缓存

这个没啥好说的,有就返回,没有就 null 。

    private EngineResource loadFromActiveResources(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {   // isMemoryCacheable 默认为 true
            return null;
        }
        EngineResource active = activeResources.get(key);
        if (active != null) {
            active.acquire();
        }

        return active;
    }

从 MemoryCache 获取缓存

首先调用 remove() 获取缓存,这里之所以不调用 get() 是因为 ActiveResources 同样是内存缓存,它保存的是没被垃圾回收的资源。而 MemoryCache 是有内存大小限制的,将缓存存入 ActiveResources 后它也没必要再持有多一份。

如果有缓存的话则将资源包装成 EngineResource 返回。

    private EngineResource loadFromCache(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {
            return null;
        }

        EngineResource cached = getEngineResourceFromCache(key); // 执行点
        if (cached != null) {
            cached.acquire();
            activeResources.activate(key, cached);                  // 将获取到的缓存存入 ActiveResources 中
        }
        return cached;
    }
    
    private EngineResource getEngineResourceFromCache(Key key) {
        Resource cached = cache.remove(key);                     // 关注点

        final EngineResource result;
        if (cached == null) {
            result = null;
        } else if (cached instanceof EngineResource) {
            result = (EngineResource) cached;
        } else {
            result = new EngineResource<>(cached, true /*isMemoryCacheable*/, true /*isRecyclable*/);
        }
        return result;
    }

执行加载

首先会获取线程池,由于默认的磁盘缓存策略是允许解码缓存资源的,所以获取到的是 diskCacheExecutor 。这是一个核心线程数和最大线程数为1,工作队列为 PriorityBlockingQueue 的线程池。Glide 默认的 Priority 为 NORMAL ,我们在使用的时候可以通过设置 Priority 来改变加载的优先级。

接着把 DecodeJob 放进线程池执行,因为它实现了 Runnable 接口。DecodeJob 的具体调用过程请查看 Glide源码解析之DecodeJob。

面试经常会被问到线程池,但是实际项目中却很少用到,因为大多数工作第三方库都帮我们做了,所以阅读源码的一个好处是能学到平时很少接触的知识。

    //EngineJob
    public synchronized void start(DecodeJob decodeJob) {
        this.decodeJob = decodeJob;
        GlideExecutor executor = decodeJob.willDecodeFromCache()
                ? diskCacheExecutor
                : getActiveSourceExecutor();
        executor.execute(decodeJob);
    }
    
    //DecodeJob
    boolean willDecodeFromCache() {
        Stage firstStage = getNextStage(Stage.INITIALIZE);
        return firstStage == Stage.RESOURCE_CACHE || firstStage == Stage.DATA_CACHE;
    }
    
    
    private Stage getNextStage(Stage current) {
        switch (current) {
            case INITIALIZE:
                //默认 diskCacheStrategy 的类为 AUTOMATIC ,decodeCachedResource() 返回true
                return diskCacheStrategy.decodeCachedResource()
                        ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);
            case RESOURCE_CACHE:
                return diskCacheStrategy.decodeCachedData()
                        ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);
            case DATA_CACHE:
                return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;
            case SOURCE:
            case FINISHED:
                return Stage.FINISHED;
            default:
                throw new IllegalArgumentException("Unrecognized stage: " + current);
        }
    }

获取数据完成

在 DecodeJob 获取到数据之后会通过 onResourceReady() 回调给 EngineJob ,首先对当前的状态进行检查,接着会将 resource 包装为一个 EngineResource ,这也不难解释为什么在上面 Engine 的 load() 里面从缓存获取的是 EngineResource 了。

然后对监听进行回调,这是由 Engine 实现的,在资源加载成功后对它进行缓存。

最后将 CallResourceReady 放进线程池执行。

    //EngineJob    
    public void onResourceReady(Resource resource, DataSource dataSource) {
        synchronized (this) {
            this.resource = resource;
            this.dataSource = dataSource;
        }
        notifyCallbacksOfResult();  //执行点
    }
    
    void notifyCallbacksOfResult() {
        ResourceCallbacksAndExecutors copy;
        Key localKey;
        EngineResource localResource;
        synchronized (this) {
            stateVerifier.throwIfRecycled();
            if (isCancelled) {
                resource.recycle();
                release();
                return;
            } else if (cbs.isEmpty()) {
                throw new IllegalStateException("Received a resource without any callbacks to notify");
            } else if (hasResource) {
                throw new IllegalStateException("Already have resource");
            }
            engineResource = engineResourceFactory.build(resource, isCacheable);
            
            hasResource = true;
            copy = cbs.copy();
            incrementPendingCallbacks(copy.size() + 1);

            localKey = key;
            localResource = engineResource;
        }

        listener.onEngineJobComplete(this, localKey, localResource);    //执行点

        //在 Engine 的 load() 中通过 engineJob.addCallback(cb, callbackExecutor); 添加进来
        for (final ResourceCallbackAndExecutor entry : copy) {
            entry.executor.execute(new CallResourceReady(entry.cb));    //执行点
        }
        decrementPendingCallbacks();
    }
    
    //Engine
    public synchronized void onEngineJobComplete(
            EngineJob engineJob, Key key, EngineResource resource) {
        if (resource != null) {
            resource.setResourceListener(key, this);

            if (resource.isCacheable()) {
                activeResources.activate(key, resource);    //存进 ActiveResources 缓存
            }
        }

        jobs.removeIfCurrent(key, engineJob);
    }

回调数据

在上面我们知道会将 CallResourceReady 放进线程池执行,那么这个线程池是从哪来的呢。答案就是在一开始使用 into() 的时候进行赋值,一步步传递给 EnginJob的。这个线程池里面的 execute() 则是使用了主线程的 Handler 来进行 post() ,这样就把线程从子线程切换为主线程来执行了。

接着就会把资源通过回调传给 SingleRequest 了,它首先也会对资源进行检查,如果没有问题则最终会通过 onResourceReady() 来进行处理。

    //RequestBuilder
    public > Y into(@NonNull Y target) {
        return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
    }
    
    public static Executor mainThreadExecutor() {
        return MAIN_THREAD_EXECUTOR;
    }
  
    private static final Executor MAIN_THREAD_EXECUTOR =
        new Executor() {
         private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(@NonNull Runnable command) {
          handler.post(command);
        }
    };
      
    
    //CallResourceReady
    public void run() {
        synchronized (EngineJob.this) {
            if (cbs.contains(cb)) {
                engineResource.acquire();
                callCallbackOnResourceReady(cb);    //执行点
                removeCallback(cb);
            }
            decrementPendingCallbacks();
        }
    }
    
    //EngineJob
    synchronized void callCallbackOnResourceReady(ResourceCallback cb) {
        try {
            cb.onResourceReady(engineResource, dataSource);     //执行点
        } catch (Throwable t) {
            throw new CallbackException(t);
        }
    }
    
    //SingleRequest
    public synchronized void onResourceReady(Resource resource, DataSource dataSource) {
        stateVerifier.throwIfRecycled();
        loadStatus = null;
        if (resource == null) {
            GlideException exception = new GlideException("Expected to receive a Resource with an "
                    + "object of " + transcodeClass + " inside, but instead got null.");
            onLoadFailed(exception);
            return;
        }

        Object received = resource.get();   //获取实际的资源,这里是 BitmapDrawable
        if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
            releaseResource(resource);
            GlideException exception = new GlideException("Expected to receive an object of "
                    + transcodeClass + " but instead" + " got "
                    + (received != null ? received.getClass() : "") + "{" + received + "} inside" + " "
                    + "Resource{" + resource + "}."
                    + (received != null ? "" : " " + "To indicate failure return a null Resource "
                    + "object, rather than a Resource object containing null data."));
            onLoadFailed(exception);
            return;
        }

        if (!canSetResource()) {
            releaseResource(resource);
            status = Status.COMPLETE;
            return;
        }

        onResourceReady((Resource) resource, (R) received, dataSource);  //执行点
    }
    

加载图片

如果有设置 Listener 的,则会先调用 Listener 的回调,没有则会将资源交给 Target 去加载。需要注意的是如果你设置的 Listener 在 onResourceReady() 中返回了 true ,则 Glide 认为你已经自己处理了,不会再帮你把图片加载到 ImageView 里的。

ImageView 接受到资源后,最终会调用 ImageView 的 setImageDrawable() 将图片显示处理,到此整个加载流程就结束了。

    private synchronized void onResourceReady(Resource resource, R result, DataSource dataSource) {
        boolean isFirstResource = isFirstReadyResource();
        status = Status.COMPLETE;
        this.resource = resource;

        isCallingCallbacks = true;
        try {
            boolean anyListenerHandledUpdatingTarget = false;
            
            //这个 requestListeners 是使用时自己添加来监听加载成功与否的,比如
            /* 
             *  Glide.with(this)
             *  .load("")
             *  .listener(new RequestListener() {
             *      @Override
             *      public boolean onLoadFailed(@Nullable GlideException e, *Object model, Target target, boolean *isFirstResource) {
             *          return false;
             *      }
             *
             *      @Override
             *      public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
             *          return false;
             *      }
             *  })
             *  .centerCrop()
             *  .into(imageView);*/
            if (requestListeners != null) {
                for (RequestListener listener : requestListeners) {
                    anyListenerHandledUpdatingTarget |=
                            listener.onResourceReady(result, model, target, dataSource, isFirstResource);
                }
            }
            
            // 在 RequestBuilder 的 into() 中可知 targetListener 为 null
            anyListenerHandledUpdatingTarget |=
                    targetListener != null
                            && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

            if (!anyListenerHandledUpdatingTarget) {
                Transition animation =
                        animationFactory.build(dataSource, isFirstResource);   // NO_ANIMATION
                target.onResourceReady(result, animation);  //执行点
            }
        } finally {
            isCallingCallbacks = false;
        }

        notifyLoadSuccess();
    }
    
    //ImageViewTarget
    public void onResourceReady(@NonNull Z resource, @Nullable Transition transition) {
        if (transition == null || !transition.transition(resource, this)) {
            setResourceInternal(resource);  //执行点
        } else {
            maybeUpdateAnimatable(resource);
        }
    }
    
    private void setResourceInternal(@Nullable Z resource) {
        setResource(resource);              //执行点
        maybeUpdateAnimatable(resource);
    }
    
    //DrawableImageViewTarget
    protected void setResource(@Nullable Drawable resource) {
        view.setImageDrawable(resource);
    }
    

总结

Glide 源码解析系列到此就完结了,仍然记得一开始看源码的时候随着调用链的越来越深,看的云里雾里的,曾经一度想放弃。但是后来想明白了,人生会经历的挫折还很多,一遇到困难就想放弃,那还怎么成大器。所幸的是自己坚持了下来,并且阅读源码的能力又更上一层楼。

总有一天,你会感谢那个不曾放弃的自己!

你可能感兴趣的:(Glide源码解析之加载流程)