Glide图片原理解析

1.Glide是什么?

Glide是Google在2014的IO大会发布一款图片处理框架,是目前android领域比较成熟的一款,也是Google官方推荐的图片处理框架,主要支持网络图片、二进制流、drawable资源、本地图片显示,还支持本地视频显示。

2.Glide基本功能使用

  1. 在app级别下面配置gradle
dependencies {
    compile 'com.github.bumptech.glide:glide:3.7.0'
}
  1. 使用起来
    通过Glide类进行一个链式调用,下面代码显示了一张网络图片。
  • with支持传入一下对象,我们在使用过程中尽量要传入activity、fragment因为glide会依赖它们的生命周期,如果onPaush时候,Glide就会暂停加载,重新onResume之后,又会继续加载。


    image
  • load() 支持网络图片、二进制流、drawable资源、本地图片的传入。

  • crossFade 这是开启显示淡入淡出的动画

  • override 如果获取的网络图片过大,我们通过它进行一个大小的裁剪,传入width和height参数进行宽高裁剪。

  • diskCacheStrategy 磁盘缓存的设置,默认Glide会开启的。

DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 只缓存全尺寸图
DiskCacheStrategy.RESULT 只缓存最终的加载图
DiskCacheStrategy.ALL 缓存所有版本图(默认行为)

Glide 不仅缓存了全尺寸的图,还会根据 ImageView 大小所生成的图也会缓存起来。比如,请求一个 800x600 的图加载到一个 400x300 的 ImageView 中,Glide默认会将这原图还有加载到 ImageView 中的 400x300 的图也会缓存起来。

  • error 这里的设置是当加载图片出现错误时,显示的图片。
  • placeholder 图片加载完成之前显示的占位图。


 String url = "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2912495429,3557331556&fm=27&gp=0.jpg";
        Glide
                .with(this)
                .load(url)
                .crossFade()
                .override(200, 200)
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .error(R.mipmap.active_user_default_icon)
                .placeholder(R.mipmap.ic_album_image_source_pick)
                .into(img);

获取Glide请求完成的Bitmap
通过前面这种方式我们能很轻松的显示一张,图片到ImgeView上。但是我们有时候想获取加载完成后的Bitmap怎么办呢,可以通过SimpleTarget来获取。

//SimpleTarget 获取bitmap,定义siez
    SimpleTarget<Bitmap> mSimpleTarget = new SimpleTarget<Bitmap>(200, 200) {
        @Override
        public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
            img.setImageBitmap(resource);
        }
    };

 //获取Bitmap的加载方法。
        Glide.with(getApplicationContext())
                .load(url)
                .asBitmap()
                .into(mSimpleTarget);

我们定义了一个SimpleTarget类,里面有一个onResourceReady方法的回调,会返回加载完成的Bitmap对象,into传入这个SimpleTarget对象。

自定义控件使用Glide显示Image
有时候我们自定义的控件也需要显示Imaeg,但是这个控件不是Image类或者子类,那么前面这种方法就会不使用了,我们可以通过ViewTarget来实现这个逻辑。

public class MyImageView extends FrameLayout {
    private ImageView mImageView;

    public MyImageView(@NonNull Context context) {
        super(context);
    }

    public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mImageView = new ImageView(getContext());
        addView(mImageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    public void setImage(Drawable drawable) {
        mImageView.setImageDrawable(drawable);
    }
}
 MyImageView img3;
 //ViewTarget自定义View也支持获取Bitmap
        ViewTarget viewTarget = new ViewTarget<MyImageView, GlideDrawable>(img3) {
            @Override
            public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
                img3.setImage(resource);
            }
        };

        Glide.with(getApplicationContext())
                .load(url)
                .into(viewTarget);

通过定义一个ViewTarget传入了二个泛型类,一个是MyImageView,一个是GlideDrawable,传入了img3。通过onResourceReady的回调我们可以对自定义控件进行图片显示了。

3.Glide原理了解?

Glide的with、load、into虽然只有三个方法调用但是内部的逻辑是很复杂的,成吨的代码这里我只做一个大概了解。首先with方法会构造一个单例的RequestManager对象。

with方法

 public static RequestManager with(FragmentActivity activity) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(activity);
    }
  public static RequestManagerRetriever get() {
        return INSTANCE;
    }

可以看出来我们一个程序生命周期内只有一个RequestManager对象,工程师在设计的时候很考虑到性能的问题。

load方法

 public DrawableTypeRequest<String> load(String string) {
        return (DrawableTypeRequest<String>) fromString().load(string);
    }

接着通过RequestManage对象的load方法返回一个DrawableTypeRequest类,它是一个DrawableRequestBuilder的一个子类,通过类名可以看出来主要作用是主要是构建请求中的一些参数用的,比如我们之前写的 override、error、placeholder、diskCacheStrategy这些辅助方法的设置。

into方法
最后的一个方法也是最复杂的一个,是真正请求网络请求的地方。

public Target<GlideDrawable> into(ImageView view) {
        return super.into(view);
    }
 public Target<TranscodeType> into(ImageView view) {
        Util.assertMainThread();
       ...
        return into(glide.buildImageViewTarget(view, transcodeClass));
    }
 @SuppressWarnings("unchecked")
    public <Z> Target<Z> buildTarget(ImageView view, Class<Z> clazz) {
        if (GlideDrawable.class.isAssignableFrom(clazz)) {
            return (Target<Z>) new GlideDrawableImageViewTarget(view);
        } else if (Bitmap.class.equals(clazz)) {
            return (Target<Z>) new BitmapImageViewTarget(view);
        } else if (Drawable.class.isAssignableFrom(clazz)) {
            return (Target<Z>) new DrawableImageViewTarget(view);
        } else {
            throw new IllegalArgumentException("Unhandled class: " + clazz
                    + ", try .as*(Class).transcode(ResourceTranscoder)");
        }
    }

into接收一个ViewTeger,

into的调用


 public <Y extends Target<TranscodeType>> Y into(Y target) {
      ...
        Request request = buildRequest(target);
        target.setRequest(request);
        lifecycle.addListener(target);
        requestTracker.runRequest(request);

        return target;
    }
 public void runRequest(Request request) {
        requests.add(request);
        if (!isPaused) {
            request.begin();
        } else {
            pendingRequests.add(request);
        }
    }

runRequest方法来控制请求的队列,如果当前视图状态是Paused状态就会把request添加到等待请求的队列,如果不是则直接执行。里面会有一个HttpUrlFetcher来执行前面封装好的网络请求

4.Glide是如何缓存的?

glide缓存主要分为

  1. 内存缓存

内存缓存主要是防止同样的图片重复读取到内存中来,解约JVM的内存空间

glide的内存缓存机制是使用LruCache算法实现,首先需要会需要一个key,这个key是通过url+构建请求时候的参数决定的。keyFactory.buildKey的代码就是生成Key的逻辑。glide内存缓存会调用二个方法来获取缓存。

  • loadFromCache
    使用LruCache

  • loadFromActiveResources
    使用弱引用

内存缓存的逻辑就是,先会在loadFromCache中去取缓存,这个是一个LruCaChe算啊实现的,如果取到了值就会把它存入一个弱引用中。这样的防止被LruCache算法回收掉,如果LruCache没有取到就会去loadFromActiveResources方法里面取。

如果都没有那就会开启子线程去网络请求图片。

 public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
            DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
            Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
        Util.assertMainThread();
        long startTime = LogTime.getLogTime();

        final String id = fetcher.getId();
        EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
                loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
                transcoder, loadProvider.getSourceEncoder());

        EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
        if (cached != null) {
            cb.onResourceReady(cached);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from cache", startTime, key);
            }
            return null;
        }

        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
            cb.onResourceReady(active);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Loaded resource from active resources", startTime, key);
            }
            return null;
        }

        EngineJob current = jobs.get(key);
        if (current != null) {
            current.addCallback(cb);
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                logWithTimeAndKey("Added to existing load", startTime, key);
            }
            return new LoadStatus(cb, current);
        }

        EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
        DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
                transcoder, diskCacheProvider, diskCacheStrategy, priority);
        EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
        jobs.put(key, engineJob);
        engineJob.addCallback(cb);
        engineJob.start(runnable);

        if (Log.isLoggable(TG, Log.VERBOSE)) {
            logWithTimeAndKey("Started new load", startTime, key);
        }
        return new LoadStatus(cb, engineJob);
    }
  1. 磁盘缓存

磁盘缓存主要是防止同一张网络图片,重复从网络中读取和下载,磁盘缓存也是使用LruCache算法实现的。

磁盘缓存有一个逻辑,当需要去加载一张图片的时候,Glide默认不会显示原始图片,而是会对图片进行压缩转换。经过这些转换操作之后才会把,图片显示出来,磁盘缓存默认就是缓存转换后的图片。

Glide

Glide的缓存策略是如果加载是一张1000 * 1000大小的图片,但是最终显示到ImageView上的只有500 * 500,Glide只会缓存最终显示的那张500*500的图片。

Glide的四种缓存策略

  1. DiskCacheStrategy.NONE 不缓存文件
  2. DiskCacheStrategy.SOURCE 只缓存原图
  3. DiskCacheStrategy.RESULT 只缓存最终加载的图(默认的缓存策略)
  4. DiskCacheStrategy.ALL 同时缓存原图和结果图

配置缓存策略
通过实现GlideModel接口来定义一个自定义的缓存策略类

 public class DiskCachMoudle implements GlideModule {

        @Override
        public void applyOptions(Context context, GlideBuilder builder) {

        }

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

        }
    }
<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".test.Activity2">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.jin.mak.DiskCachMoudle"
            android:value="DiskCachMoudle" />
    </application>

Glide源码里面的get方法会自动读取我们在ManifestParert中配置的那个DiskCachMoudle配置。

public static Glide get(Context context) {
        if (glide == null) {
            synchronized (Glide.class) {
                if (glide == null) {
                    Context applicationContext = context.getApplicationContext();

                    //第一步
                    List<GlideModule> modules = new ManifestParser(applicationContext).parse();
                    
                     //第二步
                    GlideBuilder builder = new GlideBuilder(applicationContext);
                    for (GlideModule module : modules) {
                        //在builder构造出glide之前,读取使用者自定义的配置.
                        module.applyOptions(applicationContext, builder);
                    }
                    glide = builder.createGlide();

                    //第三步
                    for (GlideModule module : modules) {
                        module.registerComponents(applicationContext, glide);
                    }
                }
            }
        }

        return glide;
    }


applyOptions 方法

  • builder.setBitmapPool ()
    这个是用来设置BitMap缓存池的,需要实现一个BitmapPool接口,它的默认实现是LruBitmapPool类。

  • builder. setMemoryCache()
    设置内存缓存,需要实现MemoryCache接口,它的默认实现是LruResourceCache类。

  • setResizeService(ExecutorService service)
    当资源不在缓存中时,需要通过这个Executor发起请求,默认是实现是FifoPriorityThreadPoolExecutor。

  • setDiskCacheService(ExecutorService service)
    读取磁盘缓存的服务,默认实现是FifoPriorityThreadPoolExecutor。

  • setDecodeFormat(DecodeFormat decodeFormat)
    用于控制Bitmap解码的清晰度,DecodeFormat可选的值有PREFER_ARGB_8888/PREFER_RGB_565,默认为PREFER_RGB_565。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • 【Android 库 Glide】 引用 Android图片加载框架最全解析(一),Glide的基本用法Andro...
    Rtia阅读 5,435评论 0 22
  • 前言 android中图片加载框架有很多,所有框架最终达到的目都是在Android平台上以极度简单的方式加载和展示...
    luoqiang108阅读 27,934评论 7 120
  • 7.1 压缩图片 一、基础知识 1、图片的格式 jpg:最常见的图片格式。色彩还原度比较好,可以支持适当压缩后保持...
    AndroidMaster阅读 2,515评论 0 13
  • Android缓存机制:如果没有缓存,在大量的网络请求从远程获取图片时会造成网络流量的浪费,加载速度较慢,用户体验...
    芒果味的你呀阅读 4,457评论 13 22
  • 6月13日,离开中卫,乘火车,独自。 甚是轻松 五个月没坐过火车了 许是久了 放弃两个小时的飞机 选择了十八个小时...
    王亦恺阅读 218评论 0 0