glide源码分析

来源:互联网 发布:linux 系统api 编辑:程序博客网 时间:2024/06/05 05:55

项目介绍

图片加载框架,相对于UniversalImageLoader,Picasso,它还支持video,Gif,SVG格式,支持缩略图请求,旨在打造更好的列表图片滑动体验。Glide有生命周期的概念(主要是对请求进行pause,resume,clear),而且其生命周期与Activity/Fragment的生命周期绑定,支持Volley,OkHttp,并提供了相应的integration libraries,内存方面也更加友好。

特点

1.内存缓存有两级
2.支持gif
3.与okhttp无缝对接
4.高效处理Bitmap 使用Bitmap Pool使Bitmap复用,主动调用recycle回收需要回收的Bitmap,减小系统回收压力
5.支持多种数据源网络、本地、资源、Assets 等
6.高效缓存策略,支持Memory和Disk图片缓存。默认Bitmap格式采用RGB_565内存使用至少减少一半
7..Glide被设计成能和Activity/Fragment的生命周期完美的相结合。
8.本地视频剧照的解码
9.动画的支持

使用示例

                Glide.with(SplashActivity.this)                        .load("http://www.baidu.com/img/bdlogo.png")                        .into(mIvGlideTest);

定制glide

GlideBuilder.setMemoryCache(MemoryCache memoryCache).setBitmapPool(BitmapPool bitmapPool).setDiskCache(DiskCache.Factory diskCacheFactory).setDiskCacheService(ExecutorService service).setResizeService(ExecutorService service).setDecodeFormat(DecodeFormat decodeFormat)ARGB8888  和 RGB565  4字节2字节builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888)public class SimpleGlideModule implements GlideModule {      @Override public void applyOptions(Context context, GlideBuilder builder) {        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);    }    @Override public void registerComponents(Context context, Glide glide) {        // nothing to do here    }}

自定义内存缓存

public class CustomCachingGlideModule implements GlideModule {      @Override public void applyOptions(Context context, GlideBuilder builder) {        MemorySizeCalculator calculator = new MemorySizeCalculator(context);        int defaultMemoryCacheSize = calculator.getMemoryCacheSize();        int defaultBitmapPoolSize = calculator.getBitmapPoolSize();        int customMemoryCacheSize = (int) (0.2 * defaultMemoryCacheSize);        int customBitmapPoolSize = (int) (0.2 * defaultBitmapPoolSize);        builder.setMemoryCache( new LruResourceCache( customMemoryCacheSize );        builder.setBitmapPool( new LruBitmapPool( customBitmapPoolSize );    }    @Override public void registerComponents(Context context, Glide glide) {        // nothing to do here    }}

自定磁盘缓存

public class CustomCachingGlideModule implements GlideModule {      @Override    public void applyOptions(Context context, GlideBuilder builder) {        // set size & external vs. internal        int cacheSize100MegaBytes = 104857600;        builder.setDiskCache(            new InternalCacheDiskCacheFactory(context, cacheSize100MegaBytes)        );        //builder.setDiskCache(        //new ExternalCacheDiskCacheFactory(context, cacheSize100MegaBytes));    }    @Override    public void registerComponents(Context context, Glide glide) {        // nothing to do here    }}

磁盘缓存到指定目录

// or any other pathString downloadDirectoryPath = Environment.getDownloadCacheDirectory().getPath(); builder.setDiskCache(          new DiskLruCacheFactory( downloadDirectoryPath, cacheSize100MegaBytes ));// In case you want to specify a cache sub folder (i.e. "glidecache")://builder.setDiskCache(//    new DiskLruCacheFactory( downloadDirectoryPath, "glidecache", cacheSize100MegaBytes ) //);

总体设计

类图

来自于网络

核心模块分述

glide源码比较庞大,这里还是只对主要流程做一下分析:

先调用到Glide的with方法,具体代码如下:

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

再调用到RequestManagerRetriever的get方法

    public RequestManager get(FragmentActivity activity) {        if (Util.isOnBackgroundThread()) {            return get(activity.getApplicationContext());        } else {            assertNotDestroyed(activity);            FragmentManager fm = activity.getSupportFragmentManager();            return supportFragmentGet(activity, fm);        }    }

再调用到supportFragmentGet方法

    RequestManager supportFragmentGet(Context context, FragmentManager fm) {        SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);        RequestManager requestManager = current.getRequestManager();        if (requestManager == null) {            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());            current.setRequestManager(requestManager);        }        return requestManager;    }

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

之后会走到RequestManager的load方法:

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

会调得到一个DrawableRequestBuilder

    @Override    public DrawableRequestBuilder<ModelType> load(ModelType model) {        super.load(model);        return this;    }

再然后就会调用DrawableRequestBuilder的into方法

    @Override    public Target<GlideDrawable> into(ImageView view) {        return super.into(view);    }

再调用到父类的into方法

public Target<TranscodeType> into(ImageView view) {        Util.assertMainThread();        if (view == null) {            throw new IllegalArgumentException("You must pass in a non null View");        }        if (!isTransformationSet && view.getScaleType() != null) {            switch (view.getScaleType()) {                case CENTER_CROP:                    applyCenterCrop();                    break;                case FIT_CENTER:                case FIT_START:                case FIT_END:                    applyFitCenter();                    break;                //$CASES-OMITTED$                default:                    // Do nothing.            }        }        return into(glide.buildImageViewTarget(view, transcodeClass));    }

再调用到:

 public <Y extends Target<TranscodeType>> Y into(Y target) {        Util.assertMainThread();        if (target == null) {            throw new IllegalArgumentException("You must pass in a non null Target");        }        if (!isModelSet) {            throw new IllegalArgumentException("You must first set a model (try #load())");        }        Request previous = target.getRequest();        if (previous != null) {            previous.clear();            requestTracker.removeRequest(previous);            previous.recycle();        }        Request request = buildRequest(target);        target.setRequest(request);        lifecycle.addListener(target);        requestTracker.runRequest(request);        return target;    }

target.setRequest(request)也是一个比较值得注意的地方,如果target是ViewTarget,那么request会被设置到View的tag上。这样其实是有一个好处,每一个View有一个自己的Request,如果有重复请求,那么都会先去拿到上一个已经绑定的Request,并且从RequestManager中清理回收掉。这应该是去重的功能。

这个方法里面最重要的是:requestTracker.runRequest(request);

然后看一下requestTracker的runRequest的方法

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

这个方法里面的重点是:request.begin();

我们看下这个begin的具体实现:

 /**     * {@inheritDoc}     */    @Override    public void begin() {        startTime = LogTime.getLogTime();        if (model == null) {            onException(null);            return;        }        status = Status.WAITING_FOR_SIZE;        if (Util.isValidDimensions(overrideWidth, overrideHeight)) {            onSizeReady(overrideWidth, overrideHeight);        } else {            target.getSize(this);        }        if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {            target.onLoadStarted(getPlaceholderDrawable());        }        if (Log.isLoggable(TAG, Log.VERBOSE)) {            logV("finished run method in " + LogTime.getElapsedMillis(startTime));        }    }

这个方法里面会调用到onSizeReady

 @Override    public void onSizeReady(int width, int height) {        if (Log.isLoggable(TAG, Log.VERBOSE)) {            logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));        }        if (status != Status.WAITING_FOR_SIZE) {            return;        }        status = Status.RUNNING;        width = Math.round(sizeMultiplier * width);        height = Math.round(sizeMultiplier * height);        ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();        final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);        if (dataFetcher == null) {            onException(new Exception("Failed to load model: \'" + model + "\'"));            return;        }        ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();        if (Log.isLoggable(TAG, Log.VERBOSE)) {            logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));        }        loadedFromMemoryCache = true;        loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,                priority, isMemoryCacheable, diskCacheStrategy, this);        loadedFromMemoryCache = resource != null;        if (Log.isLoggable(TAG, Log.VERBOSE)) {            logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));        }    }

重点看这行代码: loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
这行代码调用的是engine的load方法,代码如下:

    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(TAG, Log.VERBOSE)) {            logWithTimeAndKey("Started new load", startTime, key);        }        return new LoadStatus(cb, engineJob);    }

先从cache中寻找资源,如果找到则将其从cache中移除并放入activeResources中,否则从activeResources中寻找。cache是LruResourceCache对象,作为资源的LRU缓存;activeResources是以弱引用为值的Map,用于缓存使用中的资源。比一般内存缓存额外多一级缓存的意义在于,当内存不足时清理cache中的资源时,不会对使用中的Bitmap造成影响。
若再次寻找失败,则创建EngineJob对象并调用其start方法。

上面有一些值得注意的地方:
内存缓存:在Glide中默认是LruResourceCache。当然你也可以自定义;
为何要两级内存缓存(loadFromActiveResources)。个人理解是一级缓存采用LRU算法进行缓存,并不能保证全部能命中,添加二级缓存提高命中率之用;
EngineJob和DecodeJob各自职责:EngineJob充当了管理和调度者,主要负责加载和各类回调通知;DecodeJob是真正干活的劳动者,这个类实现了Runnable接口。

        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);

建立一个runnable,提交到线程池中去执行

我们看一下EngineRunnable的run方法:

   public void run() {        if (isCancelled) {            return;        }        Exception exception = null;        Resource<?> resource = null;        try {            resource = decode();        } catch (Exception e) {            if (Log.isLoggable(TAG, Log.VERBOSE)) {                Log.v(TAG, "Exception decoding", e);            }            exception = e;        }        if (isCancelled) {            if (resource != null) {                resource.recycle();            }            return;        }        if (resource == null) {            onLoadFailed(exception);        } else {            onLoadComplete(resource);        }    }

在EngineRunnable的run方法中进行编码,根据缓存策略调用decodeFromCache或者decodeFromSource。

这里重点关注一下resource = decode();去看下decode的实现:

    private Resource<?> decode() throws Exception {        if (isDecodingFromCache()) {            return decodeFromCache();        } else {            return decodeFromSource();        }    }

这里有两个分支,一个是decodeFromCache,一个是decodeFromSource,先看一下decodeFromCache具体实现

    private Resource<?> decodeFromCache() throws Exception {        Resource<?> result = null;        try {            result = decodeJob.decodeResultFromCache();        } catch (Exception e) {            if (Log.isLoggable(TAG, Log.DEBUG)) {                Log.d(TAG, "Exception decoding result from cache: " + e);            }        }        if (result == null) {            result = decodeJob.decodeSourceFromCache();        }        return result;    }

现场时调用decodeJob.decodeResultFromCache(),如果是空的话,再调用decodeJob.decodeSourceFromCache()

看一下decodeJob.decodeResultFromCache的具体实现:

    public Resource<Z> decodeResultFromCache() throws Exception {        if (!diskCacheStrategy.cacheResult()) {            return null;        }        long startTime = LogTime.getLogTime();        Resource<T> transformed = loadFromCache(resultKey);        if (Log.isLoggable(TAG, Log.VERBOSE)) {            logWithTimeAndKey("Decoded transformed from cache", startTime);        }        startTime = LogTime.getLogTime();        Resource<Z> result = transcode(transformed);        if (Log.isLoggable(TAG, Log.VERBOSE)) {            logWithTimeAndKey("Transcoded transformed from cache", startTime);        }        return result;    }
    private Resource<T> loadFromCache(Key key) throws IOException {        File cacheFile = diskCacheProvider.getDiskCache().get(key);        if (cacheFile == null) {            return null;        }        Resource<T> result = null;        try {            result = loadProvider.getCacheDecoder().decode(cacheFile, width, height);        } finally {            if (result == null) {                diskCacheProvider.getDiskCache().delete(key);            }        }        return result;    }

大致含义是从文件缓存中取数据

再看decodeFromSource的实现

    public Resource<Z> decodeFromSource() throws Exception {        Resource<T> decoded = decodeSource();        return transformEncodeAndTranscode(decoded);    }

重点关注decodeSource:

    private Resource<T> decodeSource() throws Exception {        Resource<T> decoded = null;        try {            long startTime = LogTime.getLogTime();            final A data = fetcher.loadData(priority);            if (Log.isLoggable(TAG, Log.VERBOSE)) {                logWithTimeAndKey("Fetched data", startTime);            }            if (isCancelled) {                return null;            }            decoded = decodeFromSourceData(data);        } finally {            fetcher.cleanup();        }        return decoded;    }
            final A data = fetcher.loadData(priority);

编码前需要先通过DataFetcher访问网络获得文件流。接口DataFetcher的实现类根据配置而不同,设置为通过OkHttp3进行网络通信的情况下,该实现类为OkHttpStreamFetcher。
之后根据需要将文件流写入磁盘缓存,再对文件流进行编码。

这里就是从网络中取数据的位置。DataFetcher是一个接口,它有多种实现。

阅读体会&优缺点

不一样的地方,和fragment、activity生命周期同步

内部有一个bitmappool。由BitmapPool提供一个Bitmap作为下一步的Canvas载体。BitmapPool的实现类是LruBitmapPool,顾名思义是一个基于LRU方式的Bitmap缓存池,用于Bitmap的复用。

内存缓存是两级的内存缓存,命中率更高了

支持缩略图请求

源码的解析只是把最重要的加载流程走了一遍,有一些比较细节的地方没有关注,如果你有需要,可以自己跟着这个主线debug一下就能查找到。

todo/问题

更多资料

具体使用:
http://mrfu.me/2016/02/27/Glide_Getting_Started/
源码分析:
http://www.lightskystreet.com/2015/10/12/glide_source_analysis/
http://frodoking.github.io/2015/10/10/android-glide/

2 1