美文网首页
Glide 4.0学习笔记

Glide 4.0学习笔记

作者: niknowzcd | 来源:发表于2018-08-29 23:30 被阅读165次

    Glide 4.0使用

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

    有效使用范围

    Generated API 目前仅可以在 Application 模块内使用。这一限制可以让我们仅持有一份 Generated API,而不是各个 Library 和 Application 中均有自己定义出来的 Generated API。这一做法会让 Generated API 的调用更简单,并确保 Application 模块中 Generated API 调用的选项在各处行为一致。

    导入

    annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'  //注解依赖包 
    implementation 'com.github.bumptech.glide:glide:4.7.1'  //api包
    
    //Generated API  必须要导入上述注解依赖包才可以使用
    @GlideModule   
    public class MyAppGlideModule extends AppGlideModule {
    }
    

    Generated API

    Generated API 默认名为 GlideApp ,与 Application 模块中 AppGlideModule的子类包名相同。在 Application 模块中将 Glide.with() 替换为 GlideApp.with(),即可使用该 API 去完成加载工作

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

    Glide.with() 不同,诸如 fitCenter()placeholder() 等选项在 Builder 中直接可用,并不需要再传入单独的 RequestOptions 对象。

    占位符

    • 加载占位符(placeholder): 加载图片时展示,加载失败时没有设置error,显示的还是placeholder

    • 错误符(error):加载失败时展示,如果加载的url为null,且没有设置fallback时展示error

    • 后备回调符(fallback): 在请求的url为 null 时展示,表示允许用户url为null,比如用户头像还未设置时,展示的默认图片

    使用

    generated api

    GlideApp.with(fragment)
      .load(url)
      .placeholder(R.drawable.placeholder)
      .error(R.drawable.error)
      .fallback(R.drawable.fallback)
      .into(view)
    

    Glide方式

    RequestOptions options=new RequestOptions();
    options
        .placeholder(R.drawable.placeholder)
        .error(R.drawable.error)
        .fallback(R.drawable.fallback);
    Glide.with(this).load(url).apply(options).into(view);
    

    补充

    占位符是异步加载的吗?

    No。占位符是在主线程从Android Resources加载的。我们通常希望占位符比较小且容易被系统资源缓存机制缓存起来。

    变换是否会被应用到占位符上?

    No。Transformation仅被应用于被请求的资源,而不会对任何占位符使用。例如你正在加载圆形图片,你可能希望在你的应用中包含圆形的占位符。但是你也可以考虑自定义一个View来剪裁(clip)你的占位符,达到你的transformation的效果。

    在多个不同的View上使用相同的Drawable可行么?

    通常可以,但不是绝对的。任何无状态(non-stateful)的 Drawable(例如 BitmapDrawable )通常都是ok的。但是有状态的 Drawable 不一样,在同一时间多个 View 上展示它们通常不是很安全,因为多个View会立刻修改(mutate) Drawable 。对于有状态的 Drawable ,建议传入一个资源ID,或者使用 newDrawable() 来给每个请求传入一个新的拷贝。

    View的mutate

    如果同一个资源文件被两个drawable所使用,那么这两个drawable的状态会保持一致,其中一个drawable改变透明度,另外一个drawable也会跟着改变。这时如果想要两个drawable独立,就需要用到mutate

    drawableA.mutate().setAlpha(255); 
    drawableB.mutate().setAlpha(70);
    

    RequestOptions

    Glide4.0之后的原始api中很多样式的设置都需要通过RequestOptions,包括

    • 占位符(Placeholders)
    • 转换(Transformations)
    • 缓存策略(Caching Strategies)
    • 组件特有的设置项,例如编码质量,或Bitmap的解码配置等。

    比如要实现一个CenterCrop转换.

    RequestOptions cropOptions = new RequestOptions().centerCrop(context);
    ...
    Glide.with(fragment)
        .load(url)
        .apply(cropOptions)
        .into(imageView);
    

    其中apply()可以调用多次,也就是说你可以配置多个RequestOptions参数,如果 RequestOptions 对象之间存在相互冲突的设置,那么只有最后一个被应用的 RequestOptions 会生效。

    另外Glide内部还提供了静态方法来实现CenterCrop转换

    import static com.bumptech.glide.request.RequestOptions.centerCropTransform;
    
    Glide.with(fragment)
        .load(url)
        .apply(centerCropTransform(context))
        .into(imageView);
    

    当然如果你使用的是Generated API的话,就可以直接使用

    GlideApp.with(fragment)
        .load(url)
        .centerCrop()
        .into(imageView);
    

    图片加载过渡动画(TransitionOptions)

    使用 TransitionOption 可以应用以下变换:

    • View淡入
    • 与占位符交叉淡入
    • 或者什么都不发生
    import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
    
    Glide.with(fragment)
        .load(url)
        .transition(withCrossFade())   //交叉淡入
        .into(view);
    

    另外TransitionOptions是特定资源类型独有的,即加在bitmapdrawable的时候需要用到不同的api,如 BitmapTransitionOptions ,而不是 DrawableTransitionOptions

    请求主干RequestOptions

    Glide中请求的发起者是RequestBuilder这个对象。

    多个请求

    RequestBuilder<Drawable> requestBuilder =
            Glide.with(fragment)
                .asDrawable()
                .apply(requestOptions);
    
    for (int i = 0; i < numViews; i++) {
       ImageView view = viewGroup.getChildAt(i);
       String url = urls.get(i);
       requestBuilder.load(url).into(view);
    }
    

    缩略图请求

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

    缩略图应改指向一个低分辨率的图片,这样就快速加在并在主体图片加载之前显示。

    如果只有一个远程的url地址,可以强制要求加载一个低分辩率的图像,通过sizeMultiplier

    Glide.with(this)
            .load("")
            .thumbnail(0.25f)  // 尺寸是View的百分比
            .into(imageView);
    

    在Glide4.3之后还提供了一个失败加载

    Glide.with(fragment)
      .load(primaryUrl)
      .error(Glide.with(fragment)
          .load(fallbackUrl))
      .into(imageView);
    

    这个error请求之后在正常请求失败之后才会启动.

    多重变换

    默认情况下,每个 transform() 调用,或任何特定转换方法(fitCenter(), centerCrop(), bitmapTransform() )的调用都会替换掉之前的变换。

    其中 MultiTransformation

    Glide.with(fragment)
      .load(url)
      .transform(new MultiTransformation(new FitCenter(), new YourCustomTransformation())
      .into(imageView);
    

    自定义变化

    可以选择继承自BitmapTransformation或者DrawableTransitionOptions,

    需要注意的是,对于任何 Transformation 子类,包括 BitmapTransformation,你都有三个方法你 必须 实现它们,以使得磁盘和内存缓存正确地工作:

    1. equals()
    2. hashCode()
    3. updateDiskCacheKey

    ImageView的自动变换

    在Glide中,当你为一个 ImageView 开始加载时,Glide可能会自动应用 FitCenterCenterCrop这取决于view的 ScaleType 。如果 scaleTypeCENTER_CROP , Glide 将会自动应用 CenterCrop 变换。如果 scaleTypeFIT_CENTERCENTER_INSIDE ,Glide会自动使用 FitCenter 变换。

    当然,你总有权利覆写默认的变换,只需要一个带有 Transformation 集合的 RequestOptions 即可。另外,你也可以通过使用 dontTransform() 确保不会自动应用任何变换

    Gif的加载动画

    Glide可以将 Bitmap Transformation应用到 BitmapDrawable , GifDrawable , 以及 Bitmap 资源上,因此通常你只需要编写和应用 Bitmap``Transformation 。然而,如果你添加了额外的资源类型,你可能需要考虑派生 RequestOptions 类,并且,在内置的这些 Bitmap Transformations 之外,你还需要为你的自定义资源类型提供一个 Transformation

    目标Target

    Glide.with(fragment)
            .load(url)
            .into(imageView);  //into() 开始启动请求
    

    通常情况下,我们是这样使用Glide的,其中into(imageView)这行会将我们期望展示图片的容器ImageView传递进去。这时大部分人会想Glide是将下载好的图片直接赋值给ImgaeView,实际上这中间并不是一步到位的,

    ViewTarget<ImageView, Drawable> target = Glide.with(this)
            .load("")
            .into(imageView);
    

    如上述代码所示into(imageView)会返回一个ViewTarget对象,这个ViewTarget能对加载的图片进行各种操作,另外也能返回最开始的imageView,通过

    ImageView image=target.getView();
    

    既然target是每个imageView的包装,所以每个target都是唯一的,如果你使用之前的target来加载新的图片,就会重置之前的操作,将前一步加载的资源释放掉。

    ViewTarget<ImageView, Drawable> target = Glide.with(this)
                    .load(url)
                    .into(imageView);
    ... 
    // Some time in the future:
    Glide.with(fragment)
      .load(newUrl)
      .into(target);   //这时老的imageView上的图片资源会被释放,新图片会替代来图片展示
      
      方法二
      Glide.with(this).clear(target);
    

    Glide 的 ViewTarget 子类使用了 Android Framework 的 getTag()setTag() 方法来存储每个请求的相关信息,因此在使用Glide时,你还需要使用到setTag()时需要注意是否会出现冲突。

    最简单的解决方式是调用 setTag(int,object)

    加载目标尺寸

    默认情况下,Glide 使用目标通过 getSize 方法提供的尺寸来作为请求的目标尺寸。这允许 Glide 选取合适的 URL,下采样,裁剪和变换合适的图片以减少内存占用,并确保加载尽可能快地完成。

    target.getSize(new SizeReadyCallback(width,height));
    

    默认情况下Glide的尺寸加载逻辑

    1. 如果 View 的布局参数尺寸 > 0 且 > padding,则使用该布局参数;
    2. 如果 View 尺寸 > 0 且 > padding,使用该实际尺寸;
    3. 如果 View 布局参数为 wrap_content 且至少已发生一次 layout ,则打印一行警告日志,建议使用 Target.SIZE_ORIGINAL 或通过 override() 指定其他固定尺寸,并使用屏幕尺寸为该请求尺寸;
    4. 其他情况下(布局参数为 match_parent0, 或 wrap_content 且没有发生过 layout ),则等待布局完成,然后回溯到步骤1。

    Transition(过渡动画)

    Transition指的是从占位符到新加载的图片,或从缩略图到全尺寸图像过渡,而不是从一个请求到另一个请求的动画

    另外不同于 Glide v3,Glide v4 将不会默认应用交叉淡入或任何其他的过渡效果。每个请求必须手动应用过渡。

    动画的性能提示

    Android中的动画代价是比较大的,尤其是同时开始大量动画的时候。 交叉淡入和其他涉及 alpha 变化的动画显得尤其昂贵。 此外,动画通常比图片解码本身还要耗时。在列表和网格中滥用动画可能会让图像的加载显得缓慢而卡顿。为了提升性能,请在使用 Glide 向 ListView , GridView, 或 RecyclerView 加载图片时考虑避免使用动画,尤其是大多数情况下,你希望图片被尽快缓存和加载的时候。作为替代方案,请考虑预加载,这样当用户滑动到具体的 item 的时候,图片已经在内存中了。

    在多个请求间交叉淡入

    Transitions 并不能让你在不同请求中加载的两个图像之间做过渡。当新的加载被应用到 View 或 Target (查看 Target的文档 )上时,Glide 默认会取消任何已经存在的请求。因此,如果你想加载连个个不同的图片并在它们之间做动画,你无法直接通过 Glide 来完成。等待第一个加载完成并在 View 外持有这个 Bitmap 或 Drawable ,然后开始新的加载并手动在这两者之间做动画,诸如此类的策略看起来有效,但是实际上不安全,并可能导致程序崩溃或图像错误。

    相反,最简单的办法是使用包含两个 ImageViewViewSwitcher 来完成。将第一张图片加载到 getNextView() 的返回值里面,然后将第二张图片加载到 getNextView() 的下一个返回值中,并使用一个 RequestListener 在第二张图片加载完成时调用 showNext() 。为了更好地控制,你也可以使用 Android开发者文档 指出的策略。但要记住与 ViewSwitcher 一样,仅在第二次图像加载完成后才开始交叉淡入淡出。

    配置

    在非application中不能实现AppGlideModule

    内存缓存

    默认情况下,Glide使用 LruResourceCache ,这是 MemoryCache 接口的一个缺省实现,使用固定大小的内存和 LRU 算法。LruResourceCache 的大小由 Glide 的 MemorySizeCalculator 类来决定,这个类主要关注设备的内存类型,设备 RAM 大小,以及屏幕分辨率。

    应用程序可以自定义 MemoryCache 的大小,具体是在它们的 AppGlideModule 中使用 applyOptions(Context, GlideBuilder) 方法配置 MemorySizeCalculator

    @GlideModule
    public class AppGlideModule extends AppGlideModule {
      @Override
      public void applyOptions(Context context, GlideBuilder builder) {
        MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
            .setMemoryCacheScreens(2)
            .build();
        builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
      }
    }
    

    或者

    @GlideModule
    public class AppGlideModule extends AppGlideModule {
      @Override
      public void applyOptions(Context context, GlideBuilder builder) {
        int memoryCacheSizeBytes = 1024 * 1024 * 20; // 20mb
        builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
      }
    }
    

    Bitmap 池

    Glide 使用 LruBitmapPool 作为默认的 BitmapPoolLruBitmapPool 是一个内存中的固定大小的 BitmapPool,使用 LRU 算法清理。默认大小基于设备的分辨率和密度,同时也考虑内存类和 isLowRamDevice 的返回值。具体的计算通过 Glide 的 MemorySizeCalculator 来完成,与 Glide 的 MemoryCache的大小检测方法相似

    缓存模式

    默认情况下Glide会在开始一个新的图片请求之前按顺序检查下列各级缓存
    1. 活动资源 (Active Resources) - 现在是否有另一个 View 正在展示这张图片?
    2. 内存缓存 (Memory cache) - 该图片是否最近被加载过并仍存在于内存中?
    3. 资源类型(Resource) - 该图片是否之前曾被解码、转换并写入过磁盘缓存?
    4. 数据来源 (Data) - 构建这个图片的资源是否之前曾被写入过文件缓存?

    前两步检查图片是否在内存中,如果是则直接返回图片。后两步则检查图片是否在磁盘上,以便快速但异步地返回图片。

    如果四个步骤都未能找到图片,则Glide会返回到原始资源以取回数据(原始文件,Uri, Url等)。

    缓存的图片的key

    在 Glide v4 里,所有缓存键都包含至少两个元素:

    1. 请求加载的 model(File, Url, Url)
    2. 一个可选的 签名(Signature)

    另外,步骤1-3(活动资源,内存缓存,资源磁盘缓存)的缓存键还包含一些其他数据,包括:

    1. 宽度和高度
    2. 可选的变换(Transformation)
    3. 额外添加的任何 选项(Options)
    4. 请求的数据类型 (Bitmap, GIF, 或其他)

    活动资源和内存缓存使用的键还和磁盘资源缓存略有不同,以适应内存 选项(Options),比如影响 Bitmap 配置的选项或其他解码时才会用到的参数。

    为了生成磁盘缓存上的缓存键名称,以上的每个元素会被哈希化以创建一个单独的字符串键名,并在随后作为磁盘缓存上的文件名使用。

    磁盘缓存策略(Disk Cache Strateg)

    默认是AUTOMATIC模式,当你加载远程数据(比如,从URL下载)时,AUTOMATIC 策略仅会存储未被你的加载过程修改过(比如,变换,裁剪–译者注)的原始数据,因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,AUTOMATIC 策略则会仅存储变换过的缩略图,因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。

    修改缓存策略
    GlideApp.with(fragment)
      .load(url)
      .diskCacheStrategy(DiskCacheStrategy.ALL)
      .into(imageView);
    

    其他的一些api

    GlideApp.with(this)
            .load("")
            .onlyRetrieveFromCache(true)  //仅从缓存加载 (省流量模式)
            .skipMemoryCache(true)        //跳过缓存,加载原始图片 (图片验证码)
            .diskCacheStrategy(DiskCacheStrategy.NONE) //仅跳过磁盘缓存
            .into(imageView);
    
    刷新图片缓存的方式
    GlideApp.with(yourFragment)
        .load(yourFileDataModel)
        .signature(new ObjectKey(yourVersionMetadata))
        .into(yourImageView);
    

    加载图片的时候,传入一个可变的signature数据来控制图片的刷新

    清理磁盘

    new AsyncTask<Void, Void, Void> {
      @Override
      protected Void doInBackground(Void... params) {
        // This method must be called on a background thread.
        Glide.get(applicationContext).clearDiskCache();
        return null;
      }
    }
    

    注意需要开启子线程,而且需要传入application以防止内存泄露

    Glide 4.0 参考文档

    相关文章

      网友评论

          本文标题:Glide 4.0学习笔记

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