Glide实现原理记录

Glide初始化,采用懒注册的方式,设置参数

Glide是一个单例,应用第一次使用Glide是会调用initializeGlide方法,编译期根据GlideModule或清单文件中的模块命名(已弃用)生成GeneratedAppGlideModuleImpl类,提供初始化构造参数的方法填充GlideBuilder,最终build出Glide
GlideModule注解类,提供初始化默认参数修改,AppGlideModule提供两个方法

  • isManifestParsingEnabled 是否读取清单文件中的参数
  • applyOptions 修改GlideBuilder的默认初始化参数,设置缓存参数之类
    使用方法是新建类继承这个类并且加上GlideModule注解,按照需要重写上面两个方法完成初始化参数设置
/**
Defines a set of dependencies and options to use when initializing Glide within an application.
...
*/
public abstract class AppGlideModule extends LibraryGlideModule implements AppliesOptions {
  /**
   * Returns {@code true} if Glide should check the AndroidManifest for {@link GlideModule}s.
   *
   * 

Implementations should return {@code false} after they and their dependencies have migrated * to Glide's annotation processor. * *

Returns {@code true} by default. */ public boolean isManifestParsingEnabled() { return true; } @Override public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) { // Default empty impl. } }

/**
 * Registers a set of components to use when initializing Glide within an app when
*/
public abstract class LibraryGlideModule implements RegistersComponents {
  @Override
  public void registerComponents(@NonNull Context context, @NonNull Glide glide,
      @NonNull Registry registry) {
    // Default empty impl.
  }
}

registerComponents可增加解析组件
例子:增加webp格式的图片加载

@GlideModule
public class WebpGlideLibraryModule extends LibraryGlideModule {

    @Override
    public void registerComponents(Context context, Glide glide, Registry registry) {

        // We should put our decoder before the build-in decoders,
        // because the Downsampler will consume arbitrary data and make the inputstream corrupt
        // on some devices
        final Resources resources = context.getResources();
        final BitmapPool bitmapPool = glide.getBitmapPool();
        final ArrayPool arrayPool = glide.getArrayPool();
        /* static webp decoders */
        WebpDownsampler webpDownsampler = new WebpDownsampler(registry.getImageHeaderParsers(),
                resources.getDisplayMetrics(), bitmapPool, arrayPool);
        AnimatedWebpBitmapDecoder bitmapDecoder = new AnimatedWebpBitmapDecoder(arrayPool, bitmapPool);
        ByteBufferBitmapWebpDecoder byteBufferBitmapDecoder = new ByteBufferBitmapWebpDecoder(webpDownsampler);
        StreamBitmapWebpDecoder streamBitmapDecoder = new StreamBitmapWebpDecoder(webpDownsampler, arrayPool);
        /* animate webp decoders */
        ByteBufferWebpDecoder byteBufferWebpDecoder =
                new ByteBufferWebpDecoder(context, arrayPool, bitmapPool);
        registry
                /* Bitmaps for static webp images */
                .prepend(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
                .prepend(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
                /* BitmapDrawables for static webp images */
                .prepend(
                        Registry.BUCKET_BITMAP_DRAWABLE,
                        ByteBuffer.class,
                        BitmapDrawable.class,
                        new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
                .prepend(
                        Registry.BUCKET_BITMAP_DRAWABLE,
                        InputStream.class,
                        BitmapDrawable.class,
                        new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
                /* Bitmaps for animated webp images*/
                .prepend(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class,
                        new ByteBufferAnimatedBitmapDecoder(bitmapDecoder))
                .prepend(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class,
                        new StreamAnimatedBitmapDecoder(bitmapDecoder))
                /* Animated webp images */
                .prepend(ByteBuffer.class, WebpDrawable.class, byteBufferWebpDecoder)
                .prepend(InputStream.class, WebpDrawable.class, new StreamWebpDecoder(byteBufferWebpDecoder, arrayPool))
                .prepend(WebpDrawable.class, new WebpDrawableEncoder());
    }
}

3级缓存结构

缓存优先级 ActiveResources,MemoryCache,DiskCache
从最终的load方法可以看出,加载会先取ActiveResources,再取MemoryCache如果有的话会从MemoryCache中删除并且添加到ActiveResources中,最后创建EngineJob异步取本地缓存或网络获取,获取成功后会添加到ActiveResources中,ActiveResources利用ReferenceQueue在弱应用被回收时从ActiveResources中移除并添加到MemoryCache中。

  public  LoadStatus load(...) {
    ...
    EngineResource active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }

    EngineResource cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }

    EngineJob current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

    EngineJob engineJob =
        engineJobFactory.build(...);

    DecodeJob decodeJob =
        decodeJobFactory.build(...);

    jobs.put(key, engineJob);

    engineJob.addCallback(cb);
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }
  • ActiveResources可以理解为是当前正在被引用的资源弱引用(WeakReference)
  • MemoryCache是内存缓存,可设置大小限制,不会包含ActiveResources中的资源
  • DiskCache 本地文件缓存,可设置缓存路径及缓存文件总大小
  • 这里有个细节,为什么要有两个内存缓存,原因是MemoryCache是有大小限制的,为防止正在使用的缓存被释放,所以增加了ActiveResources
    DiskCache本地缓存,首次获取本地缓存时会触发本地文件缓存扫描,缓存目录会有一个journal文件
    里面记录了本地缓存文件列表及信息,如下

libcore.io.DiskLruCache
1
1
1

CLEAN b31dfe4cd710e8868de840b27c2feba6443539b378343717c28174c5ff4c3a51 8318
CLEAN 3802507bffb423d3e9455801d15a5d82937e505f82134e0675e3096ecadbea5b 10414
CLEAN 28e5a4b66341ef88252f41602144354db0aa878c1863262e9950559df4488a1f 15396

  private void readJournal() throws IOException {
    StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);
    try {
      String magic = reader.readLine();
      String version = reader.readLine();
      String appVersionString = reader.readLine();
      String valueCountString = reader.readLine();
      String blank = reader.readLine();
      if (!MAGIC.equals(magic)
          || !VERSION_1.equals(version)
          || !Integer.toString(appVersion).equals(appVersionString)
          || !Integer.toString(valueCount).equals(valueCountString)
          || !"".equals(blank)) {
        throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "
            + valueCountString + ", " + blank + "]");
      }
      ...
  }

journal文件前5行目前来看只是起到了判断文件是否是glide缓存目录文件的一个规则,后面才是缓存文件信息,以空格分开,分别是文件状态/文件名/文件大小,目前来看只有CLEAN状态的文件才有文件大小信息
文件状态有

  private static final String CLEAN = "CLEAN"; //干净的数据
  private static final String DIRTY = "DIRTY";  //脏数据,可能正在被修改
  private static final String REMOVE = "REMOVE"; //已被删除
  private static final String READ = "READ";  //正在被读取

文件名有后缀,journal文件中文件名信息并不完整,可能考虑以后缓存多分文件所以是以name.0开始命名
缓存文件。

    private Entry(String key) {
      this.key = key;
      this.lengths = new long[valueCount];
      cleanFiles = new File[valueCount];
      dirtyFiles = new File[valueCount];

      // The names are repetitive so re-use the same builder to avoid allocations.
      StringBuilder fileBuilder = new StringBuilder(key).append('.');
      int truncateTo = fileBuilder.length();
      for (int i = 0; i < valueCount; i++) {
          fileBuilder.append(i);
          cleanFiles[i] = new File(directory, fileBuilder.toString());
          fileBuilder.append(".tmp");
          dirtyFiles[i] = new File(directory, fileBuilder.toString());
          fileBuilder.setLength(truncateTo);
      }
    }

Glide的生命周期

Glide通过with(context)的方法创建一个RequestManager,这里要说的就是这个manager的生命周期,

  @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);
  }

如上代码如果context是Application,则生命跟随进程,若为Activity或FragmentActivity,则会创建一个空SupportRequestManagerFragment加到Activity中,监听activity的生命周期

  private RequestManager supportFragmentGet(
      @NonNull Context context,
      @NonNull FragmentManager fm,
      @Nullable Fragment parentHint,
      boolean isParentVisible) {
    SupportRequestManagerFragment current =
        getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
    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;
  }

由此即绑定了RequestManager与context的生命周期,发生变化后会通知给RequestManager中的Request和Target

  @Override
  public void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  /**
   * Lifecycle callback that unregisters for connectivity events (if the
   * android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
   */
  @Override
  public void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }

  /**
   * Lifecycle callback that cancels all in progress requests and clears and recycles resources for
   * all completed requests.
   */
  @Override
  public void onDestroy() {
    targetTracker.onDestroy();
    for (Target target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }

以此实现请求的暂停/继续与结束,该功能为我们实现滑动时暂停图片加载提供基础帮助。

你可能感兴趣的:(Glide实现原理记录)