[Glide系列第1篇]Glide源码分析之加载图片流程(1/2)

前言:开发的过程中一定少不了图片加载库,对于Android图片加载库,我们在最常听到的有老牌图片加载框架UniversalImageLoader,Glide和Picasso,还有Facebook的Fresco。关于老牌框架UIL在这里先不谈了,我们的目的也是选择一款适合自己的框架,关于Glide和Picasso,可以说这两个框架就像龙凤胎,有太多的相似点,它们的比较看这篇文章,里面详细的比较了这两款框架,关于Fresco,可以说是结合了许多图片库的优点,基本满足了所有的网络图片展示需求,拥有三级缓存,内存管理是它的特色,但是api没有其他的使用起来简便,也不能加载gif图,并且体积几乎有5M, 对比glide只有几百k,所以一般使用这个框架的都是对图片处理需求大要求高那种。所以根据自己的偏好选择一款就好了(没有最好的框架,只有最适合自己的框架)。本文是偏爱Glide,并对加载流程进行分析。

本文章是基于Glide V4版本, 和V3会有差异
关于V4和V3版本的差异,参考官网说明,当你读完源码,可能就更能理解所做的改变了。Glide从v3迁移到v4
全篇篇幅较长 编辑的时候出现了好多次如下图片,所以分了两篇写加载的过程。感觉能把代码撸到最后的都是战士。。

关于Glide加载图片最常使用的一句,也是最简单的语句就是::

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

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

看似简单的一句话,却想不到在这句话下面做了多少的处理,看到最后,会惊异于Glide库的强大。
话不多少,我就是从头一点一点的过到最后,耗时也是蛮久,中间几度迷路在代码中,看到眼睛都要花了,😁😀


先放一张总结的思维脑图,帮助大家理清

1.Glide.with()

with()方法是Glide类中的一组静态方法,它有好几个方法重载,正是通过这几个重载,with()方法才可以接收Context、Activity或者Fragment类型的参数。也就是说我们选择的范围非常广,不管是在Activity还是Fragment中调用with()方法,都可以直接传this。那如果调用的地方既不在Activity中也不在Fragment中呢?也没关系,我们可以获取当前应用程序的ApplicationContext,传入到with()方法当中。
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();
  }
}
通过静态get()方法得到一个Glide对象,单例实现,保证一个应用程序只有一个实例。在 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;
  }
}
最后调用RequestManagerRetriever的实例get()方法的一系列重载,去获取RequestManager对象。(Glide.with()的产物)
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类型参数:

    • 在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理。
    • 如果传入的就是Application类型参数(Glide.with(getApplicationContext())),那么会通过getApplicationManager()方法返回RequestManager。Glide加载图片的生命周期是和with里的参数保持一致,而对于Application类型,只有当应用程序被杀掉的时候,图片加载才会停止。
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类型调用supportFragmentGet()方法,在这个方法里调用getSupportRequestManagerFragment()
  • Activity类型调用fragmentGet()方法,在这个方法里调用getRequestManagerFragment()
可以看到在这两个方法做了相同的事:添加一个新的Fragment---RequestManagerFragment到当前页面;而这个Fragment的作用就是为了方便Glide控制生命周期,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样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);
  }
}
能调用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);
  }
然后调用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类调用,所以就是先确定类型,如果不指定 默认为asDrawable
而对于RequestBuilder类的load方法也有多个重载,这个方法用于指定待加载的图片资源,所以也就说明Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、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;
  }
}
所以实际使用时,我们还可以根据需要这样使用load()方法:
// 加载本地图片
File file = new File(getExternalCacheDir() + "/image.jpg");
Glide.with(this).load(file).into(imageView);

// 加载应用资源
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);
现在我们肯定是继续看loadGeneric这个方法了,这个方法最终返回的也就是
RequestBuilder<TranscodeType> 这个类本身,也就是接下来into的操作也是在这个类中

关于into篇幅太长,所以单独放到一篇文章中

【两篇就懂系列】Glide源码分析之加载图片流程(2/2)

其他系列文章:

Glide源码分析流程思维导图

【一篇就懂系列】Glide源码分析之缓存处理

Glide图片加载库从v3迁移到v4的改变和使用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,077评论 25 707
  • 一、简介 在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫Glide的图片加载库,作者是bumptech。这...
    天天大保建阅读 7,472评论 2 28
  • 日期:2017.6.3 习惯:每天阅读 训练结果:没有阅读 强化信念:一定要腾出时间阅读 原因分析:因为我效率太低...
    Huifre7阅读 184评论 0 0
  • 当锦承铁路扩建工程占了我家的三亩六分地。我便开始了与官打交道的日子,第一次村组长让上山去查树,去了之后人家是用测绘...
    女作家阅读 148评论 0 0
  • 给自己十分钟的休息 图书馆四点关门,与其那样拥挤不如好好的回家。 我早早结束了图书馆学习,和那位大连女孩互相祝福了...
    猫本猫的猫阅读 199评论 0 0