Glide源码解析之加载流程

作者: 断了谁的弦 | 来源:发表于2020-05-20 09:09 被阅读0次

    前言

    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 <R> LoadStatus load(
                GlideContext glideContext,
                Object model,
                Key signature,
                int width,
                int height,
                Class<?> resourceClass,
                Class<R> transcodeClass,
                Priority priority,
                DiskCacheStrategy diskCacheStrategy,
                Map<Class<?>, 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<R> engineJob =
                    engineJobFactory.build(
                            key,
                            isMemoryCacheable,
                            useUnlimitedSourceExecutorPool,
                            useAnimationPool,
                            onlyRetrieveFromCache);
    
            //从磁盘或者数据源(比如网络)中获取资源,并进行转换和转码。
            DecodeJob<R> 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<R> 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<R> 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 extends Target<TranscodeType>> 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<R> 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<R>) resource, (R) received, dataSource);  //执行点
        }
        
    

    加载图片

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

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

        private synchronized void onResourceReady(Resource<R> 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<Drawable>() {
                 *      @Override
                 *      public boolean onLoadFailed(@Nullable GlideException e, *Object model, Target<Drawable> target, boolean *isFirstResource) {
                 *          return false;
                 *      }
                 *
                 *      @Override
                 *      public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
                 *          return false;
                 *      }
                 *  })
                 *  .centerCrop()
                 *  .into(imageView);*/
                if (requestListeners != null) {
                    for (RequestListener<R> 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<? super R> animation =
                            animationFactory.build(dataSource, isFirstResource);   // NO_ANIMATION
                    target.onResourceReady(result, animation);  //执行点
                }
            } finally {
                isCallingCallbacks = false;
            }
    
            notifyLoadSuccess();
        }
        
        //ImageViewTarget
        public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> 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源码解析之加载流程

        本文链接:https://www.haomeiwen.com/subject/pixgohtx.html