美文网首页Android DevAndroid TechAndroid Dev
Glide 一个专注于平滑滚动的图片加载和缓存库

Glide 一个专注于平滑滚动的图片加载和缓存库

作者: 倾城_之泪 | 来源:发表于2015-08-23 16:34 被阅读112121次

    在图片加载库烂大街的今天,选择一个适合自己使用的图片加载库已经成为了每一个Android开发者的必经之路。现在市面上知名的图片加载库有UIL,Picasso,Volley ImageLoader,Fresco以及我们今天的主角Glide。它们各有千秋,不能评定谁一定比谁好,只能说哪一个更适合你。

    我的理解

    下面我来谈一下个人对这些图片加载库的理解,如有错误,还望指教。

    Universal Image Loader:一个强大的图片加载库,包含各种各样的配置,最老牌,使用也最广泛。

    Picasso: Square出品,必属精品。和OkHttp搭配起来更配呦!

    Volley ImageLoader:Google官方出品,可惜不能加载本地图片~

    Fresco:Facebook出的,天生骄傲!不是一般的强大。

    Glide:Google推荐的图片加载库,专注于流畅的滚动。

    更多详情请看stackoverflow上这个问题

    初试Glide

    下面进入今天的主题,相信之前很多同学都看到过这篇介绍Glide的文章,中文版在这里。文中从各个方面介绍和比较了Glide与Picasso,总体来说二者极为相似,有着近乎相同的API的使用风格。但Glide在缓存策略和加载GIF方面略胜一筹。最后作者也极力推荐了这个库。

    而且据说在Google新出的Photos应用中,到处可见Glide的踪迹。看到这里,你是不是已经迫不及待的想试一试这个库呢?就在你下定决心尝试一记的时候,你又听说Yelp app(据说是美国的大众点评)也在使用这个吊炸天的库。你的心中激动万分,发四一定要使用这个库。说干就干,打开Android Studio,在builde.gradle里面添加上

    compile 'com.github.bumptech.glide:glide:3.6.1'
    

    然后全局搜索图片加载的地方,全部换成了下面的代码:

    Glide.with(mContext)
            .load(url)
            .placeholder(R.drawable.loading_spinner)
            .crossFade()
            .into(myImageView);
    

    在经过漫长的编译过程之后,再次打开APP,看到有着渐现效果的图片呈现在你的面前,你不禁叫道:“wocao,真TM帅!为什么我以前没有发现呢?”。

    不过在你使用了几天之后你会发现一些问题:

    为什么 有的图片第一次加载的时候只显示占位图,第二次才显示正常的图片呢?

    为什么 我总会得到类似You cannot start a load for a destroyed activity这样的异常呢?

    为什么 我不能给加载的图片setTag()呢?

    为什么?为什么?这么NB的库竟然会有这么多的问题。没错,这就是我今天要讲的重点。怎么避免上面的问题发生。

    一些解决方案

    1.如果你刚好使用了这个圆形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 
            } 
        }); 
    

    感谢aeecc0d15a40指出该方法在listview上复用有问题的bug,如果在listview中加载CircleImageView,请不要使用该方法。
    方案四:不使用Glide的默认动画:

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

    2.至于第二个问题,请记住一句话:不要再非主线程里面使用Glide加载图片,如果真的使用了,请把context参数换成getApplicationContext。更多的细节请参考这个issue

    3.为什么不能设置Tag,是因为你使用的姿势不对哦。如何为ImageView设置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();
                }
            });
    

    一些使用技巧

    1.Glide.with(context).resumeRequests()和 Glide.with(context).pauseRequests()

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

    2.Glide.clear()

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

    3.ListPreloader

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

    一些基于Glide的优秀库

    1.glide-transformations

    一个基于Glide的transformation库,拥有裁剪,着色,模糊,滤镜等多种转换效果,赞的不行不行的~~

    2.GlidePalette

    一个可以在Glide加载时很方便使用Palette的库。

    相关文章

      网友评论

      • ddaf45ff5e08:干货一篇,谢谢楼主科普!tag的问题困扰了我好久了,特别是这一阵,给某集团公司做内刊宣传册,时间又特别赶。。。为了这个内刊,最近真的是昼夜颠倒黑白无常,零食吃了一大堆……不但人胖成猪了, 黑眼圈直接挂到下巴上了啊!!! 胖的问题还好解决,抹三星期法国Fibretutu瘦身霜好歹也减掉了个十几斤,但黑眼圈真是无可奈何,消不去,老是有人跟我说你看起来好憔悴,哎。。。
        2f587fccbb69:和你一样,我也是用这个法国的Fibretutu减了21斤
      • 84e09d377fe0:简单明了,特别是设置Tag,按照作者方案完美解决问题。Nice, No problem!
      • 533c44e72971:Glide好像源码里就解决了错位的问题了吧:relaxed:
      • rivc:You cannot start a load for a destroyed activity 问题的解决办法,链接文章最后 http://www.jianshu.com/p/519bb23987ca
      • fuuuuuccccck:博主有没有遇过Bad position 的情况呢,issues有人提过,但是作者只说了原因,也没找到解决方案,用fresco和浏览器是可以加载出来的,比如这张图
        http://api.res.mojing.com//forum//20161223//14BDFAA1820B3CE0589EDFF9D90694CF.gif
        会报Bad position的错误
        倾城_之泪:@fuuuuuccccck glide在加载gif方面支持并不是很好,建议使用fresco或者先把图片下载到本地,自己解析gif
      • 十二点方向:为什么给imageview设置tag一般在适配器中不都是直接给convertView设置holder吗
      • 24K纯帅豆:不知道楼主知不知道如何加载占位图圆形
      • ea94f9641bea:recyclerview使用瀑布流导致加载图片会变形,要第二下刷新才会显示正常,求解楼主。
        代码如下: Glide.with(mContext).load(video.thumb).asBitmap().centerCrop().dontAnimate().into(holder.video_bg);
        倾城_之泪:@ea94f9641bea 可否给出相关的activity、adapter、和布局文件代码,这样好定位问题
      • 倾城_之泪:glide
        .load(new File(photo.getPath()))
        .asBitmap()
        .centerCrop()
        .dontAnimate()
        .placeholder(R.drawable.__picker_ic_photo_black_48dp)
        .error(R.drawable.__picker_ic_broken_image_black_48dp)
        // .diskCacheStrategy(DiskCacheStrategy.ALL)
        // .crossFade(100)
        .transform(new MyTransformation(context))
        .into(holder.ivPhoto);
        只显示占位图就加上dontAnimate()
        ysm_2015:@倾城_之泪 是偶尔快划的时候会有那么一两张只显示展位图的情况
      • ysm_2015:用了大神的方法,发现glide+RecycleView实现图片滑动会造成图片闪烁和位置错乱的问题,不知道大神有没有解决的办法
        ysm_2015:@倾城_之泪 可是我已经按照你的方法全局设置tag了呀
        倾城_之泪:@ysm_2015 这样是会错位的,因为没有给ImageView设置Tag,在ListView/RecyclerView中加载图片建议还是使用dontAnimate()或者自定义的Transformation
        ysm_2015:@ysm_2015 glide
        .load(new File(photo.getPath()))
        .asBitmap()
        .thumbnail(0.1f)
        // .override(imageSize, imageSize)
        .placeholder(R.drawable.__picker_ic_photo_black_48dp)
        .error(R.drawable.__picker_ic_broken_image_black_48dp)
        .into(new SimpleTarget<Bitmap>(450 , 450) {

        @Override
        public void onResourceReady(Bitmap arg0, GlideAnimation<? super Bitmap> arg1) {
        holder.ivPhoto.setImageBitmap(arg0);
        }
        });
      • dongbingliu:放一个简单地demo
      • dfcd519765e5:是不是不能加载selector?
      • 倾城_之泪:Glide.with(this)
        .load(uri)
        .asBitmap()
        .placeholder(R.drawable.ic_launcher)
        .into(new SimpleTarget<Bitmap>(450,450) {
        @Override
        public void onResourceReady(Bitmap resource,
        GlideAnimation<? super Bitmap> glideAnimation) {

        }
        });

        添加 asBitmap方法
      • 18388d2052dd:博主,我用你解决的方案:
        Glide.with(this)
        .load(uri)
        .placeholder(R.drawable.ic_launcher)
        .into(new SimpleTarget<Bitmap>(450 , 450) {

        @Override
        public void onResourceReady(Bitmap arg0, GlideAnimation<? super Bitmap> arg1) {
        iv_studentHeadImg.setImageBitmap(arg0);
        }
        });
        into那里会报错,请问是什么原因
      • Hanswf:我是listview 使用加载大量图片,在从上往下滑的过程中没有问题,但是从下晚上滑动,动作稍微快一点点就会直接跳到第一页,请问您有木有遇到过这种现象,什么原因,如何解决?

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = null;
        if (convertView == null) {
        convertView = LayoutInflater.from(mContext).inflate(R.layout.activity_ppt_show_item, parent, false);
        viewHolder = new ViewHolder();
        viewHolder.img = (ImageView) convertView.findViewById(R.id.pptImg);
        convertView.setTag(viewHolder);
        } else {
        viewHolder = (ViewHolder) convertView.getTag();
        }

        String path = list.get(position).get("VISIT_IMG_PATH").toString().replace("\\", "/");
        Glide.with(mContext).load(Urls.BASIC_URL2 + path).fitCenter().into(viewHolder.img);

        return convertView;
        }
      • 8321:RecycleView实现瀑布流滑动会出现图片位置错乱的现象,想问下作者是Glide配置引起的吗?
        灼眼行者:应该是position位置引起的,tag的正确使用应该能解决
        倾城_之泪:@王小波 你是怎么使用glide来加载图片的?
      • 三里小龙:分析的比较好,正好帮我理清楚了目前应该选择哪个图片加载库,thks :smile:
      • SingorZhu:谢谢,刚好最近遇到加载CircleImageView首次只显示placeholder的问题
      • 六边形战士:想问一下,我查看glide缓存都是在data目录下面,用哪个方法能够把缓存指向到sdcard里面呢?
        倾城_之泪:@ZiJan 可以自己建一个实现GlideModule接口的类,在applyOptions方法里面使用GlideBuilder.setDiskCache方法设置。如下:
        ```java
        public class MyGlideModule implements GlideModule {
        @Override
        public void applyOptions(Context context, GlideBuilder builder) {
        builder.setDiskCache(new ExternalCacheDiskCacheFactory(context));
        }

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

        }
        }
        ```
        使用ExternalCacheDiskCacheFactory可以缓存在sdcard,还可以自定义目录和大小。
        详情请看:https://github.com/bumptech/glide/wiki/Configuration#location
        最后不要忘记在manifest文件里面声明MyGlideModule
        ```xml
        <meta-data
        android:name="yourpackage.MyGlideModule"
        android:value="GlideModule"/>
        ```
      • 空心菜的爱:妈呀,看着就像看小说一样,赞一个
      • 小谷:我想问一下问什么我加载本地图片一直不成功
        ```
        File b = new File(Environment.getExternalStorageDirectory(), "DCIM/Camera/IMG_20151028_192555.jpg");
        File c = new File(Environment.getExternalStorageDirectory() + "/DCIM/Camera/IMG_20151028_192555.jpg");
        Glide.with(MainActivity.this).load(b).error(R.drawable.empty_pic).placeholder(R.drawable.empty_pic).into(image2);
        ```
        倾城_之泪:@小谷 可以添加一个listener查看加载失败的原因:
        Glide.with(MainActivity.this).load(b).error(R.drawable.empty_pic).placeholder(R.drawable.empty_pic).listener(new LoggingListener<String, GlideDrawable>()).into(image2);

        public class LoggingListener<T, R> implements RequestListener<T, R> {
        @Override public boolean onException(Exception e, Object model, Target target, boolean isFirstResource) {
        android.util.Log.d("GLIDE", String.format(Locale.ROOT,
        "onException(%s, %s, %s, %s)", e, model, target, isFirstResource), e);
        return false;
        }
        @Override public boolean onResourceReady(Object resource, Object model, Target target, boolean isFromMemoryCache, boolean isFirstResource) {
        android.util.Log.d("GLIDE", String.format(Locale.ROOT,
        "onResourceReady(%s, %s, %s, %s, %s)", resource, model, target, isFromMemoryCache, isFirstResource));
        return false;
        }
        }
        更多debug的方式请参考https://github.com/bumptech/glide/wiki/Debugging-and-Error-Handling,找到加载失败的原因才能对症下药
      • aeecc0d15a40:大神,使用圆形Imageview库时,遇到你说的问题.按照你提供的方法:
        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复用了之前的图片,并没有显示占位图片.,导致滚动图片闪烁的问题,不知道你有没有发现这个问题?
        宇光十色_FLY:我还是有图片复用的问题,在listview的adapter里,没有使用placeholder,加上了dontAnimate也不行。
        使用了ViewHolder
        aeecc0d15a40:@倾城_之泪
        Glide.with(context)
        .load(url)
        .asBitmap()
        .animate(R.anim.fade_in)//淡入动画效果
        .placeholder(placeholderResid)
        .into(imageView);
        恩,我后来这样子使用,解决了圆形Imageview库冲突的问题,也不会有复用的问题,具体为什么,我也不知道~~
        倾城_之泪:@aeecc0d15a40 https://github.com/bumptech/glide/issues/504#issuecomment-113459960,如果复用有问题的话,不要使用第三个方法,原来怎么加载的现在就怎么加载,不过要加上.dontAnimate()。
      • 浮游大虾:推荐使用Sketch https://github.com/xiaopansky/Sketch 以上所有问题都不存在,无需关心TAG,因为根本就不使用TAG来关联,也自带多种图片处理效果,圆形的、圆角的、高斯模糊的等等
        9ebb3be8d8cd:很强大
      • 乔伯:你这个文章只是从使用上来讲解,并不深入,如果有这几个库性能上的测试报告,那就更不错了。
      • MrFu:另外,不晓得楼主有没有碰到,一个页面加载大量大图片的情况出现无法加载的问题(非 ListView)
        issue 里有提到,但是并没有真正解决我的问题:
        https://github.com/bumptech/glide/issues/381
        https://github.com/bumptech/glide/issues/484
        https://github.com/bumptech/glide/issues/209
        https://github.com/bumptech/glide/issues/464

        其中[issue484](https://github.com/bumptech/glide/issues/484) 正是我碰到的问题

        作者是这样回答的:You're probably loading a lot of images at once on one screen and there's no more memory for the next one.
        If this is not the case, then you have a memory leak somewhere.

        In any case, reproduce the above exception in your app and then take a heap dump and check what's using so much memory.

        You should also note that non-top activities in the active app are not destroyed to reclaim memory.

        至今未解
        倾城_之泪:@MrFu 如果想和我继续交流的话,我的邮箱dfshizhiqiang@126.com,欢迎骚扰
        倾城_之泪:@MrFu 没有遇到过的,不过看issue里面是内存泄露的问题,还有很不明白为什么一定要拿到bitmap对象呢?它很有可能是内存泄露的问题
      • MrFu:棒!tag的问题困扰了我好久
      • Broncho:非常不错😊
      • 倾城_之泪:之前setTag()的第三个方法有问题,已经修改过了

      本文标题:Glide 一个专注于平滑滚动的图片加载和缓存库

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