RequestOptions requestOptions = new RequestOptions()
.override(200,100)
.diskCacheStrategy(DiskCacheStrategy.NONE);
Glide.with(MainActivity.this)
.load(url)
.thumbnail( Glide.with(this)
.load(url)
.apply(requestOptions))
.into(iv);
//Generated API 方式
GlideApp.with(Context)
.load(url)
.thumbnail( GlideApp.with(this)
.load(IMAGE_URL).override(200,100)
.diskCacheStrategy(DiskCacheStrategy.NONE))
.into(mImageView);
//********************************** 设置图片的变化操作 **********************************
//可通过
//CenterCrop(图片原图的中心区域进行裁剪显示)
//FitCenter(图片原始长宽铺满)
//CircleCrop(圆形裁剪)
//设置图片的变化操作
Glide.with(Context)
.load(IMAGE_URL)
.apply(RequestOptions.circleCropTransform())
.into(mImageView);
//Generated API 方式
GlideApp.with(Context).load(IMAGE_URL)
.circleCrop()
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(mImageView);
4. Glide包含哪些模块?都干什么用的?
接下来的环节将是本篇文章的高地。
我们可以先简单思考下,如果你是Glide的作者,你要怎么设计一个优秀的图片加载框架,需要兼顾哪些环节,需要包含哪些模块?
简单来说,图片加载需要以下几种模块。
-
参数封装
-
加载请求
-
执行引擎
-
数据加载器
-
解码器
-
编码器
-
缓存
那实际Glide是这样设计的么?
-
参数封装(获得请求的参数,配置加载图的属性)
-
网络请求(下载图片)
-
缓存逻辑(资源重用)
-
加解码(处理图片)
-
数据加载(判断加载方式/请求网络/IO读取/内存读取)
-
线程池/线程队列(或者叫任务队列,用于处理每次的加载任务)
-
防止OOM的处理机制(软引用,缓存,压缩,存储等…)
-
生命周期管理(防止内存泄露)
-
…
说了这么多,那加载一张图片,到底是怎样一个流程呢?换句话说,这些模块具体是怎样协同工作的,调用顺序又是怎样的呢?
给大家画了一个简单的图,兄弟们,这个图来之不易啊,你不从头捋一遍你真是不知道咋画啊…
好多小伙伴可能看到这个图,又懵逼了, 你这都啥跟啥啊,一个Glide你画这么几个块就over了?
那网络咋加载的?那缓存怎么处理的,那线程咋切换的?那生命周期咋控制的。啥也没说啊…
各位,各位,先别着急。咱们力争的是先 整体再局部,先总览再细分。接下来咱们针对代码进行跟进。
兄台准备好面对疾风了么?
5. Glide调用某一个方法后具体干了什么事?
首先我们拿一个简单的使用进行跟进
Glide.with(context).load(“”).into(mImageView)
链式调用,我们分解为以下三个方法进行分析。
Glide.with(context)
Glide.with(context).load(“”)
Glide.with(context).load(“”).into(mImageView)
5.1. Glide.with(context)具体干了啥?
死亡如风,常伴吾身。我会给你个痛快的!
/**
-
简单来说该方法进行了以下几步操作
-
- Glide 初始化
-
- 各种线程池初始化,各种缓存对象初始化
-
- Engine初始化,至于Engine是啥,后续我们慢慢介绍
-
- RequestManager 初始化
-
- 创建透明的fragment,动态监听生命周期
*/
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// 检测传入的context是否合规
Preconditions.checkNotNull(context, “You cannot start …”);
return Glide.get(context).getRequestManagerRetriever();
}
public static Glide get(@NonNull Context context) {
if (glide == null) {
GeneratedAppGlideModule annotationGeneratedModule = getAnnotationGeneratedGlideModules(context.getApplicationContext());
synchronized (Glide.class) {
if (glide == null) {
//如果glide为null则启动检测初始化逻辑
checkAndInitializeGlide(context, annotationGeneratedModule);
}
}
}
return glide;
}
private static void checkAndInitializeGlide(@NonNull Context context, @Nullable GeneratedAppGlideModule generatedAppGlideModule) {
// 如果已经在初始化中了,则抛异常。
if (isInitializing) {
throw new IllegalStateException( “You cannot call Glide.get() in registerComponents(), use the provided Glide instance instead”);
}
//设置初始化状态
isInitializing = true;
initializeGlide(context, generatedAppGlideModule);
isInitializing = false;
}
private static void initializeGlide(
@NonNull Context context,
@NonNull GlideBuilder builder,
@Nullable GeneratedAppGlideModule annotationGeneratedModule) {
Context applicationContext = context.getApplicationContext();
…
//调用GlideBuilder.build()方法进行Glide初始化
Glide glide = builder.build(applicationContext);
…
//注册回调
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
//GlideBuilder.build()方法,用于初始化各种线程池,以及缓存对象,初始化Glide
Glide build(@NonNull Context context) {
//创建资源池
if (sourceExecutor == null) {
sourceExecutor = GlideExecutor.newSourceExecutor();
}
//硬盘缓存池
if (diskCacheExecutor == null) {
diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
}
if (animationExecutor == null) {
animationExecutor = GlideExecutor.newAnimationExecutor();
}
if (memorySizeCalculator == null) {
memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
}
if (connectivityMonitorFactory == null) {
connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
}
if (bitmapPool == null) {
int size = memorySizeCalculator.getBitmapPoolSize();
if (size > 0) {
bitmapPool = new LruBitmapPool(size);
} else {
bitmapPool = new BitmapPoolAdapter();
}
}
if (arrayPool == null) {
arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
}
if (memoryCache == null) {
memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
}
if (diskCacheFactory == null) {
diskCacheFactory = new InternalCacheDiskCacheFactory(context);
}
if (engine == null) {
//初始化Engine
engine = new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
animationExecutor,
isActiveResourceRetentionAllowed);
}
if (defaultRequestListeners == null) {
defaultRequestListeners = Collections.emptyList();
} else {
defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
}
RequestManagerRetriever requestManagerRetriever = new RequestManagerRetriever(requestManagerFactory);
//初始化Glide
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptionsFactory,
defaultTransitionOptions,
defaultRequestListeners,
isLoggingRequestOriginsEnabled,
isImageDecoderEnabledForBitmaps,
hardwareBitmapFdLimit,
minHardwareDimension);
}
//RequestManagerRetriever.get()通过Glide.with(Context)入参获取FragmentManager
public RequestManager get(@NonNull FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
//通过Android版本>17,并且页面已经isDestroyed则抛出异常
assertNotDestroyed(activity);
//通过入参获取FragmentManager
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm, /parentHint=/ null, isActivityVisible(activity));
}
}
//RequestManagerRetriever.supportFragmentGet()
private RequestManager supportFragmentGet(
@NonNull Context context,
@NonNull FragmentManager fm,
@Nullable Fragment parentHint,
boolean isParentVisible) {
//获取请求fragment
SupportRequestManagerFragment current =getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
//获取fragment中RequestManager 对象,如果fragment是刚刚创建的requestManager 为null
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
//创建requestManager 并设置给fragment
requestManager =
factory.build( glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
private SupportRequestManagerFragment getSupportRequestManagerFragment(
@NonNull final FragmentManager fm, @Nullable Fragment parentHint, boolean isParentVisible) {
//先查找这个tag的fragment有没有被创建
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
//从pendingSupportRequestManagerFragments中在查找一次
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
//如果都为null则new创建
current = new SupportRequestManagerFragment();
current.setParentFragmentHint(parentHint);
if (isParentVisible) {
current.getGlideLifecycle().onStart();
}
//存储
pendingSupportRequestManagerFragments.put(fm, current);
//添加到FragmentManager 中
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
//发送通知
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
兄弟们,咱回回神,经过这一通暴讲,我们终于把 with() 这个方法分析完成了。
是不是贼拉牛逼。现在对with()方法的流程以及生命周期的控制管理是不是稍稍有那么一些印象了。无法复述没有关系。先能顺下来,当你第二遍看到时候就会事半功倍了。
写了这么一堆,我们回顾下这个with()干了啥?
1. 调用getRetriever(activity).get(activity)
2. 调用checkAndInitializeGlide(context, annotationGeneratedModule)检测并启动初始化
3. 调用initializeGlide(context, generatedAppGlideModule);连带调用GlideBuilder.build(applicationContext)进行Glide初始化
4. Glide初始化过程中会创建各种线程池,各种缓存对象,并初始化Engine对象。
5. 调用RequestManagerRetriever.get(activity)返回RequestManager对象,同时通过入参的activity.getSupportFragmentManager()获取FragmentManager对象。
6.调用supportFragmentGet(activity, fm, null, isActivityVisible(activity))创建RequestManager和SupportRequestManagerFragment并做关联。
这个地方需要注意下,可能调用的是fragmentGet()或getApplicationManager(),最终目的都是 创建RequestManager。
主线程下调用各个对象之间的对应关系为
1个Activity<–>1个FragmentManager<–>1个RequestManagerFragment<–> 1个RequestManager。
子线程调用会传入ApplicationContext,此时使用的是全局的单例RequestManager,生命周期和App一致。
扩展个知识点,具体Glide是怎么依赖生命周期动态控制加载图片的呢?
SupportRequestManagerFragment对象中有个属性为ActivityFragmentLifecycle对象,并且在SupportRequestManagerFragment的生命周期方法中均调用了ActivityFragmentLifecycle的对应方法。
@Override
public void onStart() {
super.onStart();
lifecycle.onStart();
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}
然后通过LifecycleListener调用对应的方法
void onStart() {
isStarted = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStart();
}
}
void onStop() {
isStarted = false;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onStop();
}
}
void onDestroy() {
isDestroyed = true;
for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
lifecycleListener.onDestroy();
}
}
我们再看都哪些类实现了LifecycleListener接口呢?其中有RequestManager,TargetTracker和Target(继承)实现了该接口,我们抽出RequestManager单独看下。
RequestManager
@Override
public synchronized void onStart() {
resumeRequests();
targetTracker.onStart();
}
/**
-
Lifecycle callback that unregisters for connectivity events (if the
-
android.permission.ACCESS_NETWORK_STATE permission is present) and pauses in progress loads.
*/
@Override
public synchronized void onStop() {
pauseRequests();
targetTracker.onStop();
}
/**
-
Lifecycle callback that cancels all in progress requests and clears and recycles resources for
-
all completed requests.
*/
@Override
public synchronized void onDestroy() {
targetTracker.onDestroy();
for (Target<?> target : targetTracker.getAll()) {
clear(target);
}
targetTracker.clear();
requestTracker.clearRequests();
lifecycle.removeListener(this);
lifecycle.removeListener(connectivityMonitor);
mainHandler.removeCallbacks(addSelfToLifecycle);
glide.unregisterRequestManager(this);
}
public synchronized void resumeRequests() {
requestTracker.resumeRequests();
}
public void pauseRequests() {
isPaused = true;
for (Request request : Util.getSnapshot(requests)) {
//如果正在执行过程中
if (request.isRunning()) {
// Avoid clearing parts of requests that may have completed (thumbnails) to avoid blinking
// in the UI, while still making sure that any in progress parts of requests are immediately
// stopped.
//调用暂停
request.pause();
//添加到挂起的请求中。
pendingRequests.add(request);
}
}
}
public void clear(@Nullable final Target<?> target) {
if (target == null) {
return;
}
untrackOrDelegate(target);
}
private void untrackOrDelegate(@NonNull Target<?> target) {
boolean isOwnedByUs = untrack(target);
// We’ll end up here if the Target was cleared after the RequestManager that started the request
// is destroyed. That can happen for at least two reasons:
// 1. We call clear() on a background thread using something other than Application Context
// RequestManager.
// 2. The caller retains a reference to the RequestManager after the corresponding Activity or
// Fragment is destroyed, starts a load with it, and then clears that load with a different
// RequestManager. Callers seem especially likely to do this in retained Fragments (#2262).
//
// #1 is always an error. At best the caller is leaking memory briefly in something like an
// AsyncTask. At worst the caller is leaking an Activity or Fragment for a sustained period of
// time if they do something like reference the Activity RequestManager in a long lived
// background thread or task.
//
// #2 is always an error. Callers shouldn’t be starting new loads using RequestManagers after
// the corresponding Activity or Fragment is destroyed because retaining any reference to the
// RequestManager leaks memory. It’s possible that there’s some brief period of time during or
// immediately after onDestroy where this is reasonable, but I can’t think of why.
Request request = target.getRequest();
if (!isOwnedByUs && !glide.removeFromManagers(target) && request != null) {
//移除请求调用清理。
target.setRequest(null);
request.clear();
}
}
看到以上及其简短的注释,估计你是明白咋回事了。
本质原理就是 SupportRequestManagerFragment是通过传入的Context创建的。可以感知onStart(), onStop(), onDestroy() 等方法。
在这些方法中调用 ActivityFragmentLifecycle 中对应的方法,然后 循环 找到 ActivityFragmentLifecycle中与之对应的RequestManager对象,再调用 对应生命周期 的方法。
最后通过requestTracker循环找到对应的Request对象,然后调用对应的处理方法从而达到了根据生命周期动态控制图片加载的目的。
好了,我就不多赘述了。我还得抓紧赶路,争取一气呵成。
5.2. Glide.with(context).load(“”)具体干了啥?
长路漫漫 惟剑做伴 吾之荣耀 离别已久 宁日安在 无人能云 且随疾风前行
Glide.with(context)已经返回了RequestManager,因此Glide.with(context).load(“”)相当于RequestManager.load(“”);
public RequestBuilder load(@Nullable String string) {
return asDrawable().load(string);
}
//注意返回值为RequestBuilder
public RequestBuilder asDrawable() {
return as(Drawable.class);
}
public RequestBuilder as(
@NonNull Class resourceClass) {
//初始化RequestBuilder对象
return new RequestBuilder<>(glide, this, resourceClass, context);
}
//RequestBuilder的构造方法
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();
//初始化RequestListeners
initRequestListeners(requestManager.getDefaultRequestListeners());
apply(requestManager.getDefaultRequestOptions());
}
private void initRequestListeners(List<RequestListener> requestListeners) {
for (RequestListener listener : requestListeners) {
addListener((RequestListener) listener);
}
}
public RequestBuilder apply(@NonNull BaseRequestOptions<?> requestOptions) {
Preconditions.checkNotNull(requestOptions);
return super.apply(requestOptions);
}
上文中的源码我们只是分析load()方法的第一步,首先分析了load()中asDrawable()方法以及一系列的连带方法。
逻辑非常简单,相对比较有代表性的是初始化了RequestBuilder对象。
我们继续分析RequestBuilder.load()
public RequestBuilder load(@Nullable String string) {
return loadGeneric(string);
}
private RequestBuilder loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
诶?这就完了?咋稍稍有点不适应呢?就只设置了下model和isModelSet属性就完事了?
看来是的,load()就是这么简单,需要注意的一点是model通过Object接收的。
5.3. Glide.with(context).load(“”).into(mImageView)具体干了啥?
回首往昔 更进一步 吾虽浪迹天涯 却从未迷失本心。最后一个方法了,黎明就在眼前
Glide.with(context).load(“”)的返回值为RequestBuilder,因此无亚于RequestBuilder.into(mImageView);
// 注意返回值为ViewTarget
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
//判断是否为主线程,不是主线程则抛异常
Util.assertMainThread();
//判空逻辑
Preconditions.checkNotNull(view);
//这个requestOptions 是啥现在还看不出来,好像是存放图片展示的相关属性的。
BaseRequestOptions<?> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// 在此方法中克隆,以便如果使用此RequestBuilder加载到视图中,然后加载到其他目标中,
//则不会保留基于上一个视图的缩放类型应用的转换。
//该switch 主要用于控制调用Glide时设置的各种样式,没设置则为默认的
switch (view.getScaleType()) {
case CENTER_CROP:
requestOptions = requestOptions.clone().optionalCenterCrop();
break;
case CENTER_INSIDE:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
requestOptions = requestOptions.clone().optionalFitCenter();
break;
case FIT_XY:
requestOptions = requestOptions.clone().optionalCenterInside();
break;
case CENTER:
case MATRIX:
default:
// Do nothing.
}
}
return into(
//注意这个transcodeClass在上述4.2中得知transcodeClass为Drawable.class
glideContext.buildImageViewTarget(view, transcodeClass),
/targetListener=/ null,
requestOptions,
Executors.mainThreadExecutor());
}
//
public ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
//由于为Drawable.class因此本方法将返回DrawableImageViewTarget对象。
public ViewTarget<ImageView, Z> buildTarget(
@NonNull ImageView view, @NonNull Class clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget<ImageView, Z>) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget<ImageView, Z>) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + “, try .as*(Class).transcode(ResourceTranscoder)”);
}
}
简单来说上述的的一串逻辑可以理解为
a. 调用into()后首先判断是否为主线程
b. 通过view.getScaleType()判断使用哪个requestOptions对象,如果没设置则使用默认的。
c. 调用glideContext.buildImageViewTarget(view, transcodeClass)返回DrawableImageViewTarget对象。
ok,我们继续看into方法,现在的into可以理解为
into(DrawableImageViewTarget,null,requestOptions,Executors.mainThreadExecutor());
private <Y extends Target> Y into(
@NonNull Y target,
@Nullable RequestListener targetListener,
BaseRequestOptions<?> options,
Executor callbackExecutor) {
//还是检查逻辑
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException(“You must call #load() before calling #into()”);
}
//创建Request对象,看面相 这个应该还是用于请求的对象,我们得看看咋创建的
Request request = buildRequest(target, targetListener, options, callbackExecutor);
//此处的target为DrawableImageViewTarget
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);
return target;
}
private Request buildRequest(
Target target,
@Nullable RequestListener targetListener,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
return buildRequestRecursive(
/requestLock=/ new Object(),
target,
targetListener,
/parentCoordinator=/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions,
callbackExecutor);
}
//此时的target为DrawableImageViewTarget,targetListener为null
private Request buildRequestRecursive(
Object requestLock,
Target target,
@Nullable RequestListener targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions<?> requestOptions,
Executor callbackExecutor) {
// 如果需要,请先构建ErrorRequestCoordinator,以便我们可以更新parentCoordinator。
//但是因为我们没有设置error,因此errorBuilder 为null
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
//我们后续详细看下这个mainRequest创建逻辑
Request mainRequest = buildThumbnailRequestRecursive(
requestLock,
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
//因为errorBuilder 为null因此上述的errorRequestCoordinator也为null,此处直接返回mainRequest了
if (errorRequestCoordinator == null) {
return mainRequest;
}
int errorOverrideWidth = errorBuilder.getOverrideWidth();
int errorOverrideHeight = errorBuilder.getOverrideHeight();
if (Util.isValidDimensions(overrideWidth, overrideHeight) && !errorBuilder.isValidOverride()) {
errorOverrideWidth = requestOptions.getOverrideWidth();
errorOverrideHeight = requestOptions.getOverrideHeight();
}
Request errorRequest = errorBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
errorRequestCoordinator,
errorBuilder.transitionOptions,
errorBuilder.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
buildThumbnailRequestRecursive()方法的逻辑比较长,我们单独拿出来去分析,但是其实我们并没有设置thumbnail(),所以thumbnailBuilder和thumbSizeMultiplier 是都null,只会走最后一个else。
我贴下整体的逻辑代码,不要惊慌,这一大片代码,只为最后创建个SingleRequest。
private Request buildThumbnailRequestRecursive(
Object requestLock,
Target target,
RequestListener targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
最后
说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
der.getPriority(),
errorOverrideWidth,
errorOverrideHeight,
errorBuilder,
callbackExecutor);
errorRequestCoordinator.setRequests(mainRequest, errorRequest);
return errorRequestCoordinator;
}
buildThumbnailRequestRecursive()方法的逻辑比较长,我们单独拿出来去分析,但是其实我们并没有设置thumbnail(),所以thumbnailBuilder和thumbSizeMultiplier 是都null,只会走最后一个else。
我贴下整体的逻辑代码,不要惊慌,这一大片代码,只为最后创建个SingleRequest。
private Request buildThumbnailRequestRecursive(
Object requestLock,
Target target,
RequestListener targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
最后
说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
[外链图片转存中…(img-S787XOEU-1714640973145)]
[外链图片转存中…(img-UbwnfUM3-1714640973146)]
[外链图片转存中…(img-XdcMugJn-1714640973146)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!