Glide源码精编解析(腾讯面试参考答案)(1),android游戏开发前景

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

// 创建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);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

总结

这次面试问的还是还是有难度的,要求当场写代码并且运行,也是很考察面试者写代码
因为Android知识体系比较庞大和复杂的,涉及到计算机知识领域的方方面面。在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
id高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。**

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-4aYHBVke-1712636739582)]
里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-sHG7sfQm-1712636739582)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值