Glide源码分析之一

本篇作为Glide源码分析的开篇,以最简单的方式使用Glide,分析,最终图片是如何显示到ImageView上的。

Glide 版本:4.12.0

先说下结论:

加载顺序

  1. Engine调用 loadFromActiveResources 方法 ,从 ActiveResources 加载。ActiveResources 内部是 有一个 Map<Key, ResourceWeakReference> , 以弱引用的方式持有 ResourceWeakReference 。持有的资源在gc的时候,会被回收。

  2. Engine 调用 loadFromCache,从内存缓存 MemoryCache cache 中加载。并把缓存中加载的资源 EngineResource 放入 activeResources,并增加 EngineResource 的引用计数 acquired , acquired 为0 的时候,会被重新从 activeResources 移除。 如果资源可以缓存的话 resource.isMemoryCacheable(),那么就放入 memoryCache ,否则就 recycle 。

  3. 先尝试使用 ResourceCacheGenerator 从包含对原始数据应用了采样或者变换(downsampled/transformed)的磁盘缓存获取。

  4. 使用 DataCacheGenerator 从没有修改过的原始磁盘缓存文件获取。

  5. 使用 SourceGenerator 从网络下载原始数据。然后根据请求参数,对原始数据应用采样或者变换(downsampled/transformed)。根据磁盘缓存策略,原始数据可能首先被写入磁盘,或者原始数据应用采样或者变换(downsampled/transformed)以后,将变换以后的文件加入到磁盘缓存,将然后从缓存文件加载,而不是直接返回。

  6. 有了磁盘缓存以后,再次重试 3,4,5 步骤,这时候就加载到图片了,把加载到的 EngineResource 资源回调出去,最终是在 ImageViewTarget 的 onResourceReady 方法中为 ImageView 设置了res setResource 。然后会在 Engine 的 onEngineJobComplete 方法中,把返回的 EngineResource 加入 activeResources。

  7. 再次加载同一张图片,activeResources 中中已经有缓存了,直接从 activeResources。 获取返回。

最简单的使用方式

/**
 * 最简单的使用方式
 */
private void sourceCodeTest() {
   
   
    Glide.with(this)
		.load(Images.imageUrls[1])
		.into(imageView1);
}

with 方法返回的是 RequestManager 对象。

RequestManager 的 load 方法。

@Override
public RequestBuilder<Drawable> load(@Nullable String string) {
   
   
    return asDrawable().load(string);
}

RequestManager 的 asDrawable 方法。

@CheckResult
public RequestBuilder<Drawable> asDrawable() {
   
   
    return as(Drawable.class);
}
public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
   
   
    return new RequestBuilder<>(glide, this, resourceClass, context);
}

最终是构建了一个 RequestBuilder 对象。

RequestBuilder 的 load方法。

@NonNull
private RequestBuilder <TranscodeType> loadGeneric(@Nullable Object model) {
   
   
    if(isAutoCloneEnabled()) {
   
   
        return clone().loadGeneric(model);
    }
    //model 就是 要加载的图片链接
    this.model = model;
    //将 isModelSet 置为true
    isModelSet = true;
    //最终返回了RequestBuilder本身,没有什么特殊的设置
    return selfOrThrowIfLocked();
}

RequestBuilder 的 into 方法

@NonNull
public ViewTarget <ImageView, TranscodeType> into(@NonNull ImageView view) {
   
   
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    BaseRequestOptions <?> requestOptions = this;
    //注释1处,如果没有设置Transformation && 允许Transformation && view 的 ScaleType 不为null,则获取 requestOptions 对象。
    if(!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() &&
        view.getScaleType() != null) {
   
   
        // Clone in this method so that if we use this RequestBuilder to load into a View and then
        // into a different target, we don't retain the transformation applied based on the previous
        // View's scale type.
		
		//在此方法中进行克隆,这样,如果我们使用此RequestBuilder加载到一个View中,然后加载到另一个目标中,
		//则不会保留基于上一个视图的缩放类型应用的转换。
        switch(view.getScaleType()) {
   
   
            case CENTER_CROP:
                requestOptions = requestOptions.clone().optionalCenterCrop();
                break;
            case CENTER_INSIDE:
                requestOptions = requestOptions.clone().optionalCenterInside();
                break;
            case FIT_CENTER:
            case FIT_START:
            case FIT_END:
                requestOptions = requestOptions.clone().optionalFitCenter();
                break;
            case FIT_XY:
                requestOptions = requestOptions.clone().optionalCenterInside();
                break;
            case CENTER:
            case MATRIX:
            default:
                // Do nothing.
        }
    }

    //注释2处,调用重载的 into 方法
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/
        null,
        requestOptions,
        Executors.mainThreadExecutor());
}

注释1处,如果没有设置Transformation && 允许Transformation && view 的 ScaleType 不为null,则获取 requestOptions 对象。

注释2处,调用重载的 into 方法。

RequestBuilder 的 into 方法。

private <Y extends Target<TranscodeType>> Y into(@NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> options, Executor callbackExecutor) {
   
   
    //注释1处,在我们的例子中,target 是 DrawableImageViewTarget 对象。
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
   
   
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
  
    //注释2处,构建request,我们先忽略其中的细节
    Request request = buildRequest(target, targetListener, options, callbackExecutor);

    Request previous = target.getRequest();
    //注释3处,如果有和之前的请求相同的请求,并且不是跳过内存缓存的请求,则使用之前的请求。
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
   
   
  
      if (!Preconditions.checkNotNull(previous).isRunning()) {
   
   
        previous.begin();
      }
      return target;
    }
    //注释4处,取消Glide可能为此target的所有未完成的加载,并释放可能已为目标加载的任何资源(例如{@link Bitmap}),以便可以重用它们。
    //注意:这里内部会清除Glide为Target设置的Tag。
    requestManager.clear(target);
    //注释5处,给target重新设置tag
    target.setRequest(request);
    //注释6处,跟踪请求
    requestManager.track(target, request);

    return target;
  }

注释1处,在我们的例子中,target 是 DrawableImageViewTarget 对象。

注释2处,构建request,我们先忽略其中的细节。返回的是一个 SingleRequest 对象。

注释3处,如果有和之前的请求相同的请求,并且不是跳过内存缓存的请求,则使用之前的请求。我们先忽略这种情况。

注释4处,取消Glide可能为此 target 的所有未完成的加载,并释放可能已为目标加载的任何资源(例如{@link Bitmap}),以便可以重用它们。 注意:这里内部会清除Glide为Target设置的Tag。

RequestManager 的 clear 方法。这块细节先略过。分析Glide在RecyclerView中应用的时候, 再详细分析。

注释5处,清除tag以后,给target重新设置tag。这个是保证不乱序的原因。

ViewTarget 的setRequest方法。

@Override
public void setRequest(@Nullable Request request) {
   
   
  setTag(request);
}
//tagId
private static int tagId = R.id.glide_custom_view_target_tag;

private void setTag(@Nullable Object tag) {
   
   
  //清除的时候,会将Tag设置为null。
  isTagUsedAtLeastOnce = true;
  view.setTag(tagId, tag);
}

RequestBuilder 的 into 方法注释6处, RequestManager 调用 track 方法。

synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
   
   
    targetTracker.track(target);
    //调用 RequestTracker 的 runRequest 方法 
    requestTracker.runRequest(request);
}

RequestTracker 的 runRequest 方法。

public void runRequest(@NonNull Request request) {
   
   
    requests.add(request);
    if(!isPaused) {
   
   
        //注释1处,调用 SingleRequest 的 begin 方法。
        request.begin();
    } else {
   
   
        request.clear();
        if(Log.isLoggable(TAG, Log.VERBOSE)) {
   
   
            Log.v(TAG, "Paused, delaying request");
        }
        pendingRequests.add(request);
    }
}

注释1处,调用 SingleRequest 的 begin 方法。

@Override
public void begin() {
   
   
    synchronized(requestLock) {
   
   
        assertNotCallingCallbacks();
        stateVerifier.throwIfRecycled();
        startTime = LogTime.getLogTime();
        if(model == null) {
   
   
            if(Util.isValidDimensions(overrideWidth, overrideHeight)) {
   
   
                width = overrideWidth;
                height = overrideHeight;
            }
            // Only log at more verbose log levels if the user has set a fallback drawable, because
            // fallback Drawables indicate the user expects null models occasionally.
            int logLevel = getFallbackDrawable() == null ? Log.WARN :
                Log.DEBUG;
            onLoadFailed(new GlideException("Received null model"),
                logLevel);
            return;
        }

        if(status == Status.RUNNING) {
   
   
            throw new IllegalArgumentException(
                "Cannot restart a running request");
        }

        // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
        // that starts an identical request into the same Target or View), we can simply use the
        // resource and size we retrieved the last time around and skip obtaining a new size, starting
        // a new load etc. This does mean that users who want to restart a load because they expect
        // that the view size has changed will need to explicitly clear the View or Target before
        // starting the new load.
        if(status == Status.COMPLETE) {
   
   
            onResourceReady(
                resource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */
                false);
            return;
        }

        // Restarts for requests that are neither complete nor running can be treated as new requests
        // and can run again from the beginning.

		//注释1处,开始状态是 WAITING_FOR_SIZE,等待控件测量过后有尺寸
        status = Status.WAITING_FOR_SIZE;
        if(Util.isValidDimensions(overrideWidth, overrideHeight)) {
   
   
            //注释2处,如果指定了宽高,直接调用onSizeReady方法。
            onSizeReady(overrideWidth, overrideHeight);
        } else {
   
   
            //注释3处,否则,调用Target的getSize方法。SingleRequest 本身做为回调传入
            target.getSize(this);
        }

        if((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) &&
            canNotifyStatusChanged()) {
   
   
            //先给ImageView 设置placeholderDrawable
            target.onLoadStarted(getPlaceholderDrawable());
        }
        if(IS_VERBOSE_LOGGABLE) {
   
   
            logV("finished run method in " + LogTime.getElapsedMillis(
                startTime));
        }
    }
}

注释1处,开始状态是 WAITING_FOR_SIZE,等待控件测量过后有宽高信息。
注释2处,如果指定了宽高,直接调用onSizeReady方法。

注释3处,否则,调用Target的getSize方法。SingleRequest 本身做为回调传入。内部是调用 SizeDeterminer 的 getSize 方法。

void getSize(@NonNull SizeReadyCallback cb) {
   
   
    int currentWidth = getTargetWidth();
    int currentHeight = getTargetHeight();
    if(isViewStateAndSizeValid(currentWidth, currentHeight)) {
   
   
        //如果有宽高信息了,直接回调 onSizeReady 方法
        cb.onSizeReady(currentWidth, currentHeight);
        return;
    }
	//没有宽高信息,叫回调cb添加到列表中,等待获取到宽高信息后,通知回调cb。
    if(!cbs.contains(cb)) {
   
   
        cbs.add(cb);
    }
    if(layoutListener == null) {
   
   
        ViewTreeObserver observer = view.getViewTreeObserver();
        layoutListener = new SizeDeterminerLayoutListener(this);
        //注释1处,添加监听,监听尺寸的变化,在View绘制之前会执行这个layoutListener,此时已经有宽高信息了。
        observer.addOnPreDrawListener(layoutListener);
    }
}

注释1处,添加监听,监听尺寸的变化,在View绘制之前会执行这个layoutListener,此时已经有宽高信息了。

ViewTarget.SizeDeterminer.SizeDeterminerLayoutListener 类

private static final class SizeDeterminerLayoutListener
implements ViewTreeObserver.OnPreDrawListener {
   
   
    private final WeakReference <SizeDeterminer> sizeDeterminerRef;

    SizeDeterminerLayoutListener(@NonNull SizeDeterminer sizeDeterminer) {
   
   
        sizeDeterminerRef = new WeakReference <> (sizeDeterminer);
    }

    @Override
    public boolean onPreDraw() {
   
   
        SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
        if(sizeDeterminer != null) {
   
   
            //注释1处,调用 SizeDeterminer 的 checkCurrentDimens 方法。
            sizeDeterminer.checkCurrentDimens();
        }
        return true;
    }
}

注释1处,获取大调用 SizeDeterminer 的 checkCurrentDimens 方法。

@Synthetic
void checkCurrentDimens() {
   
   
    if(cbs.isEmpty()) {
   
   
        return;
    }

    //注释1处,注意,getTartgetWidth和getTargetHeight方法
    int currentWidth = getTargetWidth();
    int currentHeight = getTargetHeight();
    if(!isViewStateAndSizeValid(currentWidth, currentHeight)) {
   
   
        //注释2处,宽高不合法,说明此时View还没有经过measure,直接返回,SizeDeterminerLayoutListener不会被清除。
		//等待经过measure后,再次调用checkCurrentDimens方法。
        return;
    }

    //注释3处,如果宽高合法通知回调,并清除监听SizeDeterminerLayoutListener。
    notifyCbs(currentWidth, currentHeight);
    clearCallbacksAndListener();
}

注释1处,注意,SizeDeterminer 的 getTargetWidth 和 getTargetHeight 方法。

private int getTargetWidth() {
   
   </
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值