源解 Glide V4.10.0

Glide.with()

/**
 * Glide.class
 */
@NonNull
public static RequestManager with(@NonNull Context context) {
    // step1: 首先调用 getRetriever() 获取到 RequestManagerRetriever 对象
    // step2: 调用 RequestManagerRetriever#get() 获取 RequestManager 对象
    return getRetriever(context).get(context);
}

step1

/**
 * Glide.class
 */
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // step1.1: get(context) 方法最终调用到 initializeGlide(context, new GlideBuilder(), generatedAppGlideModule)
    // step1.2: 调用获取 RequestManagerRetriever 对象
    return get(context).getRequestManagerRetriever();
}

step1.1

/**
 * Glide.class
 */
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder, @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    Context applicationContext = context.getApplicationContext();
    
    // 解析、添加 Mainfest GlideModule 配置
    // 添加 GlideModule 注解配置
    //...

    // step1.1.1: 调用到 GlideBuilder.build() 方法,配置一些基础配置,如缓存大小、线程池等。
    Glide glide = builder.build(applicationContext);

    //...
    
    // step1.1.2: 注册监听内存变化
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
}
step1.1.1
/**
 * GlideBuilder.class 
 */
Glide build(@NonNull Context context) {
    if (this.sourceExecutor == null) {
        // 用于加载内存资源的线程池,线程数 <= 4 ( min(4, Cpu核数) )
        this.sourceExecutor = GlideExecutor.newSourceExecutor();
    }

    if (this.diskCacheExecutor == null) {
        // 用于加载磁盘资源的线程池,线程数 = 1
        this.diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }

    if (this.animationExecutor == null) {
        // 用于处理动画的线程池,线程数 <= 2 (Cpu核数 >= 4 时为 2,否则为 1)
        this.animationExecutor = GlideExecutor.newAnimationExecutor();
    }

    if (this.memorySizeCalculator == null) {
        // 内存计算器,内部维护了一些用于后面内存计算的“常量”
        //   如: 
        //      - arrayPool大小 = RAM <= 1G ? 2M : 4M
        //		- 应用最大内存 = 应用内存 * (RAM <= 1G ? 0.33F : 0.4F)
        //		- targetBitmapPool大小 = 屏幕宽 * 屏幕高 * 4 * (os < 8.0 ? 4 : (RAM <=1G ? 0 : 1))
        //      - targetMemoryCache大小 = 屏幕宽 * 屏幕高 * 4 * 2.0F
        //      - 可用内存大小 = 应用最大内存 - arrayPool大小
        //      - bitmapPoolSize = (targetBitmapPool大小 + targetMemoryCache大小 <= 可用内存大小) ? targetBitmapPool大小 : 可用内存大小 / ((os < 8.0 ? 4 : (RAM <=1G ? 0 : 1)) + 2.0F) * (os < 8.0 ? 4 : (RAM <=1G ? 0 : 1))
        //		- memoryCacheSize = (targetBitmapPool大小 + targetMemoryCache大小 <= 可用内存大小) ? targetMemoryCache大小 : 可用内存大小 / ((os < 8.0 ? 4 : (RAM <=1G ? 0 : 1)) + 2.0F) * 2.0F
        this.memorySizeCalculator = (new MemorySizeCalculator.Builder(context)).build();
    }

    if (this.connectivityMonitorFactory == null) {
        // 用于监听网络状态变化
        this.connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }

    if (this.bitmapPool == null) {
        int size = this.memorySizeCalculator.getBitmapPoolSize();
        if (size > 0) {
            //有内存空间,则创建 LruBitmapPool。
            //  内部根据系统版本使用不同策略生成Key, os >= 4.4 ? bitmapSize + bitmapConfig : bitmapWidth + bitmapHeight + bitmapConfig
            this.bitmapPool = new LruBitmapPool((long)size);
        } else {
            //无复用池
            this.bitmapPool = new BitmapPoolAdapter();
        }
    }

    if (this.arrayPool == null) {
        // 创建一个 LruArrayPool,大小为 RAM <= 1G ? 2M : 4M
        this.arrayPool = new LruArrayPool(this.memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (this.memoryCache == null) {
        // 创建内存资源缓存池
        this.memoryCache = new LruResourceCache((long)this.memorySizeCalculator.getMemoryCacheSize());
    }

    if (this.diskCacheFactory == null) {
        // 创建磁盘缓存,默认大小约256M
        this.diskCacheFactory = new InternalCacheDiskCacheFactory(context);
    }

    if (this.engine == null) {
        // 创建 Engine (核心!)
        this.engine = new Engine(this.memoryCache, this.diskCacheFactory, this.diskCacheExecutor, this.sourceExecutor, GlideExecutor.newUnlimitedSourceExecutor(), this.animationExecutor, this.isActiveResourceRetentionAllowed);
    }

    // 请求监听器
    if (this.defaultRequestListeners == null) {
        this.defaultRequestListeners = Collections.emptyList();
    } else {
        this.defaultRequestListeners = Collections.unmodifiableList(this.defaultRequestListeners);
    }

    // 创建 RequestManagerRetriever,后面用于监听生命周期变化
    RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(this.requestManagerFactory);
    // 构建 Glide 对象 
    //	- 内部会持有传入参数
    //	- 创建一个 Register 对象,该对象持有各种处理组件(如,解析器、解码器、转码器等)
    //	- 创建 GlideContext 对象,对象持有传入参数,并持有新创建的 ImageViewTargetFactory 对象。
    return new Glide(context, this.engine, this.memoryCache, this.bitmapPool, this.arrayPool, requestManagerRetriever, this.connectivityMonitorFactory, 
                     this.logLevel,  // 4
                     this.defaultRequestOptionsFactory, // 返回一个 RequestOptions 对象
                     this.defaultTransitionOptions, // 返回一个 Map<Class<?>, TransitionOptions<?, ?>> 对象
                     this.defaultRequestListeners, // 返回一个 List<RequestListener<Object>> 对象
                     this.isLoggingRequestOriginsEnabled, // false
                     this.isImageDecoderEnabledForBitmaps, // false
                     this.hardwareBitmapFdLimit, // 700
                     this.minHardwareDimension // 128
                    );
}
step1.1.2
/**
 * Glide.class
 */

// 响应系统内存管理
public void onTrimMemory(int level) {
    this.trimMemory(level);
}

public void trimMemory(int level) {
    Util.assertMainThread();
    Iterator var2 = this.managers.iterator();

    //调用 RequestManager#onTrimMemory()
    while(var2.hasNext()) {
        RequestManager manager = (RequestManager)var2.next();
        // level == 60 && pauseAllOnTrim ? pauseAllRequests : ignore
        manager.onTrimMemory(level);
    }

    // level >=40 ? clear : (level >= 20 || level == 15 ? maxSize/2L : ignore)
    this.memoryCache.trimMemory(level);
    // level >= 40 || os >= 6.0 && level >= 20 ? clear : (level >=20 || level == 15 ? maxSize/2L : ignore)
    this.bitmapPool.trimMemory(level);
    // level >=40 ? clear : (level >= 20 || level == 15 ? maxSize/2L : ignore)
    this.arrayPool.trimMemory(level);
}

// 响应系统内存紧张
public void onLowMemory() {
    this.clearMemory();
}

public void clearMemory() {
    Util.assertMainThread();
    this.memoryCache.clearMemory();
    this.bitmapPool.clearMemory();
    this.arrayPool.clearMemory();
}

step2

/**
 * RequestManagerRetriever.class
 */
public RequestManager get(@NonNull Context context) {
    // step2-part1: context !is Applciation : 创建一个 Frament 监听生命周期并持有 RequestManager 对象,方法返回 RequestManager 对象
    // step2-part2: context is Application : 直接调用 fractroy.build() 创建 RequestManager 对象
    // step2-part3: 看一下 RequestManager 构造函数
    if (Util.isOnMainThread() && !(context instanceof Application)) {
        if (context instanceof FragmentActivity) {
            return this.get((FragmentActivity)context);
        }

        if (context instanceof Activity) {
            return this.get((Activity)context);
        }

        if (context instanceof ContextWrapper && ((ContextWrapper)context).getBaseContext().getApplicationContext() != null) {
            return this.get(((ContextWrapper)context).getBaseContext());
        }
    }

    return this.getApplicationManager(context);
}

step2-part1

/**
 * RequestManagerRetriever.class
 */

// context !is Application,调用 this.get((FragmentActivity) context) 最终调用至 supportFragmentGet(acitivty, activity.getSupportFragmentManager(), null, isActivityVisible(activity))
private RequestManager supportFragmentGet(@NonNull Context context, @NonNull androidx.fragment.app.FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    // step2.1.1 获取/创建 Framgnet
    SupportRequestManagerFragment current = this.getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    // 获取 requestManager,若没有则创建,并让 fragment 持有它。
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
        Glide glide = Glide.get(context);
        // step2.1.2 创建 RequestManager
        requestManager = this.factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
        current.setRequestManager(requestManager);
    }

    return requestManager;
}

// step2.1.1
// 创建一个 fragment,将其与 Activity 绑定,实现监听 Activity 生命周期,针对不同生命周期操作 RequestManager(如,Activity 销毁了,则取消所有加载请求)
private SupportRequestManagerFragment getSupportRequestManagerFragment(@NonNull androidx.fragment.app.FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
    SupportRequestManagerFragment current = (SupportRequestManagerFragment)fm.findFragmentByTag("com.bumptech.glide.manager");
    if (current == null) {
        current = (SupportRequestManagerFragment)this.pendingSupportRequestManagerFragments.get(fm);
        if (current == null) {
            current = new SupportRequestManagerFragment();
            current.setParentFragmentHint(parentHint);
            if (isParentVisible) {
                current.getGlideLifecycle().onStart();
            }

            this.pendingSupportRequestManagerFragments.put(fm, current);
            fm.beginTransaction().add(current, "com.bumptech.glide.manager").commitAllowingStateLoss();
            // 发送 handler 消息,handler 接收到消息后调用 pendingSupportRequestManagerFragments.remove()。
            //   此操作目的: commitAllowingStateLoss() 是异步操作,通过 pendingSupportRequestManagerFragments + handler 消息可以防止重复创建。
            this.handler.obtainMessage(2, fm).sendToTarget();
        }
    }

    return current;
}

// step2.1.2
// 默认 fractory
private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
    @NonNull
    public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle, @NonNull RequestManagerTreeNode requestManagerTreeNode, @NonNull Context context) {
        return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
    }
};

step2-part2

/**
 * RequestManagerRetriever.class
 */

// context is Application,直接调用 factory.buid() 创建 RequestManager 对象。
private RequestManager getApplicationManager(@NonNull Context context) {
    if (this.applicationManager == null) {
        synchronized(this) {
            if (this.applicationManager == null) {
                Glide glide = Glide.get(context.getApplicationContext());
                this.applicationManager = this.factory.build(glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode(), context.getApplicationContext());
            }
        }
    }

    return this.applicationManager;
}

step2-part3

/**
 * RequestManager.class
 */
RequestManager(Glide glide, Lifecycle lifecycle, RequestManagerTreeNode treeNode, RequestTracker requestTracker, ConnectivityMonitorFactory factory, Context context) {
    this.targetTracker = new TargetTracker(); //维护生命周期监听者
    this.addSelfToLifecycle = new Runnable() {
        public void run() {
            RequestManager.this.lifecycle.addListener(RequestManager.this);
        }
    };
    this.mainHandler = new Handler(Looper.getMainLooper());
    this.glide = glide;
    this.lifecycle = lifecycle;
    this.treeNode = treeNode;
    this.requestTracker = requestTracker;
    this.context = context;
    this.connectivityMonitor = factory.build(context.getApplicationContext(), new RequestManagerConnectivityListener(requestTracker)); //监听网络状态变化
    //监听生命周期、网络状态变化,用于下发至 TargetTracker、RequestTracker
    if (Util.isOnBackgroundThread()) {
        this.mainHandler.post(this.addSelfToLifecycle);
    } else {
        lifecycle.addListener(this); 
    }

    lifecycle.addListener(this.connectivityMonitor);
    this.defaultRequestListeners = new CopyOnWriteArrayList(glide.getGlideContext().getDefaultRequestListeners());
    this.setRequestOptions(glide.getGlideContext().getDefaultRequestOptions());
    //将自身添加的 glide 对象维护的 requestManager List 中
    glide.registerRequestManager(this);
}

Glide.load()

/**
 * RquestManager.class
 */
public RequestBuilder<Drawable> load(@Nullable String string) {
    // step1: 调用 asDrawable() 创建 RequestBuilder<Drawable>
    // step2: 调用 RequestBuilder#load() 存储请求信息
    return this.asDrawable().load(string);
}

step1

/**
 * RequestManager.class
 */
public RequestBuilder<Drawable> asDrawable() {
    return this.as(Drawable.class);
}

public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) {
    // step1.1: 看一下 RequestBuilder 构造函数
    return new RequestBuilder(this.glide, this, resourceClass, this.context);
}

step1.1

/**
 * RequestBuilder.class
 */
protected RequestBuilder(@NonNull Glide glide, RequestManager requestManager, Class<TranscodeType> transcodeClass, Context context) {
    this.isDefaultTransitionOptionsSet = true;
    this.glide = glide;
    this.requestManager = requestManager;
    this.transcodeClass = transcodeClass;
    this.context = context;
    // 默认 defaultTransition 为空,返回 GenericTransitionOptions 对象
    this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
    this.glideContext = glide.getGlideContext();
    // 默认  defaultRequestListener 为空,如果有值则添加到 RequestBuilder 维护的 RequestListener List 中
    this.initRequestListeners(requestManager.getDefaultRequestListeners());
    this.apply(requestManager.getDefaultRequestOptions());
}

step2

/**
 * RequestBuilder.class
 */
public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return this.loadGeneric(string);
}
//记录下请求信息
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    this.isModelSet = true;
    return this;
}

Glide.into()

/**
 * RequestBuilder.class
 */
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    BaseRequestOptions<?> requestOptions = this;
    // 根据 ImageView scaleType 属性配置 requestOptions
    if (!this.isTransformationSet() && this.isTransformationAllowed() && view.getScaleType() != null) {
        switch (view.getScaleType()) {
            case CENTER_CROP:
                requestOptions = this.clone().optionalCenterCrop();
                break;
            case CENTER_INSIDE:
                requestOptions = this.clone().optionalCenterInside();
                break;
            case FIT_CENTER:
            case FIT_START:
            case FIT_END:
                requestOptions = this.clone().optionalFitCenter();
                break;
            case FIT_XY:
                requestOptions = this.clone().optionalCenterInside();
            case CENTER:
            case MATRIX:
        }
    }
    
    return (ViewTarget)this.into(this.glideContext.buildImageViewTarget(view, this.transcodeClass), (RequestListener)null, (BaseRequestOptions)requestOptions, Executors.mainThreadExecutor());
}

// @params target : DrawableImageViewTarget 对象
// @params targetListener: null
// @params options : requestOptions
// @params callbackExecutor : 主线程
private <Y extends Target<TranscodeType>> Y into(@NonNull Y target, @Nullable RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> options, Executor callbackExecutor) {
    // step1: 创建 Request 对象,最终调用到 buildRequestRecursive()
    Request request = this.buildRequest(target, targetListener, options, callbackExecutor);
    Request previous = target.getRequest();
    // 如果请求是同一个,并且之前的请求已完成&设置不做内存缓存,则复用之前的请求
    if (request.isEquivalentTo(previous) && !this.isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
        if (!((Request)Preconditions.checkNotNull(previous)).isRunning()) {
            previous.begin();
        }

        return target;
    } else {
        this.requestManager.clear(target);	//取消之前的请求,重新创建请求
        target.setRequest(request);	//view.setTag(tagId, request)
        // step2: 建立新的请求
        this.requestManager.track(target, request);
        return target;
    }
}

step1

/**
 * RequestBuilder.class
 */
// 最终调用到 buildRequestRecursive()
// @params requestLock : new Object()
// @params target : DrawableImageViewTarget
// @params targetListener : null
// @params parentCoordinator : null
// @params transitionOptions : GenericTransitionOptions
// @params priority : Priority.NORMAL
// @params overrideWidth : -1
// @params overrideHeight : -1
// @params requestOptions: options
// @params callbackExecutor: 主线程
private Request buildRequestRecursive(Object requestLock, Target<TranscodeType> target, @Nullable RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, BaseRequestOptions<?> requestOptions, Executor callbackExecutor) {
    ErrorRequestCoordinator errorRequestCoordinator = null;
    // 这里我们没有设置 error(),即 errorBuilder = null
    if (this.errorBuilder != null) {
        errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, (RequestCoordinator)parentCoordinator);
        parentCoordinator = errorRequestCoordinator;
    }
    // 真正创建 Request 
    Request mainRequest = this.buildThumbnailRequestRecursive(requestLock, target, targetListener, (RequestCoordinator)parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight, requestOptions, callbackExecutor);
    if (errorRequestCoordinator == null) {
        return mainRequest;
    }
    // ...
}

private Request buildThumbnailRequestRecursive(Object requestLock, Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, @Nullable RequestCoordinator parentCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, BaseRequestOptions<?> requestOptions, Executor callbackExecutor) {
    if (this.thumbnailBuilder != null) {
        // 这里没有设置 thumbnail(RequestBuilder thumbnailRequest), 此处逻辑省略
    } else if (this.thumbSizeMultiplier != null) {
        // 这里没有设置 thumbnail(float sizeMultiplier), 此处逻辑省略
    } else {
        return this.obtainRequest(requestLock, target, targetListener, requestOptions, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight, callbackExecutor);
    }
}

//创建一个 SingleRequest 对象,即 buildRequestRecursive() 返回的值
private Request obtainRequest(Object requestLock, Target<TranscodeType> target, RequestListener<TranscodeType> targetListener, BaseRequestOptions<?> requestOptions, RequestCoordinator requestCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight, Executor callbackExecutor) {
    return SingleRequest.obtain(this.context, this.glideContext, requestLock, this.model, this.transcodeClass, requestOptions, overrideWidth, overrideHeight, priority, target, targetListener, this.requestListeners, requestCoordinator, this.glideContext.getEngine(), transitionOptions.getTransitionFactory(), callbackExecutor);
}

step2

/**
 * RequestManager.class
 */
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
    // 将 DrawableImageViewTarget 添加到 TargetTracker 维护的 targets Set 中
    this.targetTracker.track(target);
    // step3: 启动请求
    this.requestTracker.runRequest(request);
}

step3

/**
 * RequestTracker.class
 */
public void runRequest(@NonNull Request request) {
    //添加到 requests Set 中
    this.requests.add(request);
    //如果没有暂停,则开始请求,否则添加到 pendingRequests List 中
    if (!this.isPaused) {
        // step4: 开始请求
        request.begin();
    } else {
        request.clear();
        this.pendingRequests.add(request);
    }
}

step4

/**
 * SingleRequest.class
 */
public void begin() {
    synchronized(this.requestLock) {
        //...
        this.status = SingleRequest.Status.WAITING_FOR_SIZE;
        // 这里默认没有设置大小,所以走 else 逻辑
        if (Util.isValidDimensions(this.overrideWidth, this.overrideHeight)) {
            this.onSizeReady(this.overrideWidth, this.overrideHeight);
        } else {
            // 获取 ImageView 的宽高,调用 ViewTarget#getSize()
            //  回调到 onSizeReady()
            this.target.getSize(this);
        }

        if ((this.status == SingleRequest.Status.RUNNING || this.status == SingleRequest.Status.WAITING_FOR_SIZE) && this.canNotifyStatusChanged()) {
            // 回调通知 ImageViewTaget onLoadStarted(),显示 placeHolder drawable
            this.target.onLoadStarted(this.getPlaceholderDrawable());
        }
    }
}

// 实现 SizeReadyCallback#onSizeReady()
public void onSizeReady(int width, int height) {
    synchronized(this.requestLock) {
        if (this.status == SingleRequest.Status.WAITING_FOR_SIZE) {
            this.status = SingleRequest.Status.RUNNING;
            // 如果调用了 thumbnail(float sizeMultiplier), 则计算缩略图的宽高。 sizeMultiplier 默认 1.0F
            float sizeMultiplier = this.requestOptions.getSizeMultiplier();
            this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
            this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
            // step5: 执行加载
            this.loadStatus = this.engine.load(this.glideContext, this.model, this.requestOptions.getSignature(), this.width, this.height, this.requestOptions.getResourceClass(), this.transcodeClass, this.priority, this.requestOptions.getDiskCacheStrategy(), this.requestOptions.getTransformations(), this.requestOptions.isTransformationRequired(), this.requestOptions.isScaleOnlyOrNoTransform(), this.requestOptions.getOptions(), this.requestOptions.isMemoryCacheable(), this.requestOptions.getUseUnlimitedSourceGeneratorsPool(), this.requestOptions.getUseAnimationPool(), this.requestOptions.getOnlyRetrieveFromCache(), this, this.callbackExecutor);
            if (this.status != SingleRequest.Status.RUNNING) {
                this.loadStatus = null;
            }
        }
    }
}

/**
 * ViewTarget.class
 */
void getSize(@NonNull SizeReadyCallback cb) {
    int currentWidth = this.getTargetWidth();
    int currentHeight = this.getTargetHeight();
    if (this.isViewStateAndSizeValid(currentWidth, currentHeight)) {
        // 宽高 > 0 || 宽高 == Integer.MIN_VALUE ? 执行 callback 
        cb.onSizeReady(currentWidth, currentHeight);
    } else {
        // 将 callback 添加到 callback List 中,待 ImageView meaure + layout 之后获取宽高
        if (!this.cbs.contains(cb)) {
            this.cbs.add(cb);
        }

        if (this.layoutListener == null) {
            ViewTreeObserver observer = this.view.getViewTreeObserver();
            this.layoutListener = new SizeDeterminerLayoutListener(this);
            observer.addOnPreDrawListener(this.layoutListener);
        }

    }
}

step5

/**
 * Engine.class
 */
// @params glideContext : GlideContext,即在 Glide.with() - step1.1.1 中 new Glide() 内部创建
// @params model : String,即 Glide.into() 传入的 ImageUrl
// @params signature : 签名,默认 EmptySignature 
// @params width : ImageView 宽
// @params height : ImageView 高
// @params resourceClass : Obejct.class
// @params transcodeClass : Drawable.class
// @params priority: Priority.NORMAL
// @params diskCacheStrategy : DiskCacheStrategy.AUTOMATIC
// @parmas transformations : CachedHashCodeArrayMap 对象,继承自ArrayMap
// @params isTransformationRequired : false
// @params isScaleOnlyOrNoTransform : true
// @params options : Options 对象,实现接口 Key
// @params isMemoryCacheable : true
// @params useUnlimitedSourceExecutorPool :  false
// @params useAnimationPool : false
// @params onlyRetrieveFromCache : false
// @params cb : SingleRequest 中实现回调
// @params callbackExecutor : 主线程
public <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) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0L;
    //生成 Key,即 new EngineKey()
    EngineKey key = this.keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
    EngineResource memoryResource;
    synchronized(this) {
        //step5-part1: 先从内存缓存中获取
        memoryResource = this.loadFromMemory(key, isMemoryCacheable, startTime);
        if (memoryResource == null) {
            //step5-part2: 从磁盘缓存中获取,若没有则发起请求
            return this.waitForExistingOrStartNewJob(glideContext, model, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, options, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache, cb, callbackExecutor, key, startTime);
        }
    }
    //如果内存缓存中有则直接 callback
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
}

step5-part1

/**
 * Engine.class
 */
private EngineResource<?> loadFromMemory(EngineKey key, boolean isMemoryCacheable, long startTime) {
    //先从 ActiveResources#get(key) 中获取,内部维护了个 Map<Key,ResourceWeakReference>
    EngineResource<?> active = this.loadFromActiveResources(key);
    if (active != null) {
        return active;
    } else {
        // 从内存缓存中加载
        EngineResource<?> cached = this.loadFromCache(key);
        if (cached != null) {
            return cached;
        } else {
            return null;
        }
    }
}

// 从 active resource 中获取
private EngineResource<?> loadFromActiveResources(Key key) {
    EngineResource<?> active = this.activeResources.get(key);
    // 如果有缓存,则引用数+1
    if (active != null) {
        active.acquire();
    }
    return active;
}

// 从内存缓存中获取
private EngineResource<?> loadFromCache(Key key) {
    EngineResource<?> cached = this.getEngineResourceFromCache(key);
    // 如果有缓存,则引用数+1,并添加到 active resource 中。
    if (cached != null) {
        cached.acquire();
        this.activeResources.activate(key, cached);
    }
    return cached;
}

private EngineResource<?> getEngineResourceFromCache(Key key) {
    // 从 Map 中移除
    Resource<?> cached = this.cache.remove(key);
    EngineResource result;
    if (cached == null) {
        result = null;
    } else if (cached instanceof EngineResource) {
        result = (EngineResource)cached;
    } else {
        result = new EngineResource(cached, true, true, key, this);
    }
    return result;
}

step5-part2

/**
 * Engine.class
 */
private <R> LoadStatus waitForExistingOrStartNewJob(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, long startTime) {
    // Jobs 维护了两个 Map(jobs、onlyCacheJobs), 这里 onlyRetrieveFromCache 为 false,从 jobs Map 中获取。
    // 	第一次加载,所以为 null
    EngineJob<?> current = this.jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
        current.addCallback(cb, callbackExecutor);
        return new LoadStatus(cb, current);
    } else {
        // 创建 EngineJob 对象
        EngineJob<R> engineJob = this.engineJobFactory.build(key, isMemoryCacheable, useUnlimitedSourceExecutorPool, useAnimationPool, onlyRetrieveFromCache);
        // 创建 DecodeJob 对象
        DecodeJob<R> decodeJob = this.decodeJobFactory.build(glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, isScaleOnlyOrNoTransform, onlyRetrieveFromCache, options, engineJob);
        this.jobs.put(key, engineJob);
        engineJob.addCallback(cb, callbackExecutor);
        //step5-part2-step1
        engineJob.start(decodeJob);
        return new LoadStatus(cb, engineJob);
    }
}
step5-part2-step1
/**
 * EngineJob.class
 */
public synchronized void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    //step5-part2-step2: 创建 Excutor, 返回为 diskCacheExecutor,即在 Glide.with()-step1.1.1 中创建的 diskCacheExecutor,一个单线程的线程池
    GlideExecutor executor = decodeJob.willDecodeFromCache() ? this.diskCacheExecutor : this.getActiveSourceExecutor();
    //step5-part2-step3: 执行 DecodeJob#run() 
    executor.execute(decodeJob);
}
step5-part2-step2
/**
 * DecodeJob.class
 */
boolean willDecodeFromCache() {
    Stage firstStage = this.getNextStage(DecodeJob.Stage.INITIALIZE);
    return firstStage == DecodeJob.Stage.RESOURCE_CACHE || firstStage == DecodeJob.Stage.DATA_CACHE;
}

private Stage getNextStage(Stage current) {
    switch (current) {
        case RESOURCE_CACHE:
            return this.diskCacheStrategy.decodeCachedData() ? DecodeJob.Stage.DATA_CACHE : this.getNextStage(DecodeJob.Stage.DATA_CACHE);
        case DATA_CACHE:
            return this.onlyRetrieveFromCache ? DecodeJob.Stage.FINISHED : DecodeJob.Stage.SOURCE;
        case SOURCE:
        case FINISHED:
            return DecodeJob.Stage.FINISHED;
        case INITIALIZE:
            // 此处 diskCacheStrategy 为 DiskCacheStrategy.AUTOMATIC,条件为 true,返回 DecodeJob.Stage.RESOURCE_CACHE
            return this.diskCacheStrategy.decodeCachedResource() ? DecodeJob.Stage.RESOURCE_CACHE : this.getNextStage(DecodeJob.Stage.RESOURCE_CACHE);
        default:
            throw new IllegalArgumentException("Unrecognized stage: " + current);
    }
}
step5-part2-step3
/**
 * DecodeJob.class
 */
public void run() {

    //如果取消,则回调失败,释放资源,如重置 EngineJob 参数
    if (isCancelled) {
        notifyFailed();
        return;
    }
    runWrapped();
}

private void runWrapped() {
    switch (this.runReason) {
        // 初次执行,默认为 INITIALIZE
        case INITIALIZE:
            stage = getNextStage(Stage.INITIALIZE);
            //根据 stage 值,返回 ResourceCacheGenerator 对象
            currentGenerator = getNextGenerator();
            runGenerators();
            break;
        // 当 stage == DecodeJob.Stage.SOURCE,通过回调再次执行 run() 时,会执行到此处
        case SWITCH_TO_SOURCE_SERVICE:
            // step5-part2-step4: 此时再次执行 runGenerators 时,执行的是 SourceGenerator#startNext()
            runGenerators();
            break;
        case DECODE_DATA:
            decodeFromRetrievedData();
            break;
        default:
            throw new IllegalStateException("Unrecognized run reason: " + this.runReason);
    }
}

private void runGenerators() {
    boolean isStarted = false;
    //这里分别调用了 ResourceCacheGenerator 和 DataCacheGenerator 的 startNext(),用于读取磁盘缓存,如果有则返回,没有则 isStarted = false
    while(!isCancelled && currentGenerator != null && !(isStarted = currentGenerator.startNext())) {
        stage = getNextStage(stage);
        currentGenerator = getNextGenerator();
        // stage == DecodeJob.Stage.SOURCE,即 currentGenerator = SourceGenerator, 调用 reschedule() 
        if (stage == DecodeJob.Stage.SOURCE) {
            reschedule();
            return;
        }
    }
}

public void reschedule() {
    runReason = RunReason.SWITCH_TO_SOURCE_SERVICE;
    // 该 callback 会回到 EngineJob.class 中,其又会通过 sourceExecutor 执行 DecodeJob#run()。
    //   sourceExecutor 在 Glide.with()-step1.1.1 中创建,一个线程数 <=4 的线程池
    callback.reschedule(this);
}
step5-part2-step4
/**
 * SourceGenerator.class
 */
public boolean startNext() {

    //当数据请求回来后,再次触发回到 startNetxt(),此时走 cacheData()
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      // 内部逻辑为,将数据进行缓存,并创建 DataCacheGenerator 对象
      //  sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
      cacheData(data);
    }
    // 数据请求回来,缓存完成,则调用 DataCacheGenerator#startNext()
    //   其内部逻辑为,将缓存的 cacheFile 转换成 ByteBuffer,并将处理后的结果通过 callback.onDataFetcherReady() 返回到 SourceGenerator.class.
    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;
    
    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      // 通过 model 转换获取 ModelLoader,此处 model 为图片链接字符串,通过转换获得 HttpGlideUrlLoader。
      //   mode 与 modelLoader 转换关系是在 Glide 构造函数中添加到 Register 中的,即 Glide.with()-step1.1.1 最后 new Glide() 部分。
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
              || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        // 通过 HttpGlideUrlLoader 可知,此处调用的时 HttpUrlFetcher.loadData(),其内部逻辑即通过 HttpURLConnection 网络请求获取结果,超时时间为 2500ms
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
}

// 请求成功回调
public void onDataReady(Object data) {
    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {
      // 默认 DiskCacheStrategy.AUTOMATIC, 走此处逻辑。
      //  记录数据,再次调用 reschedule(),目的是在请求数据时可能在其他线程中,此操作可回到 Glide 自己的线程中。
      //  再次调用 rechedule() 又会回到 DecodeJob.run(),最后又执行到 SourceGenerator.startNext(),此时 dataToCache 已有值。
      dataToCache = data;
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(
          loadData.sourceKey,
          data,
          loadData.fetcher,
          loadData.fetcher.getDataSource(),
          originalKey);
    }
}

// 经过 DataCacheGenerator 将 cache file 转换成 ByteBuffer 后回调
@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
    // step5-part2-step5: 继续回调到 DecodeJob.class 中
    //   此处 dataSource 没有透传,而是采用了 HttpUrlFetcher.dataSource,即 DataSource.REMOTE
    cb.onDataFetcherReady(sourceKey, data, fetcher, loadData.fetcher.getDataSource(), sourceKey);
}
step5-part2-step5
/**
 * DecodeJob.class
 */
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher, DataSource dataSource, Key attemptedKey) {
    this.currentSourceKey = sourceKey;
    this.currentData = data;
    this.currentFetcher = fetcher;
    this.currentDataSource = dataSource;
    this.currentAttemptingKey = attemptedKey;
    // 如果在 glide 线程池,则再次走 reschedule(),切回到 glide 线程池,执行 decodeFromRetrievedData().
    if (Thread.currentThread() != currentThread) {
      runReason = RunReason.DECODE_DATA;
      callback.reschedule(this);
    } else {
      // 处理数据
      decodeFromRetrievedData();
    }
}

private void decodeFromRetrievedData() {
    Resource<R> resource = null;
    try {
      // step5-part2-step6: 解析数据,最终会调用到 decodeFromFetcher()
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }
    if (resource != null) {
      // step5-part2-step7: 数据解析转换完成,此时 resource 为 BitmapDrawableResource
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
}
step5-part2-step6
/**
 * DecodeJob.class
 */
private <Data> Resource<R> decodeFromFetcher(Data data, DataSource dataSource) throws GlideException {
    // 根据 data 类型获取 LoadPath,如果没有,则创建并 put 存储
    LoadPath<Data, ?, R> path = decodeHelper.getLoadPath((Class<Data>) data.getClass());
    return runLoadPath(data, dataSource, path);
}

private <Data, ResourceType> Resource<R> runLoadPath(
  Data data, DataSource dataSource, LoadPath<Data, ResourceType, R> path) throws GlideException {
    Options options = getOptionsWithHardwareConfig(dataSource);
    // data 为 ByteBuffer,所以 rewinder 为 ByteBufferRewinder
    DataRewinder<Data> rewinder = glideContext.getRegistry().getRewinder(data);
    try {
      // 解析数据,调用 LoadPath.load()
      return path.load(rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    } finally {
      rewinder.cleanup();
    }
}

/**
 * LoadPath.class
 */
public Resource<Transcode> load(
  DataRewinder<Data> rewinder,
  @NonNull Options options,
  int width,
  int height,
  DecodePath.DecodeCallback<ResourceType> decodeCallback) throws GlideException {
    //...
    return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
    //...
}

private Resource<Transcode> loadWithExceptionList(
  DataRewinder<Data> rewinder, //ByteBufferRewinder
  @NonNull Options options,
  int width,
  int height,
  DecodePath.DecodeCallback<ResourceType> decodeCallback,
  List<Throwable> exceptions) throws GlideException {
    Resource<Transcode> result = null;
    for (int i = 0, size = decodePaths.size(); i < size; i++) {
      DecodePath<Data, ResourceType, Transcode> path = decodePaths.get(i);
      try {
        //遍历解析,此处真正可用的只有持有 ByteBufferBitmapDecoder 的 DecodePath
        //  方法返回 BitmapResource
        result = path.decode(rewinder, width, height, options, decodeCallback);
      } catch (GlideException e) {
        exceptions.add(e);
      }
      if (result != null) {
        break;
      }
    }

    return result;
}


/**
 * DecodePath.class
 */
public Resource<Transcode> decode(
  DataRewinder<DataType> rewinder,
  int width,
  int height,
  @NonNull Options options,
  DecodeCallback<ResourceType> callback) throws GlideException {
    //最终调用到 Downsampler.decode 将数据进行转换 ByteBuffer -> InputStream -> BitmapResource(bitmap,bitmapPool)
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    //此处 callback 会一直回调到 DecodeJob#onResourceDecoded(DataSource.REMOTE, BitmapResource)
    //  此操作过程中会将数据缓存至磁盘转换后缓存
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    // 调用 BitmapDrawableTranscoder#transcode() 将 BitmapResource 转换成 BitmapDrawableResource
    return transcoder.transcode(transformed, options);
}
step5-part2-step7
/**
 * DecodeJob.class
 */
private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {
    //...
    
    // 回调数据处理完成,回调到 EngineJob#onResourceReady()
    notifyComplete(result, dataSource);
    
    //...
}

/**
 * EngineJob.class
 */
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) {
        // 创建 EngineResource 对象
      engineResource = engineResourceFactory.build(resource, isCacheable, key, resourceListener);

      hasResource = true;
      copy = cbs.copy();
      incrementPendingCallbacks(copy.size() + 1);
    
      localKey = key;
      localResource = engineResource;
    }
    // 回调到 Engine#onEngineJobComplete(), 将数据存储到 activeResources 中
    engineJobListener.onEngineJobComplete(this, localKey, localResource);
    
    for (final ResourceCallbackAndExecutor entry : copy) {
      // step5-part2-step8: 执行 CallResourceReady#run()
      //   内部会调用 engineResource#acquire(),调用计数+1,
      //   然后继续回调 onResourceReady(),通过代码追溯可知,此处 callback 是在 SingleRequest.class 的 engine.load 时传入的,
      //     即 callback 的实现在 SingleRequest (见 Glide.into()-step2.1-part1-load())
      entry.executor.execute(new CallResourceReady(entry.cb));
    }
    decrementPendingCallbacks();
}
step5-part2-step8
/**
 * SingleRequest.class
 */
public void onResourceReady(Resource<?> resource, DataSource dataSource) {
    Resource<?> toRelease = null;
    try {
      synchronized (requestLock) {
        loadStatus = null;
        // received = BitmapDrawable
        Object received = resource.get();
        //...
        onResourceReady((Resource<R>) resource, (R) received, dataSource);
      }
    } finally {
      if (toRelease != null) {
        engine.release(toRelease);
      }
    }
}
private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    // We must call isFirstReadyResource before setting status.
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;
    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      //...
      if (!anyListenerHandledUpdatingTarget) {
        // 调用 NoTransition.NoAnimationFactory#build() 创建 NoTransition 对象
        Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
        // 调用 DrawableImageViewTarget#onResourceReady(),该方法在其父类 ImageViewTarget 中实现。
        target.onResourceReady(result, animation);
      }
    } finally {
      isCallingCallbacks = false;
    }
    notifyLoadSuccess();
}


/**
 * ImageViewTarget.class
 */
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
    if (transition == null || !transition.transition(resource, this)) {
      // 将资源设置到 ImageView 中。
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
}

小结

资源释放

在 Glide.with()-step2 可知,Glide 通过一个 fragment 实现对 Activity 和 Fragment 生命周期的监听。下面我们看一下 Activity/Fragment 销毁后 Glide 如何释放资源的。

/**
 * SupportRequestManagerFragment.class
 */
public void onDestroy() {
    super.onDestroy();
    // step1: 通知外部已销毁。通过 Glide.with()-part2.3 可知,此处回调到 RequestManager#onDestory()
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
}

step1

/**
 * RequestManager.class
 */
public synchronized void onDestroy() {
    //通知 target 已销毁。此处 target 为 ImageViewTarget
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      // 实际调用到 untrackOrDelegate()
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
}

private void untrackOrDelegate(@NonNull Target<?> target) {
    boolean isOwnedByUs = untrack(target);
    Request request = target.getRequest();
    if (!isOwnedByUs && !glide.removeFromManagers(target) && request != null) {
      target.setRequest(null);
      request.clear();
    }
}

synchronized boolean untrack(@NonNull Target<?> target) {
    Request request = target.getRequest();
    if (request == null) {
      return true;
    }
    // step2: 清理 request
    if (requestTracker.clearAndRemove(request)) {
      targetTracker.untrack(target);
      target.setRequest(null);
      return true;
    } else {
      return false;
    }
}

step2

/**
 * RequestTracker.class
 */
public boolean clearAndRemove(@Nullable Request request) {
    if (request == null) {
      return true;
    }
    //从 list 中 remove
    boolean isOwnedByUs = requests.remove(request);
    isOwnedByUs = pendingRequests.remove(request) || isOwnedByUs;
    if (isOwnedByUs) {
      //step3: Request 内部销毁逻辑。此处 request 为 SingleRequest
      request.clear();
    }
    return isOwnedByUs;
}

step3

/**
 * SingleRequest.clas
 */
public void clear() {
    Resource<R> toRelease = null;
    synchronized (requestLock) {
      if (status == Status.CLEARED) {
        return;
      }
      // 清理资源、监听
      cancel();
      if (resource != null) {
        toRelease = resource;
        resource = null;
      }
      if (canNotifyCleared()) {
        // 通知 target(即 ImageViewTarget),资源已清理,可以现实 placeHolder Drawable
        target.onLoadCleared(getPlaceholderDrawable());
      }
      status = Status.CLEARED;
    }
    
    if (toRelease != null) {
      // step4: 调用 Engine#release(),最终调用的是 EngineResource#release()
      engine.release(toRelease);
    }
}

step4

/**
 * EngineResource.class
 */
void release() {
    boolean release = false;
    // 引用数 -1
    synchronized (this) {
      if (acquired <= 0) {
        throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
      }
      if (--acquired == 0) {
        release = true;
      }
    }
    //引用数 = 0,即资源没有持有者,则彻底释放。
    if (release) {
      // step5: 资源没有持有者,回调资源可以释放,回调到 Engine#onResourceReleased()
      listener.onResourceReleased(key, this);
    }
}

step5

/**
 * Engine.class
 */
public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
    // 从 active Resource 中移除
    activeResources.deactivate(cacheKey);
    if (resource.isMemoryCacheable()) {
      // 从内存缓存中移除
      cache.put(cacheKey, resource);
    } else {
      // 通知 资源resource 回收,此处为 BitmapDrawableResource#recycle(),其内部逻辑将 Bitmap 放入 bitmapPool 中
      resourceRecycler.recycle(resource);
    }
}

资料

Glide v4 : 资源重用
Android 开源库 #6 适可而止!看 Glide 如何把生命周期安排得明明白白 - 掘金
【带着问题学】Glide做了哪些优化? - 掘金
Android 图片加载框架 Glide 4.9.0 (二) 从源码的角度分析 Glide 缓存策略 - 掘金
聊一聊关于Glide在面试中的那些事 - 掘金





Hi,我是“青杉”,您可以通过如下方式关注我:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值