关于Glide的理解.

glide图片缓存
glide源码分享

Glide特点.

  • 使用简单

  • 可配置度高,自适应程度高

  • 支持常见图片格式Jpg png gif webp

  • 支持多种数据源网络、本地、资源、Assets 等

  • 高效缓存策略支持Memory和Disk图片缓存 默认Bitmap格式采用RGB_565内存使用至少减少一半

  • 生命周期集成根据Activity/Fragment生命周期自动管理请求

  • 高效处理Bitmap使用Bitmap Pool使Bitmap复用,主动调用recycle回收需要回收的Bitmap,减小系统回收压力

Glide是如何提高加载图片的性能?

通过控制图片大小.
  • .override(200,200)可以设置加载图片大小,但是实际大小不一定是200x200,通过源码分析下:
    在BitmapRequestBuilder中的private Downsampler downsampler = Downsampler.AT_LEAST默认就是设置了尺寸优化,超过最大比例的就会对图片进行等比例缩放,如何缩放见下面;

  • 在Downsampler中的decode方法中,获取的Bitmap大小变成1/sampleSize,倍数通过getSampleSize计算所得,

  • inSampleSize 是 BitmapFactory.Options的属性,应该大家都知道。然后再看看怎么生成.override(200,200),如果没有设置Glide默认是FitCenter,查看FitCenter可以看到图片截取方式。

  • 举个例子:
    加载的图片大小为1080x540,如果使用了.override(200,200)默认缓存一张200x100的图片,也就是默认存储结果RESULT.

通过设置Bitmap Format的图片类型

为了降低内存消耗,Glide默认配置的Bitmap Format 为 RGB_565,修改GlideBuilder's setDecodeFormat设置.

 builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);

为何要使用额外的无界面的Fragment?

根据传入Context的类型有不同的实现,这里以FragmentActivity为例(现在常用的MD样式Activity类AppCompatActivity是FragmentActivity的子类)。方法get(FragmentActivity activity)调用了方法supportFragmentGet(activity, fm),后者返回的对象类型是SupportRequestManagerFragment 。SupportRequestManagerFragment 是一个无界面的Fragment类,起到把请求和Activity生命周期同步的作用。

Glide.with() 不仅仅只是Context还可以是Activity,Fragment等,传入后自动适配,Glide加载图片是会随着Activity,Fragment 的生命周期,具体可以参考LifecycleListener,所以推荐使用Activity,Fragment.

Glide的缓存策略是做的?

通过该方法设置策略.diskCacheStrategy(DiskCacheStrategy.ALL)
DiskCacheStrategy 分别有以下几种选择,ALL缓存原图和截取后的图,NONE 不缓存,SOURCE 只缓存原图,RESULT缓存截取后的图.

因此如果图片需要分享或需要原图的建议缓存ALL,否则只缓存RESULT.

这个核心的GlideModules的作用是什么?

是什么

  • GlideModule是对glide全局配置相关的类.

  • 如可以设置缓存策略.

  • 更多的配置如下
    https://raw.githubusercontent.com/goodbranch/AndroidNote/master/note/glide/glide-GlideBuilder-1.png

  • 可以通过实现GlideModule接口来自定义一个Glide图库,可以通过他改变Glide的行为和基础配置.

在自定义GlideModule时需要注意什么?

要全局的去声明这个类,让 Glide 知道它应该在哪里被加载和使用。Glide 会扫描 AndroidManifest.xml 为 Glide module 的 meta 声明。具体可以查看源码在Glide.get(Context),通过ManifestParser对象获取GlideModule集合.
因此,你必须在 AndroidManifest.xml 的 < application> 标签内去声明这个刚刚创建的 Glide module。

  1. 创建GlideModel

public class MyGlideModule implements GlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {

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

  }
}
  1. 在AndroidManifest.xml的meta-data配置GlideModule

<meta-data android:name="com.branch.glidedemo.MyGlideModule"
           android:value="GlideModule"/>
  1. 解决GlideModel冲突
    有可能加入的library中也同样配置了GlideModule,如果配置了多个会出现冲突,无法编译运行,解决方式可在AndroidManifest.xml移除

<meta-data android:name=”com.mypackage.MyGlideModule” tools:node=”remove” />

实际开发的一些问题

###为什么 有的图片第一次加载的时候只显示占位图,第二次才显示正常的图片呢?
如果你刚好使用了这个圆形Imageview库或者其他的一些自定义的圆形Imageview,而你又刚好设置了占位的话,那么,你就会遇到第一个问题。如何解决呢?
方案一: 不设置占位;
方案二:使用Glide的Transformation API自定义圆形Bitmap的转换。这里是一个已有的例子;
方案三:使用下面的代码加载图片:

Glide.with(mContext)
    .load(url) 
    .placeholder(R.drawable.loading_spinner)
    .into(new SimpleTarget<Bitmap>(width, height) {
        @Override 
        public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
            // setImageBitmap(bitmap) on CircleImageView 
        } 
    });

该方法在listview上复用有问题的bug,如果在listview中加载CircleImageView,请不要使用该方法。

方案四:不使用Glide的默认动画:

Glide.with(mContext)
    .load(url) 
    .dontAnimate()
    .placeholder(R.drawable.loading_spinner)
    .into(circleImageview);

###为什么 我总会得到类似You cannot start a load for a destroyed activity这样的异常呢?
请记住一句话:不要再非主线程里面使用Glide加载图片,如果真的使用了,请把context参数换成getApplicationContext。

提示:这个问题主要是context对应的生命周期引起的.当 Glide 检测到 Activity 被销毁时,会自动取消等待中的请求.如果你传递的是一个
getApplicationContext,Glide就不能对其进行优化,所以适当选择。

###为什么 我不能给加载的图片setTag()呢?
首先Glide的全局tag只是为了保证你可以正常的使用view.setTag方法,和错位没关系的。因为Glide内部就是通过view.setTag来保证不错位的.
进一步说明:
当图片从ListView 中移出屏幕时,Glide 也会取消其对应的请求。
由于大多数开发者在 adapter 中重用 View,Glide 会给在请求数据时给对应的ImageView 附加一个 tag,然后再载入其他图片时检查这个 tag,如果存在的话取消第一个请求,进而形成的优化。

使用setTag(int,object)方法设置tag,具体用法如下:
Java代码是酱紫的:

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(imageViewHolder.image);
        imageViewHolder.image.setTag(R.id.image_tag, i);
        imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
            @Override
                int position = (int) v.getTag(R.id.image_tag);
                Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
            }
        });

同时在values文件夹下新建ids.xml,添加
<item name="image_tag" type="id"/>

方案二:从Glide的3.6.0之后,新添加了全局设置的方法。具体方法如下:
先实现GlideMoudle接口,全局设置ViewTaget的tagId:

public class MyGlideMoudle implements GlideModule{
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        ViewTarget.setTagId(R.id.glide_tag_id);
    }

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

    }
}

同样,也需要在ids.xml下添加id
<item name="glide_tag_id" type="id"/>
最后在AndroidManifest.xml文件里面添加

<meta-data
    android:name="com.yourpackagename.MyGlideMoudle"
    android:value="GlideModule" />

方案三:写一个继承自ImageViewTaget的类,复写它的get/setRequest方法

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(new ImageViewTarget<GlideDrawable>(imageViewHolder.image) {
            @Override
            protected void setResource(GlideDrawable resource) {
                imageViewHolder.image.setImageDrawable(resource);
            }

            @Override
            public void setRequest(Request request) {
                imageViewHolder.image.setTag(i);
                imageViewHolder.image.setTag(R.id.glide_tag_id,request);
            }

            @Override
            public Request getRequest() {
                return (Request) imageViewHolder.image.getTag(R.id.glide_tag_id);
            }
        });

        imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = (int) v.getTag();
                Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
            }
        });

Glide错误java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity

解决办法 在使用Glide的那段代码加是否在主线程判断

if(Util.isOnMainThread()) {
Glide.with(MyActivity.this).load(strURL).into(imageView);
}

在onDestory加

@Override
protected void onDestroy() {
   super.onDestroy();
   if(Util.isOnMainThread()) {
       Glide.with(this).pauseRequest();
   }

并且所有的this 都要写成getApplicationContext ,这个主要针对于在子线程使用Glide.

Glide的一些进阶优化

  1. Glide.with(context).resumeRequests()和 Glide.with(context).pauseRequests()
    当列表在滑动的时候,调用pauseRequests()取消请求,滑动停止时,调用resumeRequests()恢复请求。这样是不是会好些呢?

  2. Glide.clear()
    当你想清除掉所有的图片加载请求时,这个方法可以帮助到你。

  3. ListPreloader
    如果你想让列表预加载的话,不妨试一下ListPreloader这个类。

可以提升图片加载速度。首先是在图片展示前预读取数据,
它提供了一个 ListPreloader,通过预加载 item 的数量初始化。接着通过
setOnScrollListener(OnScrollListener) 把 ListPreloader 设置给 ListView。如果你想在 ListView 之外预载图片,只要调用上面 DrawableRequestBuilder 对象的 downloadOnly() 方法就好,像这样 builder.downloadOnly();

demo

##每日一题: glide
glide图片缓存
glide源码分享

Glide特点.

  • 使用简单

  • 可配置度高,自适应程度高

  • 支持常见图片格式Jpg png gif webp

  • 支持多种数据源网络、本地、资源、Assets 等

  • 高效缓存策略支持Memory和Disk图片缓存 默认Bitmap格式采用RGB_565内存使用至少减少一半

  • 生命周期集成根据Activity/Fragment生命周期自动管理请求

  • 高效处理Bitmap使用Bitmap Pool使Bitmap复用,主动调用recycle回收需要回收的Bitmap,减小系统回收压力

Glide是如何提高加载图片的性能?

通过控制图片大小.
  • .override(200,200)可以设置加载图片大小,但是实际大小不一定是200x200,通过源码分析下:
    在BitmapRequestBuilder中的private Downsampler downsampler = Downsampler.AT_LEAST默认就是设置了尺寸优化,超过最大比例的就会对图片进行等比例缩放,如何缩放见下面;

  • 在Downsampler中的decode方法中,获取的Bitmap大小变成1/sampleSize,倍数通过getSampleSize计算所得,

  • inSampleSize 是 BitmapFactory.Options的属性,应该大家都知道。然后再看看怎么生成.override(200,200),如果没有设置Glide默认是FitCenter,查看FitCenter可以看到图片截取方式。

  • 举个例子:
    加载的图片大小为1080x540,如果使用了.override(200,200)默认缓存一张200x100的图片,也就是默认存储结果RESULT.

通过设置Bitmap Format的图片类型

为了降低内存消耗,Glide默认配置的Bitmap Format 为 RGB_565,修改GlideBuilder's setDecodeFormat设置.

 builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);

为何要使用额外的无界面的Fragment?

根据传入Context的类型有不同的实现,这里以FragmentActivity为例(现在常用的MD样式Activity类AppCompatActivity是FragmentActivity的子类)。方法get(FragmentActivity activity)调用了方法supportFragmentGet(activity, fm),后者返回的对象类型是SupportRequestManagerFragment 。SupportRequestManagerFragment 是一个无界面的Fragment类,起到把请求和Activity生命周期同步的作用。

Glide.with() 不仅仅只是Context还可以是Activity,Fragment等,传入后自动适配,Glide加载图片是会随着Activity,Fragment 的生命周期,具体可以参考LifecycleListener,所以推荐使用Activity,Fragment.

Glide的缓存策略是做的?

通过该方法设置策略.diskCacheStrategy(DiskCacheStrategy.ALL)
DiskCacheStrategy 分别有以下几种选择,ALL缓存原图和截取后的图,NONE 不缓存,SOURCE 只缓存原图,RESULT缓存截取后的图.

因此如果图片需要分享或需要原图的建议缓存ALL,否则只缓存RESULT.

这个核心的GlideModules的作用是什么?

是什么

  • GlideModule是对glide全局配置相关的类.

  • 如可以设置缓存策略.

  • 更多的配置如下
    https://raw.githubusercontent.com/goodbranch/AndroidNote/master/note/glide/glide-GlideBuilder-1.png

  • 可以通过实现GlideModule接口来自定义一个Glide图库,可以通过他改变Glide的行为和基础配置.

在自定义GlideModule时需要注意什么?

要全局的去声明这个类,让 Glide 知道它应该在哪里被加载和使用。Glide 会扫描 AndroidManifest.xml 为 Glide module 的 meta 声明。具体可以查看源码在Glide.get(Context),通过ManifestParser对象获取GlideModule集合.
因此,你必须在 AndroidManifest.xml 的 < application> 标签内去声明这个刚刚创建的 Glide module。

  1. 创建GlideModel

public class MyGlideModule implements GlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {

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

  }
}
  1. 在AndroidManifest.xml的meta-data配置GlideModule

<meta-data android:name="com.branch.glidedemo.MyGlideModule"
           android:value="GlideModule"/>
  1. 解决GlideModel冲突
    有可能加入的library中也同样配置了GlideModule,如果配置了多个会出现冲突,无法编译运行,解决方式可在AndroidManifest.xml移除

<meta-data android:name=”com.mypackage.MyGlideModule” tools:node=”remove” />

实际开发的一些问题

###为什么 有的图片第一次加载的时候只显示占位图,第二次才显示正常的图片呢?
如果你刚好使用了这个圆形Imageview库或者其他的一些自定义的圆形Imageview,而你又刚好设置了占位的话,那么,你就会遇到第一个问题。如何解决呢?
方案一: 不设置占位;
方案二:使用Glide的Transformation API自定义圆形Bitmap的转换。这里是一个已有的例子;
方案三:使用下面的代码加载图片:

Glide.with(mContext)
    .load(url) 
    .placeholder(R.drawable.loading_spinner)
    .into(new SimpleTarget<Bitmap>(width, height) {
        @Override 
        public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
            // setImageBitmap(bitmap) on CircleImageView 
        } 
    });

该方法在listview上复用有问题的bug,如果在listview中加载CircleImageView,请不要使用该方法。

方案四:不使用Glide的默认动画:

Glide.with(mContext)
    .load(url) 
    .dontAnimate()
    .placeholder(R.drawable.loading_spinner)
    .into(circleImageview);

###为什么 我总会得到类似You cannot start a load for a destroyed activity这样的异常呢?
请记住一句话:不要再非主线程里面使用Glide加载图片,如果真的使用了,请把context参数换成getApplicationContext。

提示:这个问题主要是context对应的生命周期引起的.当 Glide 检测到 Activity 被销毁时,会自动取消等待中的请求.如果你传递的是一个
getApplicationContext,Glide就不能对其进行优化,所以适当选择。

###为什么 我不能给加载的图片setTag()呢?
首先Glide的全局tag只是为了保证你可以正常的使用view.setTag方法,和错位没关系的。因为Glide内部就是通过view.setTag来保证不错位的.
进一步说明:
当图片从ListView 中移出屏幕时,Glide 也会取消其对应的请求。
由于大多数开发者在 adapter 中重用 View,Glide 会给在请求数据时给对应的ImageView 附加一个 tag,然后再载入其他图片时检查这个 tag,如果存在的话取消第一个请求,进而形成的优化。

使用setTag(int,object)方法设置tag,具体用法如下:
Java代码是酱紫的:

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(imageViewHolder.image);
        imageViewHolder.image.setTag(R.id.image_tag, i);
        imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
            @Override
                int position = (int) v.getTag(R.id.image_tag);
                Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
            }
        });

同时在values文件夹下新建ids.xml,添加
<item name="image_tag" type="id"/>

方案二:从Glide的3.6.0之后,新添加了全局设置的方法。具体方法如下:
先实现GlideMoudle接口,全局设置ViewTaget的tagId:

public class MyGlideMoudle implements GlideModule{
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        ViewTarget.setTagId(R.id.glide_tag_id);
    }

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

    }
}

同样,也需要在ids.xml下添加id
<item name="glide_tag_id" type="id"/>
最后在AndroidManifest.xml文件里面添加

<meta-data
    android:name="com.yourpackagename.MyGlideMoudle"
    android:value="GlideModule" />

方案三:写一个继承自ImageViewTaget的类,复写它的get/setRequest方法

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(new ImageViewTarget<GlideDrawable>(imageViewHolder.image) {
            @Override
            protected void setResource(GlideDrawable resource) {
                imageViewHolder.image.setImageDrawable(resource);
            }

            @Override
            public void setRequest(Request request) {
                imageViewHolder.image.setTag(i);
                imageViewHolder.image.setTag(R.id.glide_tag_id,request);
            }

            @Override
            public Request getRequest() {
                return (Request) imageViewHolder.image.getTag(R.id.glide_tag_id);
            }
        });

        imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = (int) v.getTag();
                Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
            }
        });

Glide错误java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity

解决办法 在使用Glide的那段代码加是否在主线程判断

if(Util.isOnMainThread()) {
Glide.with(MyActivity.this).load(strURL).into(imageView);
}

在onDestory加

@Override
protected void onDestroy() {
   super.onDestroy();
   if(Util.isOnMainThread()) {
       Glide.with(this).pauseRequest();
   }

并且所有的this 都要写成getApplicationContext ,这个主要针对于在子线程使用Glide.

Glide的一些进阶优化

  1. Glide.with(context).resumeRequests()和 Glide.with(context).pauseRequests()
    当列表在滑动的时候,调用pauseRequests()取消请求,滑动停止时,调用resumeRequests()恢复请求。这样是不是会好些呢?

  2. Glide.clear()
    当你想清除掉所有的图片加载请求时,这个方法可以帮助到你。

  3. ListPreloader
    如果你想让列表预加载的话,不妨试一下ListPreloader这个类。

可以提升图片加载速度。首先是在图片展示前预读取数据,
它提供了一个 ListPreloader,通过预加载 item 的数量初始化。接着通过
setOnScrollListener(OnScrollListener) 把 ListPreloader 设置给 ListView。如果你想在 ListView 之外预载图片,只要调用上面 DrawableRequestBuilder 对象的 downloadOnly() 方法就好,像这样 builder.downloadOnly();

demo

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值