Glide源码精编解析(腾讯面试参考答案)

87 阅读8分钟

// RequestManagerRetriever.get(...) @TargetApi(Build.VERSION_CODES.HONEYCOMB) public RequestManager get(Activity activity) { if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return get(activity.getApplicationContext()); } else { assertNotDestroyed(activity); android.app.FragmentManager fm = activity.getFragmentManager(); return fragmentGet(activity, fm, null); } } @TargetApi(Build.VERSION_CODES.HONEYCOMB) RequestManager fragmentGet(Context context, android.app.FragmentManager fm, android.app.Fragment parentHint) { RequestManagerFragment current = getRequestManagerFragment(fm, parentHint); RequestManager requestManager = current.getRequestManager(); if (requestManager == null) { requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode()); current.setRequestManager(requestManager); } return requestManager; }

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) RequestManagerFragment getRequestManagerFragment( final android.app.FragmentManager fm, android.app.Fragment parentHint) { RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); if (current == null) { current = pendingRequestManagerFragments.get(fm); if (current == null) { current = new RequestManagerFragment(); current.setParentFragmentHint(parentHint); pendingRequestManagerFragments.put(fm, current); fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss(); handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget(); } } return current; }

2、创建一个RequestBuilder,并添加一个DrawableTransitionOptions类型的转场动画

public RequestBuilder asDrawable() { return as(Drawable.class).transition(new DrawableTransitionOptions()); }

3、加载对象(model域)

public RequestBuilder load(@Nullable Object model) { return loadGeneric(model); }

private RequestBuilder loadGeneric(@Nullable Object model) { this.model = model; isModelSet = true; return this; }

4、装载对象(包含请求的发起点)。

public <Y extends Target> Y into(@NonNull Y target) { Util.assertMainThread(); Preconditions.checkNotNull(target); if (!isModelSet) { throw new IllegalArgumentException("You must call #load() before calling #into()"); }

Request previous = target.getRequest();

if (previous != null) { requestManager.clear(target); }

requestOptions.lock(); Request request = buildRequest(target); target.setRequest(request); requestManager.track(target, request);

return target; }

一般而言,大部分使用者都是用来装载图片的,因此都会调用如下这个方法:

public Target into(ImageView view) { Util.assertMainThread(); Preconditions.checkNotNull(view);

if (!requestOptions.isTransformationSet() && requestOptions.isTransformationAllowed() && view.getScaleType() != null) { if (requestOptions.isLocked()) { requestOptions = requestOptions.clone(); } switch (view.getScaleType()) { case CENTER_CROP: requestOptions.optionalCenterCrop(context); break; case FIT_CENTER: case FIT_START: case FIT_END: requestOptions.optionalFitCenter(context); break; //CASESOMITTEDCASES-OMITTED default: // Do nothing. } }

return into(context.buildImageViewTarget(view, transcodeClass)); }

这里针对ImageView的填充方式做了筛选并对应设置到requestOptions上。最终的是通过ImageView和转码类型(transcodeClass)创建不通过的Target(例如Bitmap对应的BitmapImageViewTarget和Drawable对应的DrawableImageViewTarget)

4.1 Request的创建buildRequest(target)。 在Request的创建中会针对是否有缩略图来创建不同尺寸的请求,缩略图方法可以使用RequestBuilder.thumbnail(…)方法来添加上。 Glide中的Request都是使用了SingleRequest类,当然缩略图采用的是ThumbnailRequestCoordinator类:

private Request obtainRequest(Target target, BaseRequestOptions<?> requestOptions, RequestCoordinator requestCoordinator, TransitionOptions<?, ? super TranscodeType> transitionOptions, Priority priority, int overrideWidth, int overrideHeight) { requestOptions.lock();

return SingleRequest.obtain( context, model, transcodeClass, requestOptions, overrideWidth, overrideHeight, priority, target, requestListener, requestCoordinator, context.getEngine(), transitionOptions.getTransitionFactory()); }

比较值得推崇的是SingleRequest.obtain写法,个人认为比new关键字更简洁明了吧。

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

4.2 requestManager.track(target, request) 这个方法非常的复杂,主要用于触发请求、编解码、装载和缓存这些功能。下面就一步一步来看吧:

4.2.1 缓存target,并启动Request

void track(Target<?> target, Request request) { targetTracker.track(target); requestTracker.runRequest(request); } /**

  • Starts tracking the given request. */ public void runRequest(Request request) { requests.add(request); //添加内存缓存 if (!isPaused) { request.begin(); // 开始 } else { pendingRequests.add(request); // 挂起请求 } }

继续看一下SingleRequest中的begin方法:

@Override public void begin() { stateVerifier.throwIfRecycled(); startTime = LogTime.getLogTime(); // 如果model空的,那么是不能执行的。 这里的model就是前面提到的RequestBuilder中的model if (model == null) { if (Util.isValidDimensions(overrideWidth, overrideHeight)) { width = overrideWidth; height = overrideHeight; } // Only log at more verbose log levels if the user has set a fallback drawable, because // fallback Drawables indicate the user expects null models occasionally. int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG; onLoadFailed(new GlideException("Received null model"), logLevel); return; }

status = Status.WAITING_FOR_SIZE; // 如果当前的View尺寸已经加载获取到了,那么就会进入真正的加载流程。 if (Util.isValidDimensions(overrideWidth, overrideHeight)) { onSizeReady(overrideWidth, overrideHeight); } else { // 反之,当前View还没有画出来,那么是没有尺寸的。 // 这里会调用到ViewTreeObserver.addOnPreDrawListener。 // 等待View的尺寸都ok,才会继续 target.getSize(this); }

// 如果等待和正在执行状态,那么当前会加载占位符Drawable if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE) && canNotifyStatusChanged()) { target.onLoadStarted(getPlaceholderDrawable()); } if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished run method in " + LogTime.getElapsedMillis(startTime)); } }

接下来是target.getSize(this)方法。这里主要说一下尺寸未加载出来的情况(ViewTarget.java):

void getSize(SizeReadyCallback cb) { int currentWidth = getViewWidthOrParam(); int currentHeight = getViewHeightOrParam(); if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) { cb.onSizeReady(currentWidth, currentHeight); } else { // We want to notify callbacks in the order they were added and we only expect one or two // callbacks to // be added a time, so a List is a reasonable choice. if (!cbs.contains(cb)) { cbs.add(cb); } if (layoutListener == null) { final ViewTreeObserver observer = view.getViewTreeObserver(); layoutListener = new SizeDeterminerLayoutListener(this); // 绘画之前加入尺寸的监听。这一点我想大部分Android开发同学应该都知道。 // 接下来在看看系统触发该Listener时target又干了些什么。 observer.addOnPreDrawListener(layoutListener); } } }

private static class SizeDeterminerLayoutListener implements ViewTreeObserver .OnPreDrawListener { // 注意这里是弱引用 private final WeakReference sizeDeterminerRef;

public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) { sizeDeterminerRef = new WeakReference<>(sizeDeterminer); }

@Override public boolean onPreDraw() { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "OnGlobalLayoutListener called listener=" + this); } SizeDeterminer sizeDeterminer = sizeDeterminerRef.get(); if (sizeDeterminer != null) { // 通知SizeDeterminer去重新检查尺寸,并触发后续操作。 // SizeDeterminer有点像工具类,又作为尺寸回调的检测接口 sizeDeterminer.checkCurrentDimens(); } return true; } }

ok,继续回到SingleRequest.onSizeReady方法,主要就是Engine发起load操作

public void onSizeReady(int width, int height) { stateVerifier.throwIfRecycled(); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime)); } if (status != Status.WAITING_FOR_SIZE) { return; } status = Status.RUNNING;

float sizeMultiplier = requestOptions.getSizeMultiplier(); this.width = Math.round(sizeMultiplier * width); this.height = Math.round(sizeMultiplier * height);

if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime)); } loadStatus = engine.load( glideContext, model, requestOptions.getSignature(), this.width, this.height, requestOptions.getResourceClass(), transcodeClass, priority, requestOptions.getDiskCacheStrategy(), requestOptions.getTransformations(), requestOptions.isTransformationRequired(), requestOptions.getOptions(), requestOptions.isMemoryCacheable(), this); if (Log.isLoggable(TAG, Log.VERBOSE)) { logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime)); } }

特别的,所有的操作都是来之唯一一个Engine,它的创建是来至于Glide的初始化。如果有需要修改缓存配置的同学可以继续看一下diskCacheFactory的创建:

if (engine == null) { engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor); }

继续看一下Engine.load的详细过程:

public LoadStatus load( GlideContext glideContext, Object model, Key signature, int width, int height, Class resourceClass, Class transcodeClass, Priority priority, DiskCacheStrategy diskCacheStrategy, Map, Transformation<?>> transformations, boolean isTransformationRequired, Options options, boolean isMemoryCacheable, ResourceCallback cb) { Util.assertMainThread(); long startTime = LogTime.getLogTime();

// 创建key,这是给每次加载资源的唯一标示。 EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);

// 通过key查找缓存资源 (PS 这里的缓存主要是内存中的缓存,切记,可以查看MemoryCache) EngineResource<?> cached = loadFromCache(key, isMemoryCacheable); if (cached != null) { // 如果有,那么直接利用当前缓存的资源。 cb.onResourceReady(cached, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from cache", startTime, key); } return null; }

// 这是一个二级内存的缓存引用,很简单用了一个Map<Key, WeakReference<EngineResource>>装载起来的。 // 这个缓存主要是谁来放进去呢? 可以参考上面一级内存缓存loadFromCache方法。 EngineResource active = loadFromActiveResources(key, isMemoryCacheable); if (active != null) { cb.onResourceReady(active, DataSource.MEMORY_CACHE); if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Loaded resource from active resources", startTime, key); } return null; }

// 根据key获取缓存的job。 EngineJob current = jobs.get(key); if (current != null) { current.addCallback(cb); // 给当前job添加上回调Callback if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Added to existing load", startTime, key); } return new LoadStatus(cb, current); }

// 创建job EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable); DecodeJob decodeJob = decodeJobFactory.build( glideContext, model, key, signature, width, height, resourceClass, transcodeClass, priority, diskCacheStrategy, transformations, isTransformationRequired, options, engineJob); jobs.put(key, engineJob); engineJob.addCallback(cb); // 放入线程池,执行 engineJob.start(decodeJob);

if (Log.isLoggable(TAG, Log.VERBOSE)) { logWithTimeAndKey("Started new load", startTime, key); } return new LoadStatus(cb, engineJob); }

上面有一些值得注意的地方:

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

下面来看看DecodeJob是如何执行的:

private void runWrapped() { switch (runReason) { case INITIALIZE: // 初始化 获取下一个阶段状态 stage = getNextStage(Stage.INITIALIZE); currentGenerator = getNextGenerator(); // 运行 runGenerators(); break; case SWITCH_TO_SOURCE_SERVICE: runGenerators(); break; case DECODE_DATA: decodeFromRetrievedData(); break; default: throw new IllegalStateException("Unrecognized run reason: " + runReason); } }

// 这里的阶段策略首先是从resource中寻找,然后再是data,,再是source private Stage getNextStage(Stage current) { switch (current) { case INITIALIZE: // 根据定义的缓存策略来回去下一个状态 // 缓存策略来之于RequestBuilder的requestOptions域 // 如果你有自定义的策略,可以调用RequestBuilder.apply方法即可 // 详细的可用缓存策略请参看DiskCacheStrategy.java return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE); case RESOURCE_CACHE: return diskCacheStrategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE); case DATA_CACHE: return Stage.SOURCE; case SOURCE: case FINISHED: return Stage.FINISHED; default: throw new IllegalArgumentException("Unrecognized stage: " + current); }

// 根据Stage找到数据抓取生成器。 private DataFetcherGenerator getNextGenerator() { switch (stage) { case RESOURCE_CACHE: // 产生含有降低采样/转换资源数据缓存文件的DataFetcher。 return new ResourceCacheGenerator(decodeHelper, this); case DATA_CACHE: // 产生包含原始未修改的源数据缓存文件的DataFetcher。 return new DataCacheGenerator(decodeHelper, this); case SOURCE: // 生成使用注册的ModelLoader和加载时提供的Model获取源数据规定的DataFetcher。 // 根据不同的磁盘缓存策略,源数据可首先被写入到磁盘,然后从缓存文件中加载,而不是直接返回。 return new SourceGenerator(decodeHelper, this); case FINISHED: return null; default: throw new IllegalStateException("Unrecognized stage: " + stage); } }

经过很多流程,最后来到了发起实际请求的地方SourceGenerator.startNext()方法:

public boolean startNext() { if (dataToCache != null) { Object data = dataToCache; dataToCache = null; cacheData(data); }

if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) { return true; } sourceCacheGenerator = null;

loadData = null; boolean started = false; // 查找ModelLoader while (!started && hasNextModelLoader()) { loadData = helper.getLoadData().get(loadDataListIndex++); if (loadData != null && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource()) || helper.hasLoadPath(loadData.fetcher.getDataClass()))) { started = true; 根据model的fetcher加载数据 loadData.fetcher.loadData(helper.getPriority(), this); } } return started; }

这里的Model必须是实现了GlideModule接口的,fetcher是实现了DataFetcher接口。有兴趣同学可以继续看一下integration中的okhttp和volley工程。Glide主要采用了这两种网络libray来下载图片。

4.2.2 数据下载完成后的缓存处理SourceGenerator.onDataReady

public void onDataReady(Object data) { DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy(); if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) { dataToCache = data; // We might be being called back on someone else's thread. Before doing anything, we should // reschedule to get back onto Glide's thread. cb.reschedule(); } else { cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher, loadData.fetcher.getDataSource(), originalKey); } }

有些小伙伴可能看不太明白为什么就一个dataToCache = data就完了…其实cb.reschedule()很重要,这里的cb就是DecodeJob.reschedule():

public void reschedule() { runReason = RunReason.SWITCH_TO_SOURCE_SERVICE; callback.reschedule(this); }

这里又有一个Callback,继续追踪,这里的Callback接口是定义在DecodeJob内的,而实现是在外部的Engine中(这里会用线程池重新启动当前job,那为什么要这样做呢?源码中的解释是为了不同线程的切换,因为下载都是借用第三方网络库,而实际的编解码是在Glide自定义的线程池中进行的):

public void reschedule(DecodeJob<?> job) { if (isCancelled) { MAIN_THREAD_HANDLER.obtainMessage(MSG_CANCELLED, this).sendToTarget(); } else { sourceExecutor.execute(job); } }

接下来继续DecodeJob.runWrapped()方法。这个时候的runReason是SWITCH_TO_SOURCE_SERVICE,因此直接执行runGenerators(),这里继续执行SourceGenerator.startNext()方法,值得注意的dataToCache域,因为上一次执行的时候是下载,因此再次执行的时候内存缓存已经存在,因此直接缓存数据cacheData(data):

private void cacheData(Object dataToCache) { long startTime = LogTime.getLogTime(); try { // 根据不同的数据获取注册的不同Encoder Encoder encoder = helper.getSourceEncoder(dataToCache); DataCacheWriter writer = new DataCacheWriter<>(encoder, dataToCache, helper.getOptions()); originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature()); // 这里的DiskCache实现是Engine中LazyDiskCacheProvider提供的DiskCacheAdapter。 helper.getDiskCache().put(originalKey, writer); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Finished encoding source to cache"

  • ", key: " + originalKey
  • ", data: " + dataToCache
  • ", encoder: " + encoder
  • ", duration: " + LogTime.getElapsedMillis(startTime)); } } finally { loadData.fetcher.cleanup(); }

// 创建针对缓存的Generator sourceCacheGenerator = new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this); }

继续回到SourceGenerator.startNext()方法,这个时候已经有了sourceCacheGenerator,那么直接执行DataCacheGenerator.startNext()方法:

public boolean startNext() { while (modelLoaders == null || !hasNextModelLoader()) { sourceIdIndex++; if (sourceIdIndex >= cacheKeys.size()) { return false; }

Key sourceId = cacheKeys.get(sourceIdIndex); Key originalKey = new DataCacheKey(sourceId, helper.getSignature()); cacheFile = helper.getDiskCache().get(originalKey); if (cacheFile != null) { this.sourceKey = sourceId; modelLoaders = helper.getModelLoaders(cacheFile); modelLoaderIndex = 0; } }

loadData = null; boolean started = false; // 这里会通过model寻找注册过的ModelLoader while (!started && hasNextModelLoader()) {

最后如何让自己一步步成为技术专家

说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。

当你的技术在一步步贴近阿里p7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。

推荐一份Java架构之路必备的学习笔记,内容相当全面!!!

成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。

你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!

进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!

【关注】+【转发】+【点赞】支持我!创作不易!

相关阅读docs.qq.com/doc/DSmxTbFJ1cmN1R2dB