2018-10-09 glide

2018-10-19  本文已影响20人  辣公公

关于Glide

Glide是一个快速高效的Android图片加载库,注重于平滑的滚动。Glide提供了易用的API,高性能、可扩展的图片解码管道(decode pipeline),以及自动的资源池技术。



Glide 支持拉取,解码和展示视频快照,图片,和GIF动画。Glide的Api是如此的灵活,开发者甚至可以插入和替换成自己喜爱的任何网络栈。默认情况下,Glide使用的是一个定制化的基于HttpUrlConnection的栈,但同时也提供了与Google Volley和Square OkHttp快速集成的工具库。
虽然Glide 的主要目标是让任何形式的图片列表的滚动尽可能地变得更快、更平滑,但实际上,Glide几乎能满足你对远程图片的拉取/缩放/显示的一切需求。

API

Glide 使用简明的流式语法API,这是一个非常棒的设计,因为它允许你在大部分情况下一行代码搞定需求:

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

Generated API

Glide v4 使用 注解处理器 (Annotation Processor) 来生成出一个 API,在 Application 模块中可使用该流式 API 一次性调用到 RequestBuilderRequestOptions 和集成库中所有的选项。

Generated API 模式的设计出于以下两个目的:

  1. 集成库可以为 Generated API 扩展自定义选项。
  2. 在 Application 模块中可将常用的选项组打包成一个选项在 Generated API 中使用

虽然以上所说的工作均可以通过手动创建 RequestOptions 子类的方式来完成,但想将它用好更具有挑战,并且降低了 API 使用的流畅性。

GlideApp.with(fragment)
   .load(myUrl)
   .placeholder(R.drawable.placeholder)
   .fitCenter()
   .into(imageView);

性能

Glide 充分考虑了Android图片加载性能的两个关键方面:

为了让用户拥有良好的App使用体验,图片不仅要快速加载,而且还不能因为过多的主线程I/O或频繁的垃圾回收导致页面的闪烁和抖动现象。
Glide使用了多个步骤来确保在Android上加载图片尽可能的快速和平滑:

结构


image.png

Glide构造

  1. Glide.with()

作用:初始化glide,返回一个RequestManger对象
以传入的是FragmentActivity为例,getRetriever (activity)流程如下:


image

initializeGlide主要做了以下工作:

先看代码

 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.getActivity()).get(fragment);
  }
 public static RequestManager with(@NonNull android.app.Fragment fragment) {
    return getRetriever(fragment.getActivity()).get(fragment);
  }
 public static RequestManager with(@NonNull View view) {
    return getRetriever(view.getContext()).get(view);
  }

可以看到,with方法有很多,但内容基本一致,都是通过 RequestManagerRetriever.get(); 获取一个 RequestManagerRetriever 对象 retriever ,然后通过 retriever.get(context); 获取一个 RequestManager 对象并返回。这些with方法关键的不同在于传入的参数不一致,可以是Context、Activity、Fragment等等。那么为什么要分这么多种呢?其实我们应该都知道:Glide在加载图片的时候会绑定 with(context) 方法中传入的 context 的生命周期,如果传入的是 Activity ,那么在这个 Activity 销毁的时候Glide会停止图片的加载。这样做的好处是显而易见的:避免了消耗多余的资源,也避免了在Activity销毁之后加载图片从而导致的空指针问题。

初始化glide,单例模式

 public static Glide get(@NonNull Context context) {
    if (glide == null) {
      synchronized (Glide.class) {
        if (glide == null) {
          checkAndInitializeGlide(context);
        }
      }
    }
    return glide;
  }

防止重复初始化

 private static void checkAndInitializeGlide(@NonNull Context context) {
    // In the thread running initGlide(), one or more classes may call Glide.get(context).
    // Without this check, those calls could trigger infinite recursion.
    if (isInitializing) {
      throw new IllegalStateException("You cannot call Glide.get() in registerComponents(),"
          + " use the provided Glide instance instead");
    }
    isInitializing = true;
    initializeGlide(context);
    isInitializing = false;
  }

加载自定义的配置例如自定义的modeloader,网络加载栈等,注册ComponentCallbacks,在低内存时 memoryCache, bitmapPool ,arrayPool 释放到内存

 private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
    Context applicationContext = context.getApplicationContext();
    GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules();
    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();
      }
    }

    if (Log.isLoggable(TAG, Log.DEBUG)) {
      for (com.bumptech.glide.module.GlideModule glideModule : manifestModules) {
        Log.d(TAG, "Discovered GlideModule from manifest: " + glideModule.getClass());
      }
    }

    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 = builder.build(applicationContext);
    for (com.bumptech.glide.module.GlideModule module : manifestModules) {
      module.registerComponents(applicationContext, glide, glide.registry);
    }
    if (annotationGeneratedModule != null) {
      annotationGeneratedModule.registerComponents(applicationContext, glide, glide.registry);
    }
    applicationContext.registerComponentCallbacks(glide);
    Glide.glide = glide;
  }

构建Glide,配置数据转换器/解码器/转码器/编码器

Glide(
      @NonNull Context context,
      @NonNull Engine engine,
      @NonNull MemoryCache memoryCache,
      @NonNull BitmapPool bitmapPool,
      @NonNull ArrayPool arrayPool,
      @NonNull RequestManagerRetriever requestManagerRetriever,
      @NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
      int logLevel,
      @NonNull RequestOptions defaultRequestOptions,
      @NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {
    this.engine = engine;
    this.bitmapPool = bitmapPool;
    this.arrayPool = arrayPool;
    this.memoryCache = memoryCache;
    this.requestManagerRetriever = requestManagerRetriever;
    this.connectivityMonitorFactory = connectivityMonitorFactory;

    DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
    bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);

    final Resources resources = context.getResources();

    registry = new Registry();
    // Right now we're only using this parser for HEIF images, which are only supported on OMR1+.
    // If we need this for other file types, we should consider removing this restriction.
    // Note that order here matters. We want to check the ExifInterface parser first for orientation
    // and then fall back to DefaultImageHeaderParser for other fields.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
      registry.register(new ExifInterfaceImageHeaderParser());
    }
    registry.register(new DefaultImageHeaderParser());

    Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
        resources.getDisplayMetrics(), bitmapPool, arrayPool);
    ByteBufferGifDecoder byteBufferGifDecoder =
        new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
    ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
        VideoDecoder.parcel(bitmapPool);
    ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
    StreamBitmapDecoder streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
    ResourceDrawableDecoder resourceDrawableDecoder =
        new ResourceDrawableDecoder(context);
    ResourceLoader.StreamFactory resourceLoaderStreamFactory =
        new ResourceLoader.StreamFactory(resources);
    ResourceLoader.UriFactory resourceLoaderUriFactory =
        new ResourceLoader.UriFactory(resources);
    ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
        new ResourceLoader.FileDescriptorFactory(resources);
    ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
        new ResourceLoader.AssetFileDescriptorFactory(resources);
    BitmapEncoder bitmapEncoder = new BitmapEncoder(arrayPool);

    BitmapBytesTranscoder bitmapBytesTranscoder = new BitmapBytesTranscoder();
    GifDrawableBytesTranscoder gifDrawableBytesTranscoder = new GifDrawableBytesTranscoder();

    ContentResolver contentResolver = context.getContentResolver();

    registry
        .append(ByteBuffer.class, new ByteBufferEncoder())
        //省略很多行
        .register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);

    ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
    glideContext =
        new GlideContext(
            context,
            arrayPool,
            registry,
            imageViewTargetFactory,
            defaultRequestOptions,
            defaultTransitionOptions,
            engine,
            logLevel);
  }

模型转换器

转换器 功能
ResourceLoader.StreamFactory 将Android资源ID转换为Uri,在加载成为InputStream
ResourceLoader.UriFactory 将资源ID转换为Uri
ResourceLoader.FileDescriptorFactory 将资源ID转化为ParcelFileDescriptor
ResourceLoader.AssetFileDescriptorFactory 将资源ID转化为AssetFileDescriptor
UnitModelLoader.Factory 不做任何转换,返回源数据
ByteBufferFileLoader.Factory 将File转换为ByteBuffer
FileLoader.StreamFactory 将File转换为InputStream
FileLoader.FileDescriptorFactory 将File转化为ParcelFileDescriptor
DataUrlLoader.StreamFactory 将Url转化为InputStream
StringLoader.StreamFactory 将String转换为InputStream
StringLoader.AssetFileDescriptorFactory 将String转换为AssetFileDescriptor
HttpUriLoader.Factory 将http/https Uri转换为InputStream
UriLoader.StreamFactory 将Uri转换为InputStream
UriLoader.FileDescriptorFactory 将Uri转换为ParcelFileDescriptor
UriLoader.AssetFileDescriptorFactory 将Uri转换为AssetFileDescriptor
UrlUriLoader.StreamFactory 将将http/https的Uri转换为InputStream
UrlLoader.StreamFactory 将Url转换为InputStream
HttpGlideUrlLoader.Factory 将HttpGlide转换为InputStream

解码器

解码器 功能
ByteBufferGifDecoder 将ByteBuffer解码为GifDrawable
ByteBufferBitmapDecoder 将ByteBuffer解码为Bitmap
ResourceDrawableDecoder 将资源Uri解码为Drawable
ResourceBitmapDecoder 将资源ID解码为Bitmap
BitmapDrawableDecoder 将数据解码为BitmapDrawable
StreamBitmapDecoder 将InputStreams解码为Bitmap
StreamGifDecoder 将InputStream数据转换为BtyeBuffer,再解码为GifDrawable
GifFrameResourceDecoder 解码gif帧
FileDecoder 包装File成为FileResource
UnitDrawableDecoder 将Drawable包装为DrawableResource
UnitBitmapDecoder 包装Bitmap成为BitmapResource
VideoDecoder 将本地视频文件解码为Bitmap

编码器

编码器 功能
ByteBufferEncoder 将Byte数据缓存为File
StreamEncoder InputStream缓存为File
BitmapEncoder 将Bitmap数据缓存为File
BitmapDrawableEncoder 将BitmapDrawable数据缓存为File
GifDrawableEncoder 将GifDrawable数据缓存为File

Glide的加载流程可以概括为以下流程:

model(数据源)-->data(转换数据)-->decode(解码)-->transformed(缩放)-->transcoded(转码)-->encoded(编码保存到本地)

Glide 生命周期管理

  1. ComponentCallbacks
public interface ComponentCallbacks2{
    //当低内存时回调,
    void onTrimMemory(int );
}

当回调onTrimMemory(int level)执行以下代码,
释放 memoryCache bitmapPool arrayPool 一些对象

 public void trimMemory(int level) {
    // Engine asserts this anyway when removing resources, fail faster and consistently
    Util.assertMainThread();
    // memory cache needs to be trimmed before bitmap pool to trim re-pooled Bitmaps too. See #687.
    memoryCache.trimMemory(level);
    bitmapPool.trimMemory(level);
    arrayPool.trimMemory(level);
  }
  1. 与activity生命周期绑定,在Activity 添加一个不可见的RequestManagerFragment,监听其生命周期来决定当前加载任务需不需要取消
private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    if (current == null) {
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        current = new RequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        pendingRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }
  1. 如何加载GIF 文件
    注意看GifFrameLoader,注意逻辑在这里,用handle不断loadNextFrame;
  SourceGenerator-->HttpUrlFetcher-->inputstream (网络获取数据)-> lrudiskcache 保存到本地-->DataCacheGenerator-->ByteBufferFetcher-->ByteBuffer-->byteBufferGifDecoder-->GifDrawableResource-->GifDrawable --> GifFrameLoader  (这里面就开始循坏显示图片)
1609120f27c3cf3a.png 111111.png 整体时序图
上一篇 下一篇

猜你喜欢

热点阅读