美文网首页
2018-10-09 glide

2018-10-09 glide

作者: 辣公公 | 来源:发表于2018-10-19 19:15 被阅读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上加载图片尽可能的快速和平滑:

    • 自动、智能地下采样(downsampling)和缓存(caching),以最小化存储开销和解码次数;
    • 积极的资源重用,例如字节数组和Bitmap,以最小化昂贵的垃圾回收和堆碎片影响;
    • 深度的生命周期集成,以确保仅优先处理活跃的Fragment和Activity的请求,并有利于应用在必要时释放资源以避免在后台时被杀掉。

    结构


    image.png

    Glide构造

    1. Glide.with()

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


    image

    initializeGlide主要做了以下工作:

    • 获取应用中带注解的GlideModule(annotationGeneratedModule),这里要解释一下GlideModule:用户自定义glide配置模块,用来修改默认的glide配置信息。如果这个为空或者可配置menifest里面的标志为true,则获取menifest里面配置的GlideModule模块(manifestModules)。
    • 把manifestModules以及annotationGeneratedModule里面的配置信息放到builder里面(applyOptions)替换glide默认组件(registerComponents)
      各种初始化信息Glide glide = builder.build(applicationContext);
      看一下build的源码,主要做了以下工作:
    • 创建请求图片线程池sourceExecutor,创建硬盘缓存线程池diskCacheExecutor。动画线程池animationExecutor
    • 依据设备的屏幕密度和尺寸设置各种pool的size
    • 创建图片线程池LruBitmapPool,缓存所有被释放的bitmap, LruBitmapPool依赖默认的缓存策略和缓存配置。缓存策略在API大于19时,为SizeConfigStrategy,小于为AttributeStrategy。其中SizeConfigStrategy是以bitmap的size和config为key,value为bitmap的HashMap
    • 创建对象数组缓存池LruArrayPool,默认4M
    • 创建LruResourceCache,内存缓存
    • new glide里面 new Registry()注册管理任务执行对象的类(Registry),可以简单理解为:Registry是一个工厂,而其中所有注册的对象都是一个工厂员工,当任务分发时,根据当前任务的性质,分发给相应员工进行处理

    先看代码

     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 整体时序图

    相关文章

      网友评论

          本文标题:2018-10-09 glide

          本文链接:https://www.haomeiwen.com/subject/hudyaftx.html