Glide 是比较著名的图片加载库之一,类似的库还有 PicassoCOIL。这篇文章来讲讲 Glide 是如何加载图片到显示的,并且讲一讲它有哪些设计精妙的地方。

该文章使用的是 Glide 4.15.0

一、代码结构

拉取最新的代码后,使用 Android Studio 打开,可以看到 Glide 的项目结构如下:

  1. annotation: Glide 有几个非常有用的注解,如 @GlideModule@GlideOption 等,需要在本 module 里进行解析。
  2. benchmark: 一些基准测试,可以忽略
  3. glide: 主打包脚本
  4. instrumentation: 一些测试,可以忽略
  5. integration: 可以集成使用的三方库,比如 okhttp3、recyclerview 等,用于打包时进行集成。比如, Glide 支持插件化替换网络请求模块,此 module 便提供该支持
  6. library: 主 module,也是后面我们主要分析的模块
  7. mocks: 用于测试时模拟请求
  8. samples: 一些示例,可以看到基本用法和高级用法
  9. testutil: 顾名思义,测试工具类
  10. third_party: 使用的三方库,包含 disklrucachegif_decoder

我们会在下面主要分析 annotationlibrary 模块。

二、Glide 如何实现图片的加载

我们先看一下 Glide 在4.0+版本的基本用法。


// 单一 imageview
ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
Glide.with(this).load("http://image.url").into(imageView);

// list 中的 imageview
@Override 
public View getView(int position, View recycled, ViewGroup container) {
  final ImageView myImageView;
  if (recycled == null) {
    myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false);
  } else {
    myImageView = (ImageView) recycled;
  }

  String url = myUrls.get(position);

  Glide
    .with(myFragment)
    .load(url)
    .centerCrop()
    .placeholder(R.drawable.loading_spinner)
    .into(myImageView);

  return myImageView;
}

通过两种调用方式,我们可以看出,Glide 用了非常经典的 Builder 设计模式,将各种参数收集、处理,最后调用into()方法,将图片交给 ImageView。接下来我们来一步步阅读源码,看看 Glide 为了完成图片加载功能,都经过了哪些步骤。

首先看Glide.with()方法的源码,该方法有5个重载,大体相同,但也有不同的地方,我们在下面会解释:

public class Glide implements ComponentCallbacks2 {
    ...

    @GuardedBy("Glide.class")
    private static volatile Glide glide;

    @GuardedBy("managers")
    private final List<RequestManager> managers = new ArrayList<>();

    /**
     * Begin a load with Glide by passing in a context.
     *
     * <p>Any requests started using a context will only have the application level options applied
     * and will not be started or stopped based on lifecycle events. In general, loads should be
     * started at the level the result will be used in. If the resource will be used in a view in a
     * child fragment, the load should be started with {@link #with(android.app.Fragment)}} using that
     * child fragment. Similarly, if the resource will be used in a view in the parent fragment, the
     * load should be started with {@link #with(android.app.Fragment)} using the parent fragment. In
     * the same vein, if the resource will be used in a view in an activity, the load should be
     * started with {@link #with(android.app.Activity)}}.
     *
     * <p>This method is appropriate for resources that will be used outside of the normal fragment or
     * activity lifecycle (For example in services, or for notification thumbnails).
     *
     * @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(androidx.fragment.app.Fragment)
     * @see #with(androidx.fragment.app.FragmentActivity)
     */
    @NonNull
    public static RequestManager with(@NonNull Context context) {
        return getRetriever(context).get(context);
    }

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

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

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

    @SuppressWarnings("deprecation")
    @Deprecated
    @NonNull
    public static RequestManager with(@NonNull android.app.Fragment fragment) {
        return getRetriever(fragment.getActivity()).get(fragment);
    }

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

可以看到,with(android.app.Fragment)方法已经被弃用了,取而代之的是with(androidx.fragment.app.Fragment),我们接下来不再讨论被弃用的这个方法。

那么,这5个方法,有什么不同呢?

首先,从它们的传入参数可以看出,它们分别用在 Application 级别、Activity 级别、FragmentActivity 级别、Fragment 级别和 View 级别。

Application 级别意味着它可以用在 Service 里,甚至 Notification 的 Thumbnail 里。而其他的几个,只能用在主线程的 Activity 的 View 树中。

需要注意的是,**with(View)方法在 View 没有被attach之前无效**。并且该方法效率较低,不推荐使用。

接着看看这五个重载方法都调用的getRetriever()方法的代码:

@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
    // context 可能为 null(比如用户就传了个 null),
    // 但实际上它只会出现在 Fragment 的生命周期出现错误时
    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();
}

@NonNull
public RequestManagerRetriever getRequestManagerRetriever() {
    return requestManagerRetriever;
}

可见getRetriever()方法实际上是在获取一个 RequestManger 的生成器。我们先把生成器放一边,先看看 Glide 是如何实例化和初始化的:

// 获取 Glide 的单例
@NonNull
public static Glide get(@NonNull Context context) {
    if (glide == null) {
        // 解析注解配置生成的类 com.bumptech.glide.GeneratedAppGlideModuleImpl
        GeneratedAppGlideModule annotationGeneratedModule =
            getAnnotationGeneratedGlideModules(context.getApplicationContext());
        synchronized (Glide.class) {
            if (glide == null) {
                checkAndInitializeGlide(context, annotationGeneratedModule);
            }
        }
    }

    return glide;
}

// 只有拿到了 Glide.class 这个引用的锁才可以调用该方法
@GuardedBy("Glide.class")
private static void checkAndInitializeGlide(
    @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
    if (isInitializing) {
        throw new IllegalStateException(
            "You cannot call Glide.get() in registerComponents(),"
                + " use the provided Glide instance instead");
    }
    isInitializing = true;
    initializeGlide(context, generatedAppGlideModule);
    isInitializing = false;
}

@GuardedBy("Glide.class")
private static void initializeGlide(
    @NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
    initializeGlide(context, new GlideBuilder(), generatedAppGlideModule);
}

@GuardedBy("Glide.class")
@SuppressWarnings("deprecation")
private static void initializeGlide(
    @NonNull Context context,
    @NonNull GlideBuilder builder,
    @Nullable GeneratedAppGlideModule annotationGeneratedModule) {
    // 解析 GlideModule 配置
    Context applicationContext = context.getApplicationContext();
    List<com.bumptech.glide.module.GlideModule> manifestModules = Collections.emptyList();
    if (annotationGeneratedModule == null || annotationGeneratedModule.isManifestParsingEnabled()) {
        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();
        while (iterator.hasNext()) {
            com.bumptech.glide.module.GlideModule current = iterator.next();
            if (!excludedModuleClasses.contains(current.getClass())) {
                continue;
            }
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "AppGlideModule excludes manifest GlideModule: " + current);
            }
            iterator.remove();
        }
    }

    ...

    RequestManagerRetriever.RequestManagerFactory factory =
        annotationGeneratedModule != null
            ? annotationGeneratedModule.getRequestManagerFactory()
            : null;
    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) {
        try {
        module.registerComponents(applicationContext, glide, glide.registry);
        } catch (AbstractMethodError e) {
        throw new IllegalStateException(
            "Attempting to register a Glide v3 module. If you see this, you or one of your"
                + " dependencies may be including Glide v3 even though you're using Glide v4."
                + " You'll need to find and remove (or update) the offending dependency."
                + " The v3 module name is: "
                + module.getClass().getName(),
            e);
        }
    }
    if (annotationGeneratedModule != null) {
        annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    applicationContext.registerComponentCallbacks(glide);
    
    // glide 变量的声明方式是 private static volatile Glide glide; 使用了 volatile 关键字保证了可见性
    Glide.glide = glide;
}

在初始化工作中,主要是解析了 GlideModule,也即配置。在 Glide 4.0 中,有一个与 3.0 不一样的地方就在这里了:Glide 的 Module 配置不再是在 Manifest 中注册,而是通过在配置类上注解(@GlideModule)的方式来声明一个配置类

它的使用方法如下:

@GlideModule
public class GlideModuleExample extends AppGlideModule {
}

当我们编译时,因为如果要使用 Glide 4.0,我们必须在 app/build.gradle 中这样配置:

implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

也即 glide 有自己的注解处理系统,当我们编译完成后,会生成下面几个类:

其中就有我们上面提到的com.bumptech.glide.GeneratedAppGlideModuleImpl类。

配置解析完成后,就通过GlideBuilder.build()方法来创建实例了。创建完成后,将该实例赋给一个静态的 volatile 的变量。来看看build()方法做了些什么:

@NonNull
Glide build(@NonNull Context context) {
    // 新建一个源线程的线程池
    if (sourceExecutor == null) {
        sourceExecutor = GlideExecutor.newSourceExecutor();
    }
    // 新建一个缓存用的线程池
    if (diskCacheExecutor == null) {
        diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
    }
    // 新建一个动画用的线程池
    if (animationExecutor == null) {
        animationExecutor = GlideExecutor.newAnimationExecutor();
    }
    // 基于设备的一些信息设定缓存区域的大小
    if (memorySizeCalculator == null) {
        memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
    }
    // 监听网络状态,如果没有 android.permission.ACCESS_NETWORK_STATE 权限的话,就不监测
    if (connectivityMonitorFactory == null) {
        connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
    }
    // 根据之前获取的缓存区域的大小,决定要用哪种 bitmapPool
    if (bitmapPool == null) {
        int size = memorySizeCalculator.getBitmapPoolSize();
        if (size > 0) {
            // 使用 LRU 算法的 Bitmap 池
            bitmapPool = new LruBitmapPool(size);
        } else {
            // 使用空 Bitmap 池
            bitmapPool = new BitmapPoolAdapter();
        }
    }

    if (arrayPool == null) {
        arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
    }

    if (memoryCache == null) {
        memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
    }

    if (diskCacheFactory == null) {
        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);
}

可见 Glide 在初始化中做了很多的工作。

  1. 首先初始化了3个线程池,分别用来处理线程、LRU缓存、动画
  2. 根据设备的具体信息,初始化缓存区域
  3. 初始化了一个 Engine 的实例用来管理上面实例化的东西
  4. 生成一个 RequestManagerRetriver 的实例,后面会用来获取 RequestManager
  5. 生成 Glide 实例

load()

在调用with方法获取到RequestManager对象的前提下,调用load方法,并传递我们的url参数,来看下它的源码:

继续看这里

into()

继续看这里