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/
- glide源码分析
- Glide 源码分析
- Glide源码分析 一
- Glide源码分析
- Glide源码分析
- Glide源码分析1 - 框架
- Glide源码分析-生命周期管理
- Glide源码分析-下载图片
- Glide的源码分析<一>
- android开发-Glide源码分析
- Glide源码分析3 -- 绑定Activity生命周期
- Glide使用简介与源码分析
- Glide 4.0源码分析(1)
- Glide 4.0源码分析(2)
- Glide 4.0源码分析(3)
- Glide 使用OkHttp加载图片源码分析
- Glide源码
- 深入Glide图片加载库的Demo源码分析
- iOS基础:枚举
- universal imageloader源码分析
- mysql5.7 RPM 安装
- Picasso源码分析
- 利用webview处理portal认证的wifi
- glide源码分析
- http 协议
- Android任务和返回栈完全解析
- Java基础类库
- 剑指offer题解 链表中倒数第k个结点
- Android studio复制项目
- Android中重要知识点
- java.io.Serializable浅析
- 高效率地加载位图Loading Large Bitmaps Efficently——翻译自developer.andoid.com Training