面试被问Glide总是懵逼,掌握这些技巧,真的不难啊。看看这些思维导图

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

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

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

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

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

通过工厂来自建了一个RequestManager,注释2处他直接使用了ApplicationLifecycle原因是因为某些情况下会接受不到生命周期的事件,这里是做的强制性的操作是为了生命周期变化时能够正常相应。

FragmentGet()

瞟了一下,这是要一个废弃的方法了,但是和supportFragmentGet()的方法相比其实也差不太多。

private RequestManager fragmentGet(
@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible); // 1 -->
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
// 将requestManager和Fragment相挂钩
// 用以完成生命周期的监听
current.setRequestManager(requestManager);
}
return requestManager;
}

// 1 -->
// 获取对应的Fragment
private RequestManagerFragment getRequestManagerFragment(
@NonNull final android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
// 寻找的方式是通过设置的TAG
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
// 先去等待队列中进行查询
// 这一步的作用是防止Fragment的重复添加
// 因为添加的Fragment的所谓的生命周期有一定的延时性
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
// 如果等待队列创建一个新的TAG
if (current == null) {
current = new RequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}

总结

  1. 初始化Glide的同时在内部完成了RequestManagerRetriever的创建
  2. 获取到的RequestManagerRetriever调用get()方法,获取到RequestManager,获取方式分为以下两种:
  • ContextApplication时, 通过getApplicationManager()方法创建RequestManager完成,将生命周期的监听与Application强制绑定用于接收。
  • Context不为Application时, 通过supportFragmentGet()方法创建RequestManager完成,生命周期的监听是与Fragment进行绑定实现。

创建对应TAG的一个非常直接的好处,我们的图片像RecyclerView会放置中不容易出现错位的现象。

load(ImageURL)

总体来说上面的就是一个初始化和必要变量获取的操作,那接下从函数方法来看我们似乎是要去获得的图片了呢。

public RequestBuilder load(@Nullable String string) {
return asDrawable().load(string); // 1 -->
}

注释1处,我们通过观察可以知道他最后会选择将获取的数据转化变成一个Drawable的类然后再在我们对应的ImageView上来进行显示。那我们就对asDrawable()先进行一段源码的分析。

// asDrawable()不断深入能发现调用到的函数
// 是完成一个类RequestBuilder的对象创建
public RequestBuilder as(
@NonNull Class resourceClass) { // Drawable.class
return new RequestBuilder<>(glide, this, resourceClass, context);
}
复制代码

那接下来的问题就要进入到这个类中,因为在前面我们的探索其实算是并没有什么收获的,而如果只是创建一个类显然是不会让这句话显得这么重要,那关键点一定会出现在这个类的构造中了。

protected RequestBuilder(
@NonNull Glide glide,
RequestManager requestManager,
Class transcodeClass,
Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.glideContext = glide.getGlideContext();

initRequestListeners(requestManager.getDefaultRequestListeners()); // 1 -->
// 这段代码过长,不做展示,它的主要任务就是一些策略开关
// 各种选项的开启装置,比如错误提示、优先级、磁盘缓存策略、固定宽高等等
apply(requestManager.getDefaultRequestOptions());
}

// 1–>
// 从某种意义上讲就是对生命周期的监听
private void initRequestListeners(List<RequestListener> requestListeners) {
for (RequestListener listener : requestListeners) {
addListener((RequestListener) listener);
}
}

而如果回到load(string)方法。

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

他最后的差事也就是将URI的值放到了model这个变量中,那整个load()函数作用其实最后只是创建了一个RequestBuilder的事例,那最后的获取和加载工作肯定是在into()函数中才进行了操作的。

into(ImageView)

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);

BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// 通过scaleType,对图片的属性进行设定
switch (view.getScaleType()) {
case CENTER_CROP:
// 。。。。。。
default:
// Do nothing.
}
}
// 正式将图片数据塞入
// 1 -->
return into(
// 2 -->
glideContext.buildImageViewTarget(view, transcodeClass), // 深度调用可以知道也就是将View进行了赋值
/targetListener=/ null,
requestOptions,
// 能够看到线程池的影子,后面的图片的获取和处理我们猜测就是通过池来进行处理
Executors.mainThreadExecutor());
}

// 1 --> 将数据塞入
private <Y extends Target> Y into(
@NonNull Y target,
@Nullable RequestListener targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
// 正常情况构建SingleRequest的请求
// 因为thumbnail一般需要额外的需求
Request request = buildRequest(target, targetListener, options, callbackExecutor);

Request previous = target.getRequest();
// 当前请求和最新的一样
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// 如果请求完成,重新启动会保证结果送达并触动目标
// 如果请求失败,会给出机会去再次完成
// 如果请求正在运行,不会打断
if (!Preconditions.checkNotNull(previous).isRunning()) {
previous.begin();
}
return target;
}

requestManager.clear(target);
target.setRequest(request); // 置换最新的请求
requestManager.track(target, request); // 3 -->

return target;
}

下面的内容将主要对上述代码中的注释2和注释3进行讲解。

glideContext.buildImageViewTarget(view, transcodeClass)

从字面意思,相比你也能够进行理解了,就是要构建一个存放的目标。

@NonNull
public ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass); // 1 -->
}

// 1–>
// 根据不同的数据类型选择存储是以Drawable还是Bitmap构建
public ViewTarget<ImageView, Z> buildTarget(
@NonNull ImageView view, @NonNull Class clazz) {
if (Bitmap.class.equals(clazz)) {
// 以Bitmap构建
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view); // 2–>
} else if (Drawable.class.isAssignableFrom(clazz)) {
// 以Drawable构建
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view); // 2 -->
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + “, try .as*(Class).transcode(ResourceTranscoder)”);
}
}

// 2–>
// 两个注释最后深度调用之后都会调用到这段代码
// 如果单看这段代码的时候其实
public ViewTarget(@NonNull T view) {
this.view = Preconditions.checkNotNull(view);
// 如果只看这个构造函数,确实没什么东西
// 不行你可以直接看注释3的代码处
sizeDeterminer = new SizeDeterminer(view); // 3 -->
}
// 非常简单的就只是对view进行了一个赋值操作
SizeDeterminer(@NonNull View view) {
this.view = view;
}

那如果就只有上面那么一点不就完了??其实并不,如果你观察了一下DrawableImageViewTargetBitmapImageViewTarget的其他方法,能发现这样的一个特征。

protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
}

protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}

没错!! 赋值操作,这个操作说明最后其实的结束点肯定是在这里的,而调用他的函数最后也就是onResourceReady()这个方法,也就意味着图片获取成功了,不过呢这个请求完成肯定是和数据的获取相互关联的,也就是下面部分的内容了。

requestManager.track(target, request)

// 以同步的方式完成数据的请求
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
targetTracker.track(target); // 对当前的目标的生命周期有一个追踪
requestTracker.runRequest(request); // 2 --> 执行操作正式开启
}
// 2 -->
public void runRequest(@NonNull Request request) {
requests.add(request);
// 会对当前的所有请求做一个判断处理
// 会根据当前的状态确定是否要进行数据加载的操作
// 一般来说对应的就是生命周期
if (!isPaused) {
request.begin();
} else {
request.clear();
pendingRequests.add(request);
}
}

那上面一段代码说明我们正常运行的时候,网络传输的操作肯定是已经在正常运行了的,而其实正常没有设置时调用的会是SingleRequest的类,不多逼逼,瞅瞅它的begin()方法有什么特殊之处了。

public void begin() {
synchronized (requestLock) {
// 。。。。。

// 如果正在运行就抛出异常
if (status == Status.RUNNING) {
throw new IllegalArgumentException(“Cannot restart a running request”);
}

// 从缓存中直接拿出数据
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// 重中之重
// 因为在上述文章中讲到过了图片的大小问题
// 在Glide中这里就是给出解决方案的地方,两种方案:
// 1. 给出了固定长宽
// 2. 没有设置时
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
// 使用一个占位符先顶替
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
}
}

那接下来要讲述的内容就应该是他的一些大小设置问题了,Glide到底是用什么样的方式完成大小的设置的呢?

onSizeReady(overrideWidth, overrideHeight)

public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
// …
status = Status.RUNNING;
// 对长宽重新进行预估
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

loadStatus =
engine.load(各种参数);
// …
}
}

通过观察对onSizeReady()函数发现,他使用的方案其实又是一个名叫做engine.load()的方式。

public LoadStatus load(各种参数) {

EngineResource<?> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

if (memoryResource == null) {
return waitForExistingOrStartNewJob(各种参数);
}
}
cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
return null;
}

上述是我省略过后的代码,他表达意思其实非常之简单:

(1)内存里有数据,你就从我内存里拿。

(2)内存里没数据,那你就自己创建一个。

这样想来,问题又要往下继续延伸了,正常来说我们没有数据啊,那就要去调用这个waitForExistingOrStartNewJob()方法,来完成图片数据的获取了。

private LoadStatus waitForExistingOrStartNewJob(各种参数) {
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
return new LoadStatus(cb, current);
}

EngineJob engineJob =
engineJobFactory.build(各种参数);

DecodeJob decodeJob =
decodeJobFactory.build(各种参数);

jobs.put(key, engineJob); // 对当前驱动工作进行缓存操作

engineJob.addCallback(cb, callbackExecutor);
engineJob.start(decodeJob); // 开启图片获取工作

return new LoadStatus(cb, engineJob);
}

能够注意到有出现两个新的类EngineJobDecodeJob,转换成中文去理解就是工作驱动器和解码工作,并且EngineJob内部与线程池搭噶,最后肯定用于完成最后的图片获取工作,而DecodeJob作为被运行的工作,处理逻辑就应该在其之中。

public void run() {
DataFetcher<?> localFetcher = currentFetcher;
try {
// …
runWrapped(); // 1 -->
} catch (Exception e) {
// …
} finally {
// …
}
}
// 1 -->
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
// 获取 Stage.INITIALIZE 的下一步操作,也就是选择三种方案
// 1. 资源缓存:ResourceCacheGenerator
// 2. 数据缓存:DataCacheGenerator
// 3. 网络资源:SourceGenerator
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);
}
}

其实上述内容中已经开始讲述到我们常见的三大缓存了,也就是网络缓存、磁盘缓存和内存缓存,这段代码中其实已经开始涉及网络缓存和磁盘缓存了。

SourceGenerator:关于网络缓存

private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) { // 1 -->
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
// 只对Source这个枚举类型相应
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// 已完成就通知失败
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
}
// 1–>
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;
while (!started && hasNextModelLoader()) {
// 从映射表找出能够对应上的图片类型的ModelLoader
loadData = helper.getLoadData().get(loadDataListIndex++);
if (loadData != null
&& (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
|| helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
started = true;
// 完成数据的加载
startNextLoad(loadData);
}
}
return started;
}

针对注释1进行探讨,这里的话,我们就尽量不放代码查看了,因为看了这么多代码,你肯定也累了。其实你能够猜测到它的下一步是网络请求,那如果获取成功了,是要直接进行图片数据的显示吗?那三次缓存应该在什么时机进行操作呢?因为代码量的原因,这里我们用图来展示流程。

从图中可以知道,其实从网络获取的资源最后还是要被放到磁盘中进行缓存的,而磁盘缓存成功之后,接下来要干的事情就是要去通知View把获取的数据进行解码。这里你是否有一定的疑问了?

文末

初级工程师拿到需求会直接开始做,然后做着做着发现有问题了,要么技术实现不了,要么逻辑有问题。

而高级工程师拿到需求会考虑很多,技术的可行性?对现有业务有没有帮助?对现有技术架构的影响?扩展性如何?等等…之后才会再进行设计编码阶段。

而现在随着跨平台开发,混合式开发,前端开发之类的热门,Android开发者需要学习和掌握的技术也在不断的增加。

通过和一些行业里的朋友交流讨论,以及参考现在大厂面试的要求。我们花了差不多一个月时间整理出了这份Android高级工程师需要掌握的所有知识体系。你可以看下掌握了多少。

混合式开发,微信小程序。都是得学会并且熟练的

这些是Android相关技术的内核,还有Java进阶

高级进阶必备的一些技术。像移动开发架构项目实战等

Android前沿技术;包括了组件化,热升级和热修复,以及各种架构跟框架的详细技术体系

以上即是我们整理的Android高级工程师需要掌握的技术体系了。可能很多朋友觉得很多技术自己都会了,只是一些新的技术不清楚而已。应该没什么太大的问题。

而这恰恰是问题所在!为什么别人高级工程师能年限突破30万,而你只有十几万呢?

就因为你只需补充你自己认为需要的,但并不知道企业需要的。这个就特别容易造成差距。因为你的技术体系并不系统,是零碎的,散乱的。那么你凭什么突破30万年薪呢?

我这些话比较直接,可能会戳到一些人的玻璃心,但是我知道肯定会对一些人起到点醒的效果的。而但凡只要有人因为我的这份高级系统大纲以及这些话找到了方向,并且付出行动去提升自我,为了成功变得更加努力。那么我做的这些就都有了意义。

喜欢的话请帮忙转发点赞一下能让更多有需要的人看到吧。谢谢!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

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

该没什么太大的问题。

而这恰恰是问题所在!为什么别人高级工程师能年限突破30万,而你只有十几万呢?

就因为你只需补充你自己认为需要的,但并不知道企业需要的。这个就特别容易造成差距。因为你的技术体系并不系统,是零碎的,散乱的。那么你凭什么突破30万年薪呢?

我这些话比较直接,可能会戳到一些人的玻璃心,但是我知道肯定会对一些人起到点醒的效果的。而但凡只要有人因为我的这份高级系统大纲以及这些话找到了方向,并且付出行动去提升自我,为了成功变得更加努力。那么我做的这些就都有了意义。

喜欢的话请帮忙转发点赞一下能让更多有需要的人看到吧。谢谢!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-621vGe3F-1713390603004)]

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

  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值