[Glide系列第1篇]Glide源碼分析之加載圖片流程(1/2)

前言:開發(fā)的過程中一定少不了圖片加載庫,對于Android圖片加載庫,我們在最常聽到的有老牌圖片加載框架UniversalImageLoader,Glide和Picasso,還有Facebook的Fresco。關(guān)于老牌框架UIL在這里先不談了,我們的目的也是選擇一款適合自己的框架,關(guān)于Glide和Picasso,可以說這兩個框架就像龍鳳胎,有太多的相似點,它們的比較看這篇文章,里面詳細的比較了這兩款框架,關(guān)于Fresco,可以說是結(jié)合了許多圖片庫的優(yōu)點,基本滿足了所有的網(wǎng)絡(luò)圖片展示需求,擁有三級緩存,內(nèi)存管理是它的特色,但是api沒有其他的使用起來簡便,也不能加載gif圖,并且體積幾乎有5M, 對比glide只有幾百k,所以一般使用這個框架的都是對圖片處理需求大要求高那種。所以根據(jù)自己的偏好選擇一款就好了(沒有最好的框架,只有最適合自己的框架)。本文是偏愛Glide,并對加載流程進行分析。

本文章是基于Glide V4版本, 和V3會有差異
關(guān)于V4和V3版本的差異,參考官網(wǎng)說明,當(dāng)你讀完源碼,可能就更能理解所做的改變了。Glide從v3遷移到v4
全篇篇幅較長 編輯的時候出現(xiàn)了好多次如下圖片,所以分了兩篇寫加載的過程。感覺能把代碼擼到最后的都是戰(zhàn)士。。

關(guān)于Glide加載圖片最常使用的一句,也是最簡單的語句就是::

Glide.with(this).load(url).into(imageView);

三步走:先with(),再load(),最后into()

看似簡單的一句話,卻想不到在這句話下面做了多少的處理,看到最后,會驚異于Glide庫的強大。
話不多少,我就是從頭一點一點的過到最后,耗時也是蠻久,中間幾度迷路在代碼中,看到眼睛都要花了,????


先放一張總結(jié)的思維腦圖,幫助大家理清

1.Glide.with()

with()方法是Glide類中的一組靜態(tài)方法,它有好幾個方法重載,正是通過這幾個重載,with()方法才可以接收Context、Activity或者Fragment類型的參數(shù)。也就是說我們選擇的范圍非常廣,不管是在Activity還是Fragment中調(diào)用with()方法,都可以直接傳this。那如果調(diào)用的地方既不在Activity中也不在Fragment中呢?也沒關(guān)系,我們可以獲取當(dāng)前應(yīng)用程序的ApplicationContext,傳入到with()方法當(dāng)中。
public class Glide {
         ...省略
 /*
 *  Begin a load with Glide by passing in a context.
  * @param context Any context, will not be retained.
  * @return A RequestManager for the top level application that can be used to start a load.
  * @see #with(android.app.Activity)
  * @see #with(android.app.Fragment)
  * @see #with(android.support.v4.app.Fragment)
  * @see #with(android.support.v4.app.FragmentActivity)
  */
 public static RequestManager with(Context context) {
   return getRetriever(context).get(context);
 }
 public static RequestManager with(Activity activity) {
   return getRetriever(activity).get(activity);
 }
public static RequestManager with(FragmentActivity activity) {
   return getRetriever(activity).get(activity);
 }
 public static RequestManager with(android.app.Fragment fragment) {
   return getRetriever(fragment.getActivity()).get(fragment);
 }
public static RequestManager with(Fragment fragment) {
   return getRetriever(fragment.getActivity()).get(fragment);
 }
 public static RequestManager with(View view) {
   return getRetriever(view.getContext()).get(view);
 }
}

然后在重載的方法中通過getRetriever(傳入context)
public class Glide {
  ...省略
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // Context could be null for other reasons (ie the user passes in null), but in practice it will
    // only occur due to errors with the Fragment lifecycle.
    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();
  }
}
通過靜態(tài)get()方法得到一個Glide對象,單例實現(xiàn),保證一個應(yīng)用程序只有一個實例。在 checkAndInitializeGlide(context);這個方法中做一些實例重要對象的操作。(自行查看。比如緩存對象、注解、管理請求的工廠等)然后通過getRequestManagerRetriever()返回一個RequestManagerRetriever對象
public class Glide {
        ...省略
  /**
   * Get the singleton.
   *
   * @return the singleton
   */
  public static Glide get(Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }

    return glide;
  }
  public RequestManagerRetriever getRequestManagerRetriever() {
    return requestManagerRetriever;
  }
}
最后調(diào)用RequestManagerRetriever的實例get()方法的一系列重載,去獲取RequestManager對象。(Glide.with()的產(chǎn)物)
RequestManagerRetriever類
public class RequestManagerRetriever implements Handler.Callback {
...省略
 public RequestManager get(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());
    } else {
      assertNotDestroyed(activity);
      FragmentManager fm = activity.getSupportFragmentManager();
      return supportFragmentGet(activity, fm, null /*parentHint*/);
    }
  }

  public RequestManager get(Fragment fragment) {
    Preconditions.checkNotNull(fragment.getActivity(),
          "You cannot start a load on a fragment before it is attached or after it is destroyed");
    if (Util.isOnBackgroundThread()) {
      return get(fragment.getActivity().getApplicationContext());
    } else {
      FragmentManager fm = fragment.getChildFragmentManager();
      return supportFragmentGet(fragment.getActivity(), fm, fragment);
    }
  }

  public RequestManager get(Activity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();
      return fragmentGet(activity, fm, null /*parentHint*/);
    }
  }

  public RequestManager get(View view) {
    if (Util.isOnBackgroundThread()) {
      return get(view.getContext().getApplicationContext());
    }

    Preconditions.checkNotNull(view);
    Preconditions.checkNotNull(view.getContext(),
        "Unable to obtain a request manager for a view without a Context");
    Activity activity = findActivity(view.getContext());
    // The view might be somewhere else, like a service.
    if (activity == null) {
      return get(view.getContext().getApplicationContext());
    }

    if (activity instanceof FragmentActivity) {
      Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
      return fragment != null ? get(fragment) : get(activity);
    }

    // Standard Fragments.
    android.app.Fragment fragment = findFragment(view, activity);
    if (fragment == null) {
      return get(activity);
    }
    return get(fragment);
  }
}
從這些get重載方法里我們可以了解到:
  • 對于Application類型參數(shù):

    • 在非主線程當(dāng)中使用的Glide,那么不管你是傳入的Activity還是Fragment,都會被強制當(dāng)成Application來處理。
    • 如果傳入的就是Application類型參數(shù)(Glide.with(getApplicationContext())),那么會通過getApplicationManager()方法返回RequestManager。Glide加載圖片的生命周期是和with里的參數(shù)保持一致,而對于Application類型,只有當(dāng)應(yīng)用程序被殺掉的時候,圖片加載才會停止。
RequestManagerRetriever類
...
private final RequestManagerFactory factory;
private volatile RequestManager applicationManager;
...
  private RequestManager getApplicationManager(Context context) {
    if (applicationManager == null) {
      synchronized (this) {
        if (applicationManager == null) {
          Glide glide = Glide.get(context.getApplicationContext());
          applicationManager =
              factory.build(
                  glide,
                  new ApplicationLifecycle(),
                  new EmptyRequestManagerTreeNode(),
                  context.getApplicationContext());
        }
      }
    }

    return applicationManager;
  }
  /**
   * Used internally to create {@link RequestManager}s.
   */
  public interface RequestManagerFactory {
    RequestManager build(
        Glide glide,
        Lifecycle lifecycle,
        RequestManagerTreeNode requestManagerTreeNode,
        Context context);
  }
  • 對于非Application類型:

最終的流程是一樣的:
首先是獲取 自己的FragmentManager,然后
  • Fragment類型調(diào)用supportFragmentGet()方法,在這個方法里調(diào)用getSupportRequestManagerFragment()
  • Activity類型調(diào)用fragmentGet()方法,在這個方法里調(diào)用getRequestManagerFragment()
可以看到在這兩個方法做了相同的事:添加一個新的Fragment---RequestManagerFragment到當(dāng)前頁面;而這個Fragment的作用就是為了方便Glide控制生命周期,因為Fragment的生命周期和Activity是同步的,如果Activity被銷毀了,F(xiàn)ragment是可以監(jiān)聽到的,這樣Glide就可以捕獲這個事件并停止圖片加載了。
RequestManagerRetriever類
  private RequestManager supportFragmentGet(Context context, FragmentManager fm,
      Fragment parentHint) {
    SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
  private RequestManager fragmentGet(Context context, android.app.FragmentManager fm,
      android.app.Fragment parentHint) {
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      // TODO(b/27524013): Factor out this Glide.get() call.
      Glide glide = Glide.get(context);
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }
  SupportRequestManagerFragment getSupportRequestManagerFragment(
      final FragmentManager fm, Fragment parentHint) {
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingSupportRequestManagerFragments.get(fm);
      if (current == null) {
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }

  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
  RequestManagerFragment getRequestManagerFragment(
      final android.app.FragmentManager fm, android.app.Fragment parentHint) {
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        pendingRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }
RequestManagerFragment類
  public RequestManagerFragment() {
    this(new ActivityFragmentLifecycle());
  }

  // For testing only.
  @SuppressLint("ValidFragment")
  RequestManagerFragment(ActivityFragmentLifecycle lifecycle) {
    this.lifecycle = lifecycle;
  }
至此 我們了解了Glide.with()方法背后的操作。一系列操作最終得到的是RequestManager

2.load()

load()方法明顯就在RequestManager類,那我們接下來看一下這個load方法做了什么
public class RequestManager implements LifecycleListener {
...省略
  /**
   * A helper method equivalent to calling {@link #asDrawable()} and then {@link
   * RequestBuilder#load(Object)} with the given model.
   *
   * @return A new request builder for loading a {@link Drawable} using the given model.
   */
  @CheckResult
  public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
  }
 @CheckResult
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
  @CheckResult
  public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
}
能調(diào)用RequestManager類的load方法,說明還沒有指定類型,此時load方法默認置為asDrawable(),然后通過返回的RequestBuilder類中的load()加載圖片,最終返回RequestBuilder<Drawable>。
而我們知道實際使用的時候會有加載為asBitmap、asFile()等操作,此時我們看這幾個方法
RequestManager類
  private static final RequestOptions DECODE_TYPE_BITMAP = decodeTypeOf(Bitmap.class).lock();
  private static final RequestOptions DECODE_TYPE_GIF = decodeTypeOf(GifDrawable.class).lock();
  @CheckResult
  public RequestBuilder<Bitmap> asBitmap() {
    return as(Bitmap.class).apply(DECODE_TYPE_BITMAP);
  }
  @CheckResult
  public RequestBuilder<GifDrawable> asGif() {
    return as(GifDrawable.class).apply(DECODE_TYPE_GIF);
  }
  @CheckResult
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }
  @CheckResult
  public RequestBuilder<File> asFile() {
    return as(File.class).apply(skipMemoryCacheOf(true));
  }
  @CheckResult
  public <ResourceType> RequestBuilder<ResourceType> as(Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }
然后調(diào)用RequestManager類的apply方法返回RequestBuilder<TranscodeType>
RequestManager類
  @CheckResult
  public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
    Preconditions.checkNotNull(requestOptions);
    this.requestOptions = getMutableOptions().apply(requestOptions);
    return this;
  }
此時load還是由RequestBuilder類調(diào)用,所以就是先確定類型,如果不指定 默認為asDrawable
而對于RequestBuilder類的load方法也有多個重載,這個方法用于指定待加載的圖片資源,所以也就說明Glide支持加載各種各樣的圖片資源,包括網(wǎng)絡(luò)圖片、本地圖片、應(yīng)用資源、二進制流、Uri對象,Url對象,String字符串
 RequestBuilder類
public class RequestBuilder<TranscodeType> implements Cloneable {
  public RequestBuilder<TranscodeType> load(@Nullable Object model) {
    return loadGeneric(model);
  }
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
    return loadGeneric(bitmap)
        .apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
  }
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable Drawable drawable) {
    return loadGeneric(drawable)
        .apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
  }
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }
  public RequestBuilder<TranscodeType> load(@Nullable Uri uri) {
    return loadGeneric(uri);
  }
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable File file) {
    return loadGeneric(file);
  }
  @CheckResult
  public RequestBuilder<TranscodeType> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
    return loadGeneric(resourceId).apply(signatureOf(ApplicationVersionSignature.obtain(context)));
  }
public RequestBuilder<TranscodeType> load(@Nullable URL url) {
    return loadGeneric(url);
  }
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable byte[] model) {
    return loadGeneric(model).apply(signatureOf(new ObjectKey(UUID.randomUUID().toString()))
        .diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true /*skipMemoryCache*/));
  }

  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }
}
所以實際使用時,我們還可以根據(jù)需要這樣使用load()方法:
// 加載本地圖片
File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);

// 加載應(yīng)用資源
int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);

// 加載二進制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);

// 加載Uri對象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
現(xiàn)在我們肯定是繼續(xù)看loadGeneric這個方法了,這個方法最終返回的也就是
RequestBuilder<TranscodeType> 這個類本身,也就是接下來into的操作也是在這個類中

關(guān)于into篇幅太長,所以單獨放到一篇文章中

【兩篇就懂系列】Glide源碼分析之加載圖片流程(2/2)

其他系列文章:

Glide源碼分析流程思維導(dǎo)圖

【一篇就懂系列】Glide源碼分析之緩存處理

Glide圖片加載庫從v3遷移到v4的改變和使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,326評論 25 708
  • 一、簡介 在泰國舉行的谷歌開發(fā)者論壇上,谷歌為我們介紹了一個名叫Glide的圖片加載庫,作者是bumptech。這...
    天天大保建閱讀 7,786評論 2 28
  • 日期:2017.6.3 習(xí)慣:每天閱讀 訓(xùn)練結(jié)果:沒有閱讀 強化信念:一定要騰出時間閱讀 原因分析:因為我效率太低...
    Huifre7閱讀 235評論 0 0
  • 當(dāng)錦承鐵路擴建工程占了我家的三畝六分地。我便開始了與官打交道的日子,第一次村組長讓上山去查樹,去了之后人家是用測繪...
    女作家閱讀 172評論 0 0
  • 給自己十分鐘的休息 圖書館四點關(guān)門,與其那樣擁擠不如好好的回家。 我早早結(jié)束了圖書館學(xué)習(xí),和那位大連女孩互相祝福了...
    貓本貓的貓閱讀 244評論 0 0

友情鏈接更多精彩內(nèi)容