Glide源碼解析(一)

簡介

由於在Android項目開發中我們經常會用到圖片加載,你會選擇什麼第三庫來加載圖片,今天讓我們來學習一下Glide圖片加載庫的源碼吧,之前文章有講解到Glide的簡單使用

簡單使用

這裏就不說添加依賴那些了,大家可以看官方文檔,或者我之前的文章Glide的簡單使用,但是版本現在最新的版本是4.8.0
接下來我們看看Glide是如何加載圖片的,如下代碼

public class MainActivity extends AppCompatActivity {
    ImageView imageView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = findViewById(R.id.imageView);
        Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
    }
}

我們通過上述代碼中得到,Glide加載的使用方式是非常簡單的。好了根據這句代碼我們進行Glide的源碼分析吧。

Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);

通過上述代碼我們知道Glide中的with(this)調用的方法中攜帶了Activity或者Fragment的上下文對象其作用是:用於綁定該上下文對象的生命週期,如Activity或者Fragment的聲明週期。代碼如下:共有6個不同參數構造方法,返回靜態的RequestManager,用於加載圖片。

//contex對象
public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
  }
/**
   * Begin a load with Glide that will be tied to the given {@link android.app.Activity}'s lifecycle
   * and that uses the given {@link Activity}'s default options.
   *
   * @param activity The activity to use.
   * @return A RequestManager for the given activity that can be used to start a load. 用於啓動加載的給定activity上下文的RequestManager
   */
public static RequestManager with(@NonNull Activity activity) {
    return getRetriever(activity).get(activity);
  }
//FragmentActivity 上下文對象
public static RequestManager with(@NonNull FragmentActivity activity) {
    return getRetriever(activity).get(activity);
  }
// fragment上下文對象
  public static RequestManager with(@NonNull Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
 //V4的fragment對象
  public static RequestManager with(@NonNull android.app.Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
 // view對象
  public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }

RequestManager是通過RequestManagerRetrieverget() 方法獲取,而get() 方法中的參數就是我們上述代碼傳遞過來的上下文對象。
然後getRetriever(Context context)方法返回的對象是RequestManagerRetriever,如下代碼所示。

  private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // 這裏通過方法checkNotNull() 校驗攜帶的上下文是否爲null,如果是null將拋出空指針異常
    Preconditions.checkNotNull(
        context,
        "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
            + "returns null (which usually occurs when getActivity() is called before the Fragment "
            + "is attached or after the Fragment is destroyed).");
    return Glide.get(context).getRequestManagerRetriever();
  }

如果校驗上下文對象並無異常,則通過Glide.get(context).getRequestManagerRetriever();返回 RequestManagerRetriever對象。

//這裏使用的是雙重校驗單例方式初始化Glide實例
public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          //檢查並初始化Glide
          checkAndInitializeGlide(context);
        }
      }
    }
    return glide;
  }

在checkAndInitializeGlide(context)方法中的initializeGlide(context,builder)方法進行初始化,實例化唯一的Glide實例(雙重校驗單例方式獲取)。

private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();//獲取長連接上下文對象
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();//通過註解方式生成GeneratedAppGlideModule
List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
      //加載Glide Module    
      manifestModules = new ManifestParser(applicationContext).parse();
    }

    if (annotationGeneratedModule != null
        && !annotationGeneratedModule.getExcludedModuleClasses().isEmpty()) {
      Set<Class<?>> excludedModuleClasses =
          annotationGeneratedModule.getExcludedModuleClasses();
      Iterator<com.bumptech.glide.module.GlideModule> iterator = manifestModules.iterator();
//清除已經存在的Glide Module
      while (iterator.hasNext()) {
        com.bumptech.glide.module.GlideModule current = iterator.next();
        if (!excludedModuleClasses.contains(current.getClass())) {
          continue;
        }
        ...
        //清除
        iterator.remove();
      }
    }
     ...
    }
    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory() : null;
//設置Glide Builder 用於建造 Glide 
    builder.setRequestManagerFactory(factory);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.applyOptions(applicationContext, builder);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.applyOptions(applicationContext, builder);
    }
    //構建Glide  這步非常重要,接下來另一篇文章將重點分析這個模塊!!!
    Glide glide = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
     //註冊組件 此方法只調用一次
      module.registerComponents(applicationContext, glide, glide.registry);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    //上下文註冊回調
    applicationContext.registerComponentCallbacks(glide);
   //設置glide,初始化Glide完成
    Glide.glide = glide;
  }

有上面我們知道要獲取到RequestManager是通過Glide.get(context).getRequestManagerRetriever();調用get()方法。我們來看看getRequestManagerRetriever()方法。

//返回RequestManagerRetrieve對象
public RequestManagerRetriever getRequestManagerRetriever() {
    return requestManagerRetriever;
  }

現在我得到了RequestManagerRetrieve以後通過RequestManagerRetrieve.get(context)方法就可以獲取到我們的RequestManager了。
這裏需要注意的是get()方法下面有不同之處。FragmentActivity或者是``

@NonNull
  public RequestManager get(@NonNull Context context) {
    if (context == null) {
      throw new IllegalArgumentException("You cannot start a load on a null Context");
    } else if (Util.isOnMainThread() && !(context instanceof Application)) {
      if (context instanceof FragmentActivity) {
        return get((FragmentActivity) context);
      } else if (context instanceof Activity) {
        return get((Activity) context);
      } else if (context instanceof ContextWrapper) {
        return get(((ContextWrapper) context).getBaseContext());
      }
    }
    return getApplicationManager(context);
  }
public RequestManager get(FragmentActivity activity) {
    if (Util.isOnBackgroundThread()) { ///當前是不是後臺線程
      return get(activity.getApplicationContext()); //從新調用上邊get()方法
    } else {
      assertNotDestroyed(activity);  //通過斷言判斷是否是已經銷燬的上下文對象
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }

supportFragmentGet()方法得到RequestManager對象,當前Activity不能是Finished關閉狀態。

// 獲取RequestManager 對象
  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
//無界面的Fragment,即SupportRequestManagerFragment
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
// 獲取RequestManager
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      //獲取Glide 實例
      Glide glide = Glide.get(context);
      //通過RequestManagerFactory構建RequestManager。
      //current.getGlideLifecycle()獲取生命週期,getRequestManagerTreeNode獲取RequestManagerTreeNode
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

到這裏我們已經得到了一個無界面的Fragment,即SupportRequestManagerFragment ,讓請求和你的activity的生命週期同步。
接下來我們進行到調用load()方法中,看看發生什麼情況。我們知道調用load()方法是RequestManager.class,所以我們進入該類發現該類實現了LifecycleListenerModelTypes<T>接口,而ModelTypes<T>是我們所需的load()方法的接口。

//表示回調是Activity生命週期的三個方法
public interface LifecycleListener {
  void onStart();
  void onStop();
  void onDestroy();
}

ModelTypes<T>接口剛好是定義了load()方法,如下

interface ModelTypes<T> {
   //bitmap參數類型
  T load(@Nullable Bitmap bitmap);
  //drawable參數類型
  T load(@Nullable Drawable drawable);
   //string參數類型
  T load(@Nullable String string);
   //uri參數類型
  T load(@Nullable Uri uri);
  //file參數類型
  T load(@Nullable File file);
   //resourceId參數類型
  T load(@RawRes @DrawableRes @Nullable Integer resourceId);
  //url參數類型
  T load(@Nullable URL url);
  //byte[]參數類型
  T load(@Nullable byte[] model);
  //Object 參數類型
  T load(@Nullable Object model);
}

RequestManager調用方法load()代碼如下,返回的是RequestBuilder<Drawable>實例,該實例可以進行一些常用的操作,如佔位符如:placeholder、error、fallback等方式接下來我們會進行分析該佔位符。

 //返回一個請求的構建器RequestBuilder<Drawable>
  @Override  
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

我們進入asDrawable()方法看看返回RequestBuilder<Drawable>

//默認情況下,可以返回android.graphics.drawable.BitmapDrawable或GifDrawable,但如果爲其他Drawable子類註冊了其他解碼器,也可以返回任何子類。
public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
//用於加載給定資源類的新請求構建器:RequestBuilder<ResourceType>
public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }

得到RequestBuilder<ResourceType>以後調用load(),而該類實現的接口是ModelTypes<RequestBuilder<TranscodeType>>,這裏區別於RequestManager實現的接口參數ModelTypes<RequestBuilder<Drawable>>,說明在這裏已經進行了轉碼了,從Drawable發生了轉碼。

@Override
public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
@Override
  public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
    return loadGeneric(bitmap)
        .apply(diskCacheStrategyOf(DiskCacheStrategy.NONE)); //diskCacheStrategyOf(DiskCacheStrategy.NONE)表示磁盤緩存策略
//apply方法在load(@RawRes @DrawableRes @Nullable Integer resourceId) 、load(@Nullable Drawable drawable)、load(@Nullable Bitmap bitmap)調用
  }
//返回當前RequestBuilder<TranscodeType>對象
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

接下來我們進入into()方法。

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread(); // 檢查是否爲後臺線程也就是UI線程,必須在主線程調用Into()方法
    Preconditions.checkNotNull(view); //當前view是否爲空,是空就報空指針異常。

    RequestOptions requestOptions = this.requestOptions;
    if (!requestOptions.isTransformationSet() 
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
    //在此方法中克隆,以便如果我們使用此RequestBuilder加載到View中,然後加載到不同的目標中,我們不會保留基於先前View的縮放類型應用的轉換。
    //獲取view的ScaleType
      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.
      }
    }
   //調用into(),返回ViewTarget<ImageView, TranscodeType> 對象
    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),//構建ImageViewTarget,這個類擴展自BaseTarget.class
        /*targetListener=*/ null,
        requestOptions);
  }

buildImageViewTarget(view, transcodeClass)通過imageViewTargetFactory.buildTarget()工廠方法來返回ViewTarget<ImageView, X>

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

通過ImageViewTargetFactory工廠方法得到ViewTarget實例,這個過程爲後面設置圖片加載view.setImageBitmap(resource);使用。

public class ImageViewTargetFactory {
  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); //創建了BitmapImageViewTarget轉換爲ViewTarget
    } else if (Drawable.class.isAssignableFrom(clazz)) {
      return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
    } else {
      throw new IllegalArgumentException(
          "Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
    } } }

然後進入上述的into()方法中,該方法構建了Request對象實例過程並進行加載圖片。如下代碼

//返回ViewTarget<ImageView, TranscodeType>
private <Y extends Target<TranscodeType>> Y into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) {
    Util.assertMainThread();
    Preconditions.checkNotNull(target);
    if (!isModelSet) { //是否調用load()方法標誌
      throw new IllegalArgumentException("You must call #load() before calling #into()");
    }
    options = options.autoClone(); //設置自動克隆,加鎖過程
//構造一個請求Request,通過構建請求遞歸方式返回Request,調用方法是buildRequestRecursive(), 構建縮略圖buildThumbnailRequestRecursive()
//這個方法最終會調用到`SingleRequest.obtain()`並且初始化SingleRequest,而我們想要的Request對象就是SingleRequest返回的
    Request request = buildRequest(target, targetListener, options); 
    Request previous = target.getRequest();
    if (request.isEquivalentTo(previous)  //判斷這個請求和之前的是否是一樣的
        && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {//如果之前已經保存在內存緩存了則跳過內存緩存
      request.recycle(); //回收request
   //如果請求完成,則再次開始將確保重新傳遞結果,觸發RequestListeners和Targets。 如果請求失敗,則再次開始將重新啓動請求,
   //從而再次完成請求。 如果請求已在運行,我們可以讓它繼續運行而不會中斷。
      if (!Preconditions.checkNotNull(previous).isRunning()) {
      //使用先前的請求而不是新請求來允許優化,例如跳過設置佔位符,跟蹤和取消跟蹤目標,以及獲取在單個請求中完成的視圖維度。
        previous.begin(); //啓動異步加載
      }
      return target;
    }
    requestManager.clear(target); //清除target對象
    target.setRequest(request); //設置此目標(View)保留的當前請求,不應在Glide外部調用
    requestManager.track(target, request); //追蹤請求操作
    return target;
  }

通過上面代碼註解我們知道Request初始化過程,並返回了SingleRequest對象。我們進入 requestManager.track(target, request)追蹤方法,我要知道target是之前傳遞過來的View的對象轉換而成的,在TargetTracker.class中實現了生命週期LifecycleListener接口,通過弱引用集合WeakHashMap保存了target對象(記得這個對象是View轉換而成的),這裏確保了在線程同步狀態中執行。然後在requestTracker.runRequest(request);方法中啓動了給定了請求request.begin();,在SingleRequest<R>.class中實現了該接口,如下。

@Override
  public void begin() {
    assertNotCallingCallbacks(); //是否允許回調,如果是true則拋出異常無法在RequestListener或Target回調中啓動或清除加載...
    stateVerifier.throwIfRecycled();
    startTime = LogTime.getLogTime();
    if (model == null) {
      if (Util.isValidDimensions(overrideWidth, overrideHeight)) { //是否是有效尺寸
        width = overrideWidth;
        height = overrideHeight;
      }
      // 如果用戶設置了回退可繪製,則僅記錄更詳細的日誌級別,因爲回退Drawable表示用戶偶爾需要空模型。
      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 (status == Status.COMPLETE) { //成功加載媒體
      onResourceReady(resource, DataSource.MEMORY_CACHE); // DataSource.MEMORY_CACHE表示從內存緩存中獲取數據,該方法表示發佈資源,併發布完成並設置爲noll :engine.release(resource); Engine.class負責啓動負載並管理活動和緩存資源  這個方法重點
      return;
    }
    status = Status.WAITING_FOR_SIZE; //等待給予目標的回調以確定目標尺寸
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
      onSizeReady(overrideWidth, overrideHeight); //目標圖片大小讀取,這個方法中開始調用了請求方法,追蹤到Engine.load,設置一下緩存策略,並從磁盤,緩存中讀取
    } 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));
    }}

從上述代碼中成功加載的狀態跟蹤方法到這裏,我們從開始的加載Resource的對象是ImageView通過轉換爲最終的target到這裏我們將弄明白了這個加載情況,代碼如下。

private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    boolean isFirstResource = isFirstReadyResource();
    status = Status.COMPLETE;
    this.resource = resource;
   ....
    isCallingCallbacks = true;
    try {
      boolean anyListenerHandledUpdatingTarget = false;
      if (requestListeners != null) {
        for (RequestListener<R> listener : requestListeners) {
          anyListenerHandledUpdatingTarget |=
              listener.onResourceReady(result, model, target, dataSource, isFirstResource);
        }
      }
      anyListenerHandledUpdatingTarget |=
          targetListener != null
              && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);

      if (!anyListenerHandledUpdatingTarget) {
        Transition<? super R> animation =
            animationFactory.build(dataSource, isFirstResource);
        target.onResourceReady(result, animation); //target最後調用加載資源文件讀取
      }
    } finally {
      isCallingCallbacks = false;
    }
    notifyLoadSuccess(); //通知加載成功!
  }

我們回再到into()方法中,由於ImageViewTarget<Z>擴展自抽象類ViewTarget<ImageView, Z>ViewTarget<ImageView, Z>擴展自BaseTarget<Z>該父類實現了Target<Z>接口,所以ImageViewTarget<Z>重寫了該方法,而DrawableImageViewTarget.class擴展自ImageViewTarget<Z>,這樣最後將會調用setResource()方法

@Override
  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);
  }
public class DrawableImageViewTarget extends ImageViewTarget<Bitmap> {
@Override
  protected void setResource(Bitmap resource) {
    view.setImageBitmap(resource);
  }
}

總結

接下來我們將細分模塊進行講解,本篇只是大致的講解Glide加載的過程,知道加載完成的步驟,還有某些重要的模塊這裏沒有講解到。如佔位符、緩存、變化等等,下篇見分曉。

推薦

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