Glide源碼分析

Glide的用法

Glide.with(this)          // 返回RequestManager對象
     .load(imageUrl)           // 返回Y extends Target<TranscodeType>對象,設置model屬性
     .into(imageView);    // 最複雜

也可以有其它操作:

// 設置加載尺寸
Glide.with(this).load(imageUrl).override(800, 800).into(imageView);


// 設置加載中以及加載失敗圖片(api裏面對placeholder()、error()函數中有多態實現)
Glide.with(this).load(imageUrl)
	.placeholder(R.mipmap.ic_launcher).error(R.mipmap.ic_launcher).into(imageView);
 // 設置加載動畫
 Glide.with(this).load(imageUrl).animate(R.anim.item_alpha_in).into(imageView);
// 設置要加載的內容,如圖文混排
Glide.with(this).load(imageUrl).centerCrop().into(new SimpleTarget<GlideDrawable>() {
            @Override
            public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
                imageView.setImageDrawable(resource);
            }
        });


這裏會根據Glide加載的with、load、into 三個步驟的源碼進行分析瞭解。

一、with

Glide中的with是一組靜態方法,有以下幾個重載,可以傳入Activity、Fragment或者是Context。每個with()方法重載的代碼,都是先調用RequestManagerRetriever的靜態get()方法得到一個RequestManagerRetriever對象(這個靜態get()方法就是一個單例實現)然後再調用RequestManagerRetriever的實例get()方法,去獲取RequestManager對象。

public static RequestManager with(Context context) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(context);
}


public static RequestManager with(Activity activity) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(activity);
}


public static RequestManager with(FragmentActivity activity) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(activity);
}


@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(fragment);
}


public static RequestManager with(Fragment fragment) {
    RequestManagerRetriever retriever = RequestManagerRetriever.get();
    return retriever.get(fragment);
}

get的相關代碼就不貼了,關於傳參的部分可以分爲兩類:傳入Application類型的參數傳入非Application類型的參數

case1(最簡單):如果在Glide.with()方法中傳入的是一個Application對象,或在子線程調用,那麼這裏就會調用getApplicationManager(context)方法來獲取一個RequestManager對象。此時Glide的生命週期和Application對象的生命週期同步。應用程序一旦關閉,Glide的加載也就戛然而止了。

case2:如果傳入的是Activity、FragmentActivity、Fragment等,最終都會向當前的Activity當中添加一個隱藏的Fragment(即fragmentGet、supportFragmentGet方法,分別對應的app包和v4包下的兩種Fragment)。之所以要添加一個隱藏的Fragment,其實和生命週期也有關係。因爲Glide不知道Activity的生命週期,Glide將自己和這個隱藏的Fragment綁在一起,而Fragment的生命週期和Activity是同步的。所以一旦Activity被銷燬了,Glide也就可以及時的停止加載了。

總結來看,就是爲了得到一個RequestManager對象,然後Glide會根據我們傳入with()方法的參數來確定圖片加載的生命週期。

二、load

with方法返回的是一個RequestManager對象,緊接着就調用load方法。可以得知,load方法是屬於RequestManager的。


  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
    return asDrawable().load(bitmap);
  }

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
    return asDrawable().load(drawable);
  }


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

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Uri uri) {
    return asDrawable().load(uri);
  }

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable File file) {
    return asDrawable().load(file);
  }

  @SuppressWarnings("deprecation")
  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Integer resourceId) {
    return asDrawable().load(resourceId);
  }

  @SuppressWarnings("deprecation")
  @CheckResult
  @Override
  @Deprecated
  public RequestBuilder<Drawable> load(@Nullable URL url) {
    return asDrawable().load(url);
  }

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable byte[] model) {
    return asDrawable().load(model);
  }

  @NonNull
  @CheckResult
  @Override
  public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
  }

load方法有包含Bitmap 、Drawable、String 、Uri、File等多種類型的重載,最後都是調用 asDrawable() 的load,並將 load()方法中的參數以model傳遞。看一下asDrawable() 方法:

  // RequestManager類
  
  @NonNull
  @CheckResult
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }


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

asDrawable()方法最終會return一個new出來的RequestBuilder對象,換句話說,調用的load方法屬於RequestBuilder類:

 // RequestBuilder類
 
  @NonNull
  @CheckResult
  @SuppressWarnings("unchecked")
  @Override
  public RequestBuilder<TranscodeType> load(@Nullable Object model) {
    return loadGeneric(model);
  }

  @NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

鬧了半天,load的過程就是爲了設置model得值!

三、into

上一步return了一個new出來的RequestBuilder對象,因此這個into()方法是屬於RequestBuilder對象的:

// RequestBuilder類

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

    RequestOptions requestOptions = this.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.
      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.
      }
    }

	// 關注點
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }

上面代碼部分的return值得重點關注

3.1 buildImageViewTarget

glideContext.buildImageViewTarget(view, transcodeClass),/*targetListener=*/ null,requestOptions

buildImageViewTarget方法位於GlideContext.class:

// GlideContext類
@NonNull
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }

#ImageViewTargetFactory.class

public class ImageViewTargetFactory {
  @NonNull
  @SuppressWarnings("unchecked")
  public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
      @NonNull Class<Z> clazz) {
    if (Bitmap.class.equals(clazz)) {
      return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      // load的as傳的是Drawable.class,返回對應的對象	
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    }
  }
}

load()方法中的 as傳入的參數是 Drawable.class,也就是說,這裏會return一個DrawableImageViewTarget對象。

DrawableImageViewTarget的父類ImageViewTarget#onLoadStarted()會爲view提前設置一些狀態(如placeholder信息等),然後等待Engine後續的加載完成

// ImageViewTarget#onLoadStarted

 @Override
  public void onLoadStarted(@Nullable Drawable placeholder) {
    super.onLoadStarted(placeholder);
    setResourceInternal(null);
    setDrawable(placeholder);
  }

 @Override
  public void setDrawable(Drawable drawable) {
    view.setImageDrawable(drawable);
  }

3.2 return的into方法

return的into方法如下:

// RequestBuilder類


private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if (!isModelSet) {
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }

    options = options.autoClone();
    Request request = buildRequest(target, targetListener, options);

    // 獲取正在進行的Request請求
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
      request.recycle();
      // If the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    // 將Request和View進行綁定
    target.setRequest(request);
    // track請求
    requestManager.track(target, request);

    return target;
  }

這裏要通過Request previous = target.getRequest();獲取當前正在進行的Request請求。然後根據判斷,進行不同的操作。

3.2.1 將Request和View進行綁定,並保存到tag

這裏有一點需要注意,即target指的是ViewTarget(聯繫多層方法調用及泛型即可得知)。下面我們看一下target.setRequest(request);

// ViewTarget#setRequest
// 對Request和View綁定

@Override
  public void setRequest(@Nullable Request request) {
    setTag(request);
  }
// 將這層“綁定關係”保存在tag中
private void setTag(@Nullable Object tag) {
    if (tagId == null) {
      isTagUsedAtLeastOnce = true;
      view.setTag(tag);
    } else {
      view.setTag(tagId, tag);
    }
  }

ViewTarget的setRequest方法,其實就是將Request和View進行綁定(相當於卡莉斯塔和牛頭的關係),然後保存在View的tag中。做完這一步,就可以光明正大的執行requestManager.track(target, request);

3.2.2 track當前請求

// RequestManager類

void track(@NonNull Target<?> target, @NonNull Request request) {
	// TargetTracker保存了由當前RequestManager處理的所有Target的集合
    targetTracker.track(target);
    // RequestTracker是針對所有的Request集合
    requestTracker.runRequest(request);
  }

TargetTracker和RequestTracker分別是對target和request做了一個管理。
重點關注一下runRequest()方法:

// RequestTracker類

public void runRequest(@NonNull Request request) {
    requests.add(request);
    if (!isPaused) {
      request.begin();
    } else {
      pendingRequests.add(request);
    }
  }

由isPaused判斷界面的狀態,如果可見,就直接調用request#begin方法執行。否則,就添加到pendingRequests中,來保證Request不會被GC掉(request是WeakHashMap,需強引用防GC)。

防不防GC不重要,重要的是這個request.begin()方法。Request是一個接口,需要看它的實現類SingleRequest(表示正確的請求類,其餘還有縮略圖請求協助類ThumbnailRequestCoordinator、錯誤請求協助類ErrorRequestCoordinator)。

也就是說,request.begin()調用的其實是SingleRequest類中的begin()方法:

//  SingleRequest類

@Override
  public void begin() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    // 判斷url是否爲null
    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) {
      // 既然是去加載圖片,就需要獲取加載結果。
      // SingleRequest類的接口ResourceCallback,將加載結果告訴Target,Target再去通知View該如何
      onResourceReady(resource, DataSource.MEMORY_CACHE);
      return;
    }

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

    status = Status.WAITING_FOR_SIZE;
    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());
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
  }

begin()方法會對url進行檢查判斷,如果爲null就加載失敗。如果size有效,就會觸發真正的請求;否則就設置回調,等待有了size之後再去觸發請求。

順便一提, target.onLoadStarted(getPlaceholderDrawable()),這個onLoadStarted是接口類 Target中的方法,實現類是ViewTarget:


// ViewTarget類
// 調用的父類的onLoadStarted方法
  @CallSuper
  @Override
  public void onLoadStarted(@Nullable Drawable placeholder) {
    super.onLoadStarted(placeholder);
    maybeAddAttachStateListener();
  }

真正的請求是在onSizeReady()方法中發起的:

//  SingleRequest類

  @Override
  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    if (IS_VERBOSE_LOGGABLE) {
      logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
    // 判斷狀態是否是在等待size
    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;

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

    if (IS_VERBOSE_LOGGABLE) {
      logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
    }
    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);

    // This is a hack that's only useful for testing right now where loads complete synchronously
    // even though under any executor running on any thread but the main thread, the load would
    // have completed asynchronously.
    if (status != Status.RUNNING) {
      loadStatus = null;
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
  }

onSizeReady()方法會對狀態進行檢查,判斷是否是在等待size。接着會更新狀態爲Status.RUNNING,然後會調用Engine(包含了所有的參數信息,如緩存,圖片顯示等等),然後去開始真正請求,網絡、內存、磁盤緩存等等。

結合3.1部分,Glide圖片加載的執行流程就結束了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章