深圳幻海软件技术有限公司 欢迎您!

Android源码进阶之Glide加载流程和源码详解

2023-03-01

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。前言Glide是纯Java写的Android端开源图片加载库,能够帮助我们下载、缓存、展示多种格式图片,也包括GIF格式;昨天我们从源码里分析了,glide的缓存策略机制;那今天我

本文转载自微信公众号「Android开发编程」,作者Android开发编程。转载本文请联系Android开发编程公众号。

前言

Glide是纯Java写的Android端开源图片加载库,能够帮助我们下载、缓存、展示多种格式图片,也包括GIF格式;

昨天我们从源码里分析了,glide的缓存策略机制;

那今天我们就趁热打铁来分析一波加载流程;

一、glide常用的加载方法

1、加载图片到imageView

Glide.with(Context context).load(Strint url).into(ImageView imageView); 
  • 1.

2、各种形式的图片加载到ImageView

// 加载本地图片 
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); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

3、加载带有占位图

Glide.with(this).load(url).placeholder(R.drawable.loading).into(imageView); 
  • 1.

占位图目的为在目的图片还未加载出来的时候,提前展示给用户的一张图片;

4、加载失败 放置占位符

Glide.with(this).load(url).placeholder(R.drawable.loading).error(R.drawable.error) 
     .diskCacheStrategy(DiskCacheStrategy.NONE)//关闭Glide的硬盘缓存机制 
     .into(imageView); 
//DiskCacheStrategy.NONE:表示不缓存任何内容。 
//DiskCacheStrategy.SOURCE:表示只缓存原始图片。 
//DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。 
//DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

5、加载指定格式的图片--指定为静止图片

Glide.with(this) 
     .load(url) 
     .asBitmap()//只加载静态图片,如果是git图片则只加载第一帧。 
     .placeholder(R.drawable.loading) 
     .error(R.drawable.error) 
     .diskCacheStrategy(DiskCacheStrategy.NONE) 
     .into(imageView); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

6、加载动态图片

Glide.with(this) 
     .load(url) 
     .asGif()//加载动态图片,若现有图片为非gif图片,则直接加载错误占位图。 
     .placeholder(R.drawable.loading) 
     .error(R.drawable.error) 
     .diskCacheStrategy(DiskCacheStrategy.NONE) 
     .into(imageView); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

7、加载指定大小的图片

Glide.with(this) 
     .load(url) 
     .placeholder(R.drawable.loading) 
     .error(R.drawable.error) 
     .diskCacheStrategy(DiskCacheStrategy.NONE) 
     .override(100, 100)//指定图片大小 
     .into(imageView); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

8、关闭框架的内存缓存机制

Glide.with(this) 
     .load(url) 
     .skipMemoryCache(true)  //传入参数为false时,则关闭内存缓存。 
     .into(imageView); 
  • 1.
  • 2.
  • 3.
  • 4.

9、关闭硬盘的缓存

Glide.with(this) 
     .load(url) 
     .diskCacheStrategy(DiskCacheStrategy.NONE)     //关闭硬盘缓存操作 
     .into(imageView); 
//其他参数表示: 
//DiskCacheStrategy.NONE:表示不缓存任何内容。 
//DiskCacheStrategy.SOURCE:表示只缓存原始图片。 
//DiskCacheStrategy.RESULT:表示只缓存转换过后的图片(默认选项)。 
//DiskCacheStrategy.ALL :表示既缓存原始图片,也缓存转换过后的图片。 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

10、当引用的 url 存在 token 时解决方法

public class MyGlideUrl extends GlideUrl { 
    private String mUrl; 
    public MyGlideUrl(String url) { 
        super(url); 
        mUrl = url; 
    } 
    @Override 
    public String getCacheKey() { 
        return mUrl.replace(findTokenParam(), ""); 
    } 
    private String findTokenParam() { 
        String tokenParam = ""
        int tokenKeyIndex = mUrl.indexOf("?token=") >= 0 ? mUrl.indexOf("?token=") : mUrl.indexOf("&token="); 
        if (tokenKeyIndex != -1) { 
            int nextAndIndex = mUrl.indexOf("&", tokenKeyIndex + 1); 
            if (nextAndIndex != -1) { 
                tokenParam = mUrl.substring(tokenKeyIndex + 1, nextAndIndex + 1); 
            } else { 
                tokenParam = mUrl.substring(tokenKeyIndex); 
            } 
        } 
        return tokenParam; 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

然后加载图片的方式为:

Glide.with(this) 
     .load(new MyGlideUrl(url)) 
     .into(imageView); 
  • 1.
  • 2.
  • 3.

11、利用Glide将图片加载到不同控件或加载成不同使用方式

(1)、拿到图片实例

//1、通过自己构造 target 可以获取到图片实例 
SimpleTarget<GlideDrawable> simpleTarget = new SimpleTarget<GlideDrawable>() { 
    @Override 
    public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 
        imageView.setImageDrawable(resource); 
    } 
}; 
//2、将图片实例记载到指定的imageview上,也可以做其他的事情 
public void loadImage(View view) { 
    String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg"
    Glide.with(this) 
         .load(url) 
         .into(simpleTarget); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

(2)、将图片加载到任何位置

/* 
*将图片加载为控件背景 
*/ 
public class MyLayout extends LinearLayout { 
    private ViewTarget<MyLayout, GlideDrawable> viewTarget; 
    public MyLayout(Context context, AttributeSet attrs) { 
        super(context, attrs); 
        viewTarget = new ViewTarget<MyLayout, GlideDrawable>(this) { 
            @Override 
            public void onResourceReady(GlideDrawable resource, GlideAnimation glideAnimation) { 
                MyLayout myLayout = getView(); 
                myLayout.setImageAsBackground(resource); 
            } 
        }; 
    } 
    public ViewTarget<MyLayout, GlideDrawable> getTarget() { 
        return viewTarget; 
    } 
    public void setImageAsBackground(GlideDrawable resource) { 
        setBackground(resource); 
    } 

//引用图片到指定控件作为背景 
public class MainActivity extends AppCompatActivity { 
    MyLayout myLayout; 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
        myLayout = (MyLayout) findViewById(R.id.background); 
    } 
    public void loadImage(View view) { 
        String url = "http://cn.bing.com/az/hprichbg/rb/TOAD_ZH-CN7336795473_1920x1080.jpg"
        Glide.with(this) 
             .load(url) 
             .into(myLayout.getTarget()); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.

12、Glide 实现预加载

//a、预加载代码 
Glide.with(this) 
     .load(url) 
     .diskCacheStrategy(DiskCacheStrategy.SOURCE) 
     .preload(); 
//preload() 有两种重载 
 // 1、带有参数的重载,参数作用是设置预加载的图片大小; 
//2、不带参数的表示加载的图片为原始尺寸; 
//b、使用预加载的图片 
Glide.with(this) 
     .load(url) 
     .diskCacheStrategy(DiskCacheStrategy.SOURCE) 
     .into(imageView); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

二、Glide加载流程详解

1、with(context)

// Glide.java 
public static RequestManager with(@NonNull Context context) { 
    return getRetriever(context).get(context); 

public static RequestManager with(@NonNull Activity activity) { 
    return getRetriever(activity).get(activity); 

public static RequestManager with(@NonNull FragmentActivity activity) { 
    return getRetriever(activity).get(activity); 

public static RequestManager with(@NonNull Fragment fragment) { 
    return getRetriever(fragment.getContext()).get(fragment); 

public static RequestManager with(@NonNull View view) { 
    return getRetriever(view.getContext()).get(view); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

该函数创建了Glide实例并初始化了一些基本参数,然后创建了一个RequestManager对象并返回。总共有5个场景,这里就先选取参数为Context类型情形进行分析。

// Glide.java 
public static RequestManager with(@NonNull Context context) { 
    return getRetriever(context).get(context); 

  • 1.
  • 2.
  • 3.
  • 4.

可以看到该函数首先调用了getRetriever(context)获取到了RequestManagerRetriever对象。在创建该对象之前首先通过Glide.java中的get方法获得了Glide实例(Glide是一个单例),同时读取AppGlideModule和AndroidManifest.xml的配置。

// Glide.java 
private static RequestManagerRetriever getRetriever(@Nullable Context context) { 
    // Glide.get(context)获取Glide实例 
    return Glide.get(context).getRequestManagerRetriever(); 

public static Glide get(@NonNull Context context) { 
    if (glide == null) { 
      // 加载AppGlideModule 
      GeneratedAppGlideModule annotationGeneratedModule = 
          getAnnotationGeneratedGlideModules(context.getApplicationContext()); 
      synchronized (Glide.class) { 
        if (glide == null) { 
          // 加载Mainfest配置、注册模块回调 
          // 这一步执行了 Glide.build()方法构造Glide实例。build方法下面会讲到 
          checkAndInitializeGlide(context, annotationGeneratedModule); 
        } 
      } 
    } 
    return glide; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

获取到Glide实例后,紧接着调用getRequestManagerRetriever方法返回了上一步已经初始化好的RequestManagerRetriever对象。

// Glide.java 
  public RequestManagerRetriever getRequestManagerRetriever() { 
    return requestManagerRetriever; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.

接着再看一看RequestManagerRetriever是如何被初始化的,以及初始化过程中都干了哪些事。首先贴源码看看Glide.build方法内部具体实现(该方法在上述checkAndInitializeGlide()函数中被调用):

// GlideBuilder.java 
Glide build(@NonNull Context context) { 
      // 分配线程池、配置缓存策略 
      sourceExecutor = GlideExecutor.newSourceExecutor(); 
      diskCacheExecutor = GlideExecutor.newDiskCacheExecutor(); 
      animationExecutor = GlideExecutor.newAnimationExecutor(); 
      memorySizeCalculator = new MemorySizeCalculator.Builder(context).build(); 
      // 监听网络变化 
      connectivityMonitorFactory = new DefaultConnectivityMonitorFactory(); 
      int size = memorySizeCalculator.getBitmapPoolSize(); 
      if (size > 0) { 
        bitmapPool = new LruBitmapPool(size); 
      } else { 
        bitmapPool = new BitmapPoolAdapter(); 
      } 
      arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); 
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize()); 
      diskCacheFactory = new InternalCacheDiskCacheFactory(context); 
    // engine是负责执行加载任务的 
    if (engine == null) { 
      engine = 
          new Engine( 
              memoryCache, 
              diskCacheFactory, 
              diskCacheExecutor, 
              sourceExecutor, 
              GlideExecutor.newUnlimitedSourceExecutor(), 
              animationExecutor, 
              isActiveResourceRetentionAllowed); 
    } 
    if (defaultRequestListeners == null) { 
      defaultRequestListeners = Collections.emptyList(); 
    } else { 
      defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners); 
    } 
    RequestManagerRetriever requestManagerRetriever = 
        new RequestManagerRetriever(requestManagerFactory); 
    return new Glide( 
        context, 
        engine, 
        memoryCache, 
        bitmapPool, 
        arrayPool, 
        requestManagerRetriever, 
        connectivityMonitorFactory, 
        logLevel, 
        defaultRequestOptionsFactory, 
        defaultTransitionOptions, 
        defaultRequestListeners, 
        isLoggingRequestOriginsEnabled, 
        isImageDecoderEnabledForBitmaps); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 执行Glide.get()方法时就已经分配好了资源加载、缓存线程池、配置好了缓存策略,这里的engine专门负责加载、解码资源,ConnectivityMonitor注册了网络状态监听器,当网络断开时暂停请求网络资源,重连后继续请求资源;
  • RequestManagerRetriever是原来是通过RequestManagerFactory工厂类构造的。进入到RequestManagerFactory.java类中,可以看到get方法获取到了相应的RequestManager对象;
  • 从这里我们可以发现,无论哪种情况,当App进入后台后会导致页面不可见,此时RequestManager绑定到了ApplicationContext,与App的生命周期一致,因此在RequestManager.java类中也实现了生命周期相关的回调函数;
// RequestManagerRetriever.java 
// get有好几个重载方法,这里仅选取context参数进行分析 
public RequestManager get(@NonNull Context context) { 
    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 
          && ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) { 
        return get(((ContextWrapper) context).getBaseContext()); 
      } 
    } 
    return getApplicationManager(context); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

执行完Glide.with(context)后我们拿到了一个对应的RequestManager对象,接下来就执行下一个任务load(url);

2、load(url)

拿到了RequestManager,紧接着调用load方法开始执行下一步操作,同样先看看load方法的实现

// RequestManager.java 
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) { 
    return asDrawable().load(bitmap); 
  } 
  public RequestBuilder<Drawable> load(@Nullable Drawable drawable) { 
    return asDrawable().load(drawable); 
  } 
  public RequestBuilder<Drawable> load(@Nullable String string) { 
    return asDrawable().load(string); 
  } 
  public RequestBuilder<Drawable> load(@Nullable Uri uri) { 
    return asDrawable().load(uri); 
  } 
  public RequestBuilder<Drawable> load(@Nullable File file) { 
    return asDrawable().load(file); 
  } 
  public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) { 
    return asDrawable().load(resourceId); 
  } 
  public RequestBuilder<Drawable> load(@Nullable URL url) { 
    return asDrawable().load(url); 
  } 
  public RequestBuilder<Drawable> load(@Nullable byte[] model) { 
    return asDrawable().load(model); 
  } 
  public RequestBuilder<Drawable> load(@Nullable Object model) { 
    return asDrawable().load(model); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • load()同样有多个重载函数,传入的参数可以是图片对象Bitmap、Drawable、本地资源Uri、在线资源路径Url、文件对象File、assets资源的id,这里我们只看参数为Url的情形;
  • asDrawable().load(url)返回了一个RequestBuilder对象,首先看看asDrawable方法干了什么;
// RequestManager.java 
  public RequestBuilder<Drawable> asDrawable() { 
    return as(Drawable.class); 
  } 
  public <ResourceType> RequestBuilder<ResourceType> as(@NonNull Class<ResourceType> resourceClass) { 
    return new RequestBuilder<>(glide, this, resourceClass, context); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

asDrawable方法创建了RequestBuilder对象,然后调用RequestBuilder.java中的load方法;

// RequestBuilder.java 
  // 传入的String类型的url将会被作为缓存的key 
  public RequestBuilder<TranscodeType> load(@Nullable String string) { 
    return loadGeneric(string); 
  } 
  // 这里返回了自身 
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) { 
    this.model = model; 
    isModelSet = true
    return this; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

load函数主要工作就是根据传入的资源类型,构造了一个相应的RequestBuilder对象。至此一切准备工作准备就绪,接下来就是最为重要的一步了-加载、展示文件,让我们来着看into(view)方法如何完成这些任务;

3、into(view)

拿到的是对应类型RequestBuilder实例,那么就看看该类里into方法的具体实现。同样into方法有into(@NonNull Y target)和into(@NonNull ImageView )两个重载函数(这两个函数最终都会走到同一个函数中),由于调用into方法时我们传入的参数是ImageView类型的;

// RequestBuilder.java 
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) { 
    Util.assertMainThread(); 
    BaseRequestOptions<?> requestOptions = this; 
    // View's scale type. 
    // 处理图片缩放,根据缩放类型来初始化对应的requestOptions对象 
    ...... 
    return into
        glideContext.buildImageViewTarget(view, transcodeClass), 
        /*targetListener=*/ null
        requestOptions, 
        Executors.mainThreadExecutor() // 运行在主线程的handler 
    ); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 上面代码段首先处理图片缩放类型(裁剪、对齐方式等),并将生成的相关参数放入了requestOptions对象中,然后再将其作为参数传给了RequestBuilder.java类私有方法into。该方法定义的四个参数分别为:viewTarget、target回调监听器、请求参数、主线程的回调函数;
  • 显然外部传入ImageView对象最终被转换成了ViewTarget对象,转换函数便是glideContext.buildImageViewTarget(view, transcodeClass);
// GlideContext.java 
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) { 
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.

ViewTarget又是由ImageViewTargetFactory工厂方法生成,接着再看buildTarget方法是如何生成ViewTarget对象。

// imageViewTargetFactory.java 
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); 
    } 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)"); 
    } 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

可以看到无论传入参数是何种类型,最终都会转换成两种类型的ViewTarget :BitmapImageViewTarget;DrawableImageViewTarget;这里如何选择取决于asBitmap()、asGif()和asDrawable()函数是否被调用,默认是Bitmap类型,所以这里默认返回的是BitmapImageViewTarget;

// BitmapImageViewTarget.java 
public class BitmapImageViewTarget extends ImageViewTarget<Bitmap> { 
  public BitmapImageViewTarget(ImageView view) { 
    super(view); 
  } 
  @Override 
  protected void setResource(Bitmap resource) { 
    view.setImageBitmap(resource); // 显示图片 
  } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

至此ViewTarget创建完毕,我们再回到RequestBuilder.java私有into方法

// RequestBuilder.java` 
 private <Y extends Target<TranscodeType>> Y into
      @NonNull Y target, 
      @Nullable RequestListener<TranscodeType> targetListener, 
      BaseRequestOptions<?> options, 
      Executor callbackExecutor) { 
    // 注释1:创建request 
    Request request = buildRequest(target, targetListener, options, callbackExecutor); 
    // 获取前一个reqeust请求对象 
    Request previous = target.getRequest(); 
    // 与上一个请求相同 并且 上一个请求已完成 
    if (request.isEquivalentTo(previous)&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) { 
     // 上一个请求已完成,那么重新启动它 
      if (!Preconditions.checkNotNull(previous).isRunning()) { 
        previous.begin(); 
      } 
      return target; 
    } 
    // 与上一个请求不同,则清除掉上一个,再将加入新请求 
    requestManager.clear(target); 
    target.setRequest(request); 
    requestManager.track(target, request); 
    return target; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

顺着代码次序,来看看这个方法每一步都干了什么:

  • 首先执行buildRequest方法创建一个新的Request请求req1;
  • 获取当前ViewTarget上正在进行中的Request请求req2;
  • 判断新建的请求req1与已有的请求req2是否相同,如果相同则判断是否跳过req2请求的缓存,两个条件都满足则开始执行begin()方法开始请求资源并停止往下执行,条件都不满足则继续执行第四步;
  • 给ViewTarget设置最新的请求req1,然后执行track方法追踪req1。
  • 执行into(view)方法首先获取到了Request请求,然后开始执行Request。如果是复用的Request则直接执行begin(),否则执行track(target, request),但最终仍然会执行begin();
// ReqeustManager.java   
synchronized void track(@NonNull Target<?> target, @NonNull Request request) { 
    // 与lifecycle绑定 
    targetTracker.track(target); 
    // 启动reqeust 
    requestTracker.runRequest(request); 
  } 
// RequestTracker.java 
  public void runRequest(@NonNull Request request) { 
    requests.add(request); 
    if (!isPaused) { 
      request.begin(); // 立即开始加载 
    } else { 
      //防止从以前的请求中加载任何位图,释放该请求所拥有的任何资源,显示当前占位符(如果提供了该占位符),并将该请求标记为已取消。 
      // request.java( Interface ) 
      request.clear(); 
      pendingRequests.add(request); // 加入队列等待执行 
    } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • track方法的源码,先是执行targetTracker.track(target)监听ViewTarget的请求,然后runRequest开始执行。由于最终都是通过begin()方法开始请求,所以我们先来看看begin()方法的具体实现;
  • Request类是interface类型,begin()它的抽象方法,所以我们要想弄清楚begin()的具体实现,那就要先找到Request的实现类,从buildRequest(xx)方法入手,同样先贴出源码:
// RequestBuilder.java 
private Request buildRequest( 
      Target<TranscodeType> target, 
      @Nullable RequestListener<TranscodeType> targetListener, 
      BaseRequestOptions<?> requestOptions, 
      Executor callbackExecutor) { 
    return buildRequestRecursive( 
        /*requestLock=*/ new Object(), 
        target, 
        targetListener, 
        /*parentCoordinator=*/ null
        transitionOptions, 
        requestOptions.getPriority(), 
        requestOptions.getOverrideWidth(), 
        requestOptions.getOverrideHeight(), 
        requestOptions, 
        callbackExecutor); 
  } 
private Request buildRequestRecursive( 
      Object requestLock, 
      Target<TranscodeType> target, 
      @Nullable RequestListener<TranscodeType> targetListener, 
      @Nullable RequestCoordinator parentCoordinator, 
      TransitionOptions<?, ? super TranscodeType> transitionOptions, 
      Priority priority, 
      int overrideWidth, 
      int overrideHeight, 
      BaseRequestOptions<?> requestOptions, 
      Executor callbackExecutor) { 
    // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator. 
    ErrorRequestCoordinator errorRequestCoordinator = null
    // 请求出错了 
    if (errorBuilder != null) { 
      errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator); 
      parentCoordinator = errorRequestCoordinator; 
    } 
    // 无法确认完成请求和缩略图请求哪个先完成,所以当缩略图比完成请求后完成时就不再显示缩略图 
    Request mainRequest = 
        buildThumbnailRequestRecursive( 
            requestLock, 
            target, 
            targetListener, 
            parentCoordinator, 
            transitionOptions, 
            priority, 
            overrideWidth, 
            overrideHeight, 
            requestOptions, 
            callbackExecutor); 
    // 请求成功了,直接返回缩略图Request 
    if (errorRequestCoordinator == null) { 
      return mainRequest; 
    } 
    // ... 
    Request errorRequest = 
        errorBuilder.buildRequestRecursive( 
            requestLock, 
            target, 
            targetListener, 
            errorRequestCoordinator, 
            errorBuilder.transitionOptions, 
            errorBuilder.getPriority(), 
            errorOverrideWidth, 
            errorOverrideHeight, 
            errorBuilder, 
            callbackExecutor); 
    // 同时返回缩略图请求和错误请求 
    errorRequestCoordinator.setRequests(mainRequest, errorRequest); 
    return errorRequestCoordinator; 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.

显然代码里的mainRequest就是我们要找的Request了,它是由buildThumbnailRequestRecursive方法返回的,深入其内部我们发现Request最终其实是由SingleRequest.obtain方法产生,也就是说我们最终拿到的Request其实就是SingleReqeust类的一个实例。这里过程比较简单,代码就不贴出来了。我们直接去SingleReqeust类里面 看看begin方法如何实现的;

// SingleReqeust.java 
public void begin() { 
      if (status == Status.COMPLETE) { 
        // 资源已下载,直接回调 
        // 执行动画 
        onResourceReady(resource, DataSource.MEMORY_CACHE); 
        return
      } 
        // 计算尺寸 
      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()); 
      } 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

进入begin方法后首先判断如果资源已经过加载好了则直接回调onResourceReady显示图片并缓存,否则测量出图片尺寸后再开始加载图片(onSizeReady()中执行加载任务)并同时显示占位图:

①overrideWith、overrideHeight通过override(width, height)设置:

Glide.with(mContext).load(url).override(75, 75).into(imageView);

②占位图是用户调用placeholder(resId)设置:

Glide.with(mContext).load(url).placeholder(resId).into(imageView);

接着再看onSizeReady()测量完图片尺寸后如何加载图片的:

// SingleRequest.java 
@Override 
  public void onSizeReady(int width, int height) { 
      if (status != Status.WAITING_FOR_SIZE) { 
        return
      } 
      status = Status.RUNNING; 
      // 获取图片尺寸 
      float sizeMultiplier = requestOptions.getSizeMultiplier(); 
      this.width = maybeApplySizeMultiplier(width, sizeMultiplier); 
      this.height = maybeApplySizeMultiplier(height, sizeMultiplier); 
      // 开始加载任务 
      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, 
              callbackExecutor); 
  } 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 可以看到真正的下载任务是在Engine类的load方法中实现的,其中也涉及到了图片缓存逻辑;
  • 最终通过Handler机制,Glide从工作线程切换到主线程,并最终将Drawable对象显示到ImageView上;

总结

Glide初始化、显示占位图、图片封面的整个业务流程都走完了;

可以从中学习glide中的设计模式:单例模式、工厂方法、策略模式等等,发现自己的不足之处;

 

Glide的图片缓存可以看上一篇文章