前言:
Glide 源码下载
Glide.with(this).load(url).into(imageView);
这一行代码都干了什么事。
从 Glide 最基本的三步走用法:先with(),再load(),最后into()顺序来分析。
1. wiht()
with() 方法是 Glide 类中的一组静态方法,它有好几个方法重载,我们来看一下 Glide 类中所有 with() 方法的方法重载:
public class Glide {
...
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull FragmentActivity activity) {
return getRetriever(activity).get(activity);
}
@NonNull
public static RequestManager with(@NonNull Fragment fragment) {
return getRetriever(fragment.getContext()).get(fragment);
}
@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static RequestManager with(@NonNull android.app.Fragment fragment) {
return getRetriever(fragment.getActivity()).get(fragment);
}
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
}
可以看到,with() 方法的重载种类非常多,既可以传入 Activity,也可以传入 Fragment 或者是 Context。
看最后那个 wiht() 方法,wiht() 方法还有一个传入 View 对象的方法。
其实也很好理解,wiht() 方法也可以传一个绑定了 Activity 或者 Fragment 的 View 对象。然后根据 View 获取 Activity 或者 Fragment,最后还是和传 Activity 或者 Fragment 的 wiht() 方法一样。
下面来看一下 * getRetriever()* 方法:
private final RequestManagerRetriever requestManagerRetriever;
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// 检查 上下文是否为空,为空报空指针,具体代码可看下面 checkNotNull 方法
Preconditions.checkNotNull(
context,
"You cannot start a load on a not yet attached View or a Fragment where getActivity() "
+ "returns null (which usually occurs when getActivity() is called before the Fragment "
+ "is attached or after the Fragment is destroyed).");
return Glide.get(context).getRequestManagerRetriever();
}
@NonNull
public static T checkNotNull(@Nullable T arg, @NonNull String message) {
if (arg == null) {
throw new NullPointerException(message);
}
return arg;
}
/** Internal method. */
@NonNull
public RequestManagerRetriever getRequestManagerRetriever() {
return requestManagerRetriever;
}
每一个 with() 方法重载的代码都非常简单,都是先调用 RequestManagerRetriever
的静态 getRetriever 方法得到一个 RequestManagerRetriever 对象,这个静态 get() 方法就是一个单例实现,没什么需要解释的。然后再调用 RequestManagerRetriever 的实例 get() 方法,去获取 RequestManager 对象。
接下来,我们以传入Application 为例,看一下 RequestManagerRetriever 的实例 get(context) 方法代码:
@NonNull
public RequestManager get(@NonNull Context context) {
// 判空,否则抛出相应的参数错误
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
// 根据不同的上下文类型选择不同 get 方法
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper
// Only unwrap a ContextWrapper if the baseContext has a non-null application context.
// Context#createPackageContext may return a Context without an Application instance,
// in which case a ContextWrapper may be used to attach one.
&& ((ContextWrapper) context).getBaseContext().getApplicationContext() != null) {
return get(((ContextWrapper) context).getBaseContext());
}
}
// 我们是 context 走这个方法
return getApplicationManager(context);
}
// 获取一个RequestManager对象
@NonNull
private RequestManager getApplicationManager(@NonNull Context context) {
// Either an application context or we're on a background thread.
if (applicationManager == null) {
synchronized (this) {
if (applicationManager == null) {
// Normally pause/resume is taken care of by the fragment we add to the fragment or
// activity. However, in this case since the manager attached to the application will not
// receive lifecycle events, we must force the manager to start resumed using
// ApplicationLifecycle.
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context.getApplicationContext());
applicationManager =
factory.build(
glide,
new ApplicationLifecycle(),
new EmptyRequestManagerTreeNode(),
context.getApplicationContext());
}
}
}
return applicationManager;
}
调用 getApplicationManager() 方法来获取一个 RequestManager 对象。其实这是最简单的一种情况,因为 Application 对象的生命周期即应用程序的生命周期,因此 Glide 并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide 的加载也会同时终止。
接下来我们看传入非 Application 参数的情况。
public class RequestManagerRetriever implements Handler.Callback {
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
private volatile RequestManager applicationManager;
...
/**
* Retrieves and returns the RequestManagerRetriever singleton.
*/
public static RequestManagerRetriever get() {
return INSTANCE;
}
public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context");
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
public RequestManager get(FragmentActivity activity) {
// 如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
public RequestManager get(Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
}
// 如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm);
}
}
@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);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static void assertNotDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
throw new IllegalArgumentException("You cannot start a load for a destroyed activity");
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public RequestManager get(android.app.Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached");
}
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return get(fragment.getActivity().getApplicationContext());
} else {
android.app.FragmentManager fm = fragment.getChildFragmentManager();
return fragmentGet(fragment.getActivity(), fm);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm) {
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingRequestManagerFragments.get(fm);
if (current == null) {
current = new RequestManagerFragment();
pendingRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, android.app.FragmentManager fm) {
RequestManagerFragment current = getRequestManagerFragment(fm);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
current.setRequestManager(requestManager);
}
return requestManager;
}
SupportRequestManagerFragment getSupportRequestManagerFragment(final FragmentManager fm) {
SupportRequestManagerFragment current = (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
if (current == null) {
current = pendingSupportRequestManagerFragments.get(fm);
if (current == null) {
current = new SupportRequestManagerFragment();
pendingSupportRequestManagerFragments.put(fm, current);
fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER, fm).sendToTarget();
}
}
return current;
}
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;
}
...
}
可以看到,不管你在 Glide.with() 方法中传入的是 Activity、FragmentActivity、v4 包下的 Fragment、还是 app 包下的 Fragment,最终的流程都是一样的,那就是会向当前的 Activity 当中添加一个隐藏的 Fragment。具体添加的逻辑是在上述代码的中 fragmentGet() 方法,分别对应的 app 包和 v4 包下的两种 Fragment 的情况。
那么这里为什么要添加一个隐藏的 Fragment 呢?
因为 Glide 需要知道加载的生命周期。
很简单的一个道理,如果你在某个 Activity 上正在加载着一张图片,结果图片还没加载出来,Activity 就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是 Glide 并没有办法知道 Activity 的生命周期,于是 Glide 就使用了添加隐藏 Fragment 的这种小技巧,因为 Fragment 的生命周期和 Activity 是同步的,如果 Activity 被销毁了,Fragment 是可以监听到的,这样 Glide 就可以捕获这个事件并停止图片加载了。
这里额外再提一句,从注解,如果我们是在非主线程当中使用的 Glide,那么不管你是传入的 Activity 还是 Fragment,都会被强制当成 Application 来处理。不过其实这就属于是在分析代码的细节了,本篇文章我们将会把目光主要放在 Glide 的主线工作流程上面,后面不会过多去分析这些细节方面的内容。
总体来说,第一个 with() 方法的源码还是比较好理解的。其实就是为了得到一个 RequestManager 对象而已,然后 Glide 会根据我们传入 with() 方法的参数来确定图片加载的生命周期,并没有什么特别复杂的逻辑。
2. load()
由于 with() 方法返回的是一个 RequestManager 对象,那么很容易就能想到,load() 方法是在 RequestManager 类当中的,所以说我们首先要看的就是 RequestManager 这个类里面 load()。
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable Bitmap bitmap) {
return asDrawable().load(bitmap);
}
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable Drawable drawable) {
return asDrawable().load(drawable);
}
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable String string) {
return asDrawable().load(string);
}
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable Uri uri) {
return asDrawable().load(uri);
}
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable File file) {
return asDrawable().load(file);
}
@SuppressWarnings("deprecation")
@NonNull
@CheckResult
@Override
public RequestBuilder load(@RawRes @DrawableRes @Nullable Integer resourceId) {
return asDrawable().load(resourceId);
}
@SuppressWarnings("deprecation")
@CheckResult
@Override
@Deprecated
public RequestBuilder load(@Nullable URL url) {
return asDrawable().load(url);
}
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable byte[] model) {
return asDrawable().load(model);
}
@NonNull
@CheckResult
@Override
public RequestBuilder load(@Nullable Object model) {
return asDrawable().load(model);
}
Glide 是支持图片 URL 字符串、图片本地路径等等加载形式的,因此 RequestManager 中也有很多个 load() 方法的重载。但是这里我们不可能把每个 load() 方法的重载都看一遍,因此我们就只选其中一个加载图片 URL 字符串的 load() 方法来进行研究吧。
看一下 asDrawable() 方法:
@NonNull
@CheckResult
public RequestBuilder asDrawable() {
return as(Drawable.class);
}
@NonNull
@CheckResult
public RequestBuilder as(
@NonNull Class resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
最后我们可以看到,asDrawable() 方法是要返回一个RequestBuilder
接下来再看一下 RequestBuilder 里面怎么执行 load() 方法的:
public class RequestBuilder extends BaseRequestOptions>
implements Cloneable, ModelTypes> {
private final Context context;
private final RequestManager requestManager;
private final Class transcodeClass;
private final Glide glide;
...
@NonNull
@Override
@CheckResult
public RequestBuilder load(@Nullable String string) {
return loadGeneric(string);
}
@NonNull
private RequestBuilder loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
...
}
到这里,第二步 load() 方法也就分析结束了。为什么呢?
因为你会发现 RequestBuilder 类中最终 load() 方法返回的其实就是一个 RequestBuilder 对象。那么接下来我们就要进行第三步了,分析into() 方法中的逻辑。
3. into()
从上面可以看出,load() 方法最终返回的是 RequestBuilder 对象,那么,into() 方法是在 RequestBuilder 类里面的一个方法,现在看代码:
public class RequestBuilder extends BaseRequestOptions>
implements Cloneable, ModelTypes> {
...
private final Context context;
private final RequestManager requestManager;
private final Class transcodeClass;
private final Glide glide;
private final GlideContext glideContext;
...
/**
* Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into
* the view, and frees any resources Glide may have previously loaded into the view so they may be
* reused.
*
* @see RequestManager#clear(Target)
* @param view The view to cancel previous loads for and load the new resource into.
* @return The {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link
* ImageView}.
*/
@NonNull
public ViewTarget into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
BaseRequestOptions> requestOptions = this;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
// Clone in this method so that if we use this RequestBuilder to load into a View and then
// into a different target, we don't retain the transformation applied based on the previous
// View's scale type.
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(
glideContext.buildImageViewTarget(view, transcodeClass),
/*targetListener=*/ null,
requestOptions,
Executors.mainThreadExecutor());
}
}
这里前面一大堆的判断逻辑我们都可以先不用管,等到后面文章讲 transform 的时候会再进行解释,现在我们只需要关注最后一行代码。最后一行代码先是调用了glideContext.buildImageViewTarget() 方法,这个方法会构建出一个 ViewTarget 对象,ViewTarget 对象则是用来最终展示图片用的,如果我们跟进去的话会看到如下代码:
@NonNull
public ViewTarget buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
这里其实又是调用了ImageViewTargetFactory 的 buildTarget() 方法,我们继续跟进去,代码如下所示:
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public ViewTarget buildTarget(
@NonNull ImageView view, @NonNull Class clazz) {
if (Bitmap.class.equals(clazz)) {
return (ViewTarget) new BitmapImageViewTarget(view);
} else if (Drawable.class.isAssignableFrom(clazz)) {
return (ViewTarget) new DrawableImageViewTarget(view);
} else {
throw new IllegalArgumentException(
"Unhandled class: " + clazz + ", try .as*(Class).transcode(ResourceTranscoder)");
}
}
}
可以看到,在 buildTarget() 方法中会根据传入的 class 参数来构建不同的 ViewTarget 对象。那如果你要分析这个 class 参数是从哪儿传过来的。
简单起见我直接帮大家梳理清楚。这个 class 参数其实基本上只有两种情况,如果你在使用 Glide 加载图片的时候调用了 asBitmap() 方法,那么这里就会构建出 BitmapImageViewTarget 对象,否则的话构建的都是 DrawableImageViewTarget 对象,这个通常都是用不到的,我们可以暂时不用管它。
也就是说,通过 glideContext.buildImageViewTarget() 方法,我们构建出了一个 GlideDrawableImageViewTarget 对象。那现在回到刚才 into() 方法的最后一行,可以看到,这里又将这个参数传入到了GenericRequestBuilder 另一个接收 ViewTarget 对象的 into() 方法当中了。我们来看一下这个 into() 方法的源码:
private > 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()");
}
// 注释1
Request request = buildRequest(target, targetListener, options, callbackExecutor);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
// If the request is completed, beginning again will ensure the result is re-delivered,
// triggering RequestListeners and Targets. If the request is failed, beginning again will
// restart the request, giving it another chance to complete. If the request is already
// running, we can let it continue running without interruption.
if (!Preconditions.checkNotNull(previous).isRunning()) {
// Use the previous request rather than the new one to allow for optimizations like skipping
// setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
// that are done in the individual Request.
previous.begin();
}
return target;
}
requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);
return target;
}
synchronized void track(@NonNull Target> target, @NonNull Request request) {
targetTracker.track(target);
// 注释2
requestTracker.runRequest(request);
}
这里我们还是只抓核心代码,其实只有两行是最关键的。
注释 1 调用 buildRequest() 方法构建出了一个 Request 对象,注释 2 来执行这个Request。
先看注释 1 ,Request 是用来发出加载图片请求的,它是 Glide 中非常关键的一个组件。我们先来看 buildRequest() 方法是如何构建 Request 对象的:
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);
}
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) {
// Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
ErrorRequestCoordinator errorRequestCoordinator = null;
if (errorBuilder != null) {
errorRequestCoordinator = new ErrorRequestCoordinator(requestLock, parentCoordinator);
parentCoordinator = errorRequestCoordinator;
}
Request mainRequest =
buildThumbnailRequestRecursive(
requestLock,
target,
targetListener,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
requestOptions,
callbackExecutor);
...
}
可以看到,buildRequest() 方法的内部其实又调用了 buildRequestRecursive() 方法,而最终走到了 buildThumbnailRequestRecursive() 方法。代码如下:
private Request buildThumbnailRequestRecursive(
Object requestLock,
Target target,
RequestListener targetListener,
@Nullable RequestCoordinator parentCoordinator,
TransitionOptions, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight,
BaseRequestOptions> requestOptions,
Executor callbackExecutor) {
if (thumbnailBuilder != null) {
···
Request fullRequest =
obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest =
thumbnailBuilder.buildRequestRecursive(
requestLock,
target,
targetListener,
coordinator,
thumbTransitionOptions,
thumbPriority,
thumbOverrideWidth,
thumbOverrideHeight,
thumbnailBuilder,
callbackExecutor);
isThumbnailBuilt = false;
coordinator.setRequests(fullRequest, thumbRequest);
return coordinator;
} else if (thumbSizeMultiplier != null) {
// Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
ThumbnailRequestCoordinator coordinator =
new ThumbnailRequestCoordinator(requestLock, parentCoordinator);
Request fullRequest =
obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
coordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
BaseRequestOptions> thumbnailOptions =
requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);
Request thumbnailRequest =
obtainRequest(
requestLock,
target,
targetListener,
thumbnailOptions,
coordinator,
transitionOptions,
getThumbnailPriority(priority),
overrideWidth,
overrideHeight,
callbackExecutor);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(
requestLock,
target,
targetListener,
requestOptions,
parentCoordinator,
transitionOptions,
priority,
overrideWidth,
overrideHeight,
callbackExecutor);
}
}
除去设置缩略图的一些代码之后,任何情况都会走到 RequestBuilder 的 obtainRequest 方法构建一个SingleRequest。
private Request obtainRequest(
Target target,
RequestListener targetListener,
RequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight) {
return SingleRequest.obtain(
context,
glideContext,
//String
model,
//Drawable.class
transcodeClass,
//options
requestOptions,
//宽高
overrideWidth,
overrideHeight,
priority,
//DrawableImageViewTarget
target,
targetListener,
requestListeners,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
接着来看注释 2:
// 注释 2 处代码
requestManager.track(target, request);
// RequestManager 里面的 track
synchronized void track(@NonNull Target> target, @NonNull Request request) {
targetTracker.track(target);
requestTracker.runRequest(request);
}
/** Starts tracking the given request. */
public void runRequest(@NonNull Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
request.clear();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
从代码可以看到,方法 * track* 最终走到 runRequest,从名字可以看出,这个方法去执行请求的方法,果然,request.begin();
。从上面分析知道,request 这个是 SingleRequest。所以来看 SingleRequest 类里面的 begin 方法:
public void begin() {
......
if (status == Status.COMPLETE) {
//已加载完成 回调
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
status = Status.WAITING_FOR_SIZE;
//测量一下ImageView的大小
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());
}
}
测量一下 ImageView 的大小,如果在调用 Glide 加载图片时设置 override 设置宽高,那么直接会走 onSizeReady 方法处。如果没有写,那么走 target.getSize(this);
测量一下宽高,然后在里面还是会调用 SingleRequest 的 onSizeReady() 方法。
/** A callback method that should never be invoked directly. */
@Override
public void onSizeReady(int width, int height) {
stateVerifier.throwIfRecycled();
synchronized (requestLock) {
if (IS_VERBOSE_LOGGABLE) {
logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
if (status != Status.WAITING_FOR_SIZE) {
return;
}
status = Status.RUNNING;
float sizeMultiplier = requestOptions.getSizeMultiplier();
this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
if (IS_VERBOSE_LOGGABLE) {
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.isScaleOnlyOrNoTransform(),
requestOptions.getOptions(),
requestOptions.isMemoryCacheable(),
requestOptions.getUseUnlimitedSourceGeneratorsPool(),
requestOptions.getUseAnimationPool(),
requestOptions.getOnlyRetrieveFromCache(),
this,
callbackExecutor);
// This is a hack that's only useful for testing right now where loads complete synchronously
// even though under any executor running on any thread but the main thread, the load would
// have completed asynchronously.
if (status != Status.RUNNING) {
loadStatus = null;
}
if (IS_VERBOSE_LOGGABLE) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
}
开始用引擎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,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor) {
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key =
keyFactory.buildKey(
model,
signature,
width,
height,
transformations,
resourceClass,
transcodeClass,
options);
//从弱引用中找,有就直接返回
EngineResource> memoryResource;
synchronized (this) {
memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);
if (memoryResource == null) {
return waitForExistingOrStartNewJob(
glideContext,
model,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
options,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache,
cb,
callbackExecutor,
key,
startTime);
}
}
先去缓存去找,如果没有就去走 * waitForExistingOrStartNewJob* 方法,代码如下:
private LoadStatus waitForExistingOrStartNewJob(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class> resourceClass,
Class transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map, Transformation>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb,
Executor callbackExecutor,
EngineKey key,
long startTime) {
EngineJob> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb, callbackExecutor);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
// engineJob 是为 decodeJob 干事情的,管理下载过程以及状态
EngineJob engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
// 注册ResourceCallback
engineJob.addCallback(cb, callbackExecutor);
// 关键代码
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
创建 DecodeJob 并且调用 start 方法开始工作:
class EngineJob implements DecodeJob.Callback, Poolable {
···
public synchronized void start(DecodeJob decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor =
decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
executor.execute(decodeJob);
}
···
}
我们这里暂时不考虑缓存的情况,那么使用的这里使用的 GlideExecutor 是 GlideBuilder 的 build 方法中初始化的 sourceExecutor = GlideExecutor.newSourceExecutor(); 其实就是一个线程池。 然后 DecodeJob 是 Runnable,放到线程池中去执行,所以我们知道找重点,去 DecodeJob 的 run 方法看:
class DecodeJob
implements DataFetcherGenerator.FetcherReadyCallback,
Runnable,
Comparable>,
Poolable {
···
// We need to rethrow only CallbackException, but not other types of Throwables.
@SuppressWarnings("PMD.AvoidRethrowingException")
@Override
public void run() {
// This should be much more fine grained, but since Java's thread pool implementation silently
// swallows all otherwise fatal exceptions, this will at least make it obvious to developers
// that something is failing.
GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);
// Methods in the try statement can invalidate currentFetcher, so set a local variable here to
// ensure that the fetcher is cleaned up either way.
DataFetcher> localFetcher = currentFetcher;
try {
if (isCancelled) {
notifyFailed();
return;
}
// 关键代码
runWrapped();
} catch (CallbackException e) {
// If a callback not controlled by Glide throws an exception, we should avoid the Glide
// specific debug logic below.
throw e;
} catch (Throwable t) {
// Catch Throwable and not Exception to handle OOMs. Throwables are swallowed by our
// usage of .submit() in GlideExecutor so we're not silently hiding crashes by doing this. We
// are however ensuring that our callbacks are always notified when a load fails. Without this
// notification, uncaught throwables never notify the corresponding callbacks, which can cause
// loads to silently hang forever, a case that's especially bad for users using Futures on
// background threads.
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(
TAG,
"DecodeJob threw unexpectedly" + ", isCancelled: " + isCancelled + ", stage: " + stage,
t);
}
// When we're encoding we've already notified our callback and it isn't safe to do so again.
if (stage != Stage.ENCODE) {
throwables.add(t);
notifyFailed();
}
if (!isCancelled) {
throw t;
}
throw t;
} finally {
// Keeping track of the fetcher here and calling cleanup is excessively paranoid, we call
// close in all cases anyway.
if (localFetcher != null) {
localFetcher.cleanup();
}
GlideTrace.endSection();
}
}
private void runWrapped() {
switch (runReason) {
// runReason 在 DecodeJob 初始化的时候初始值是 INITIALIZE,上面没有带大家看,这不是重点
case INITIALIZE:
// 获取下一个 state
stage = getNextStage(Stage.INITIALIZE);
// 这里会去获取一个 SourceGenerator
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
// 一次完整的请求,会把这里的都走完
private DataFetcherGenerator getNextGenerator() {
switch (stage) {
case RESOURCE_CACHE:
return new ResourceCacheGenerator(decodeHelper, this);
case DATA_CACHE:
return new DataCacheGenerator(decodeHelper, this);
case SOURCE:
//会走到这里
return new SourceGenerator(decodeHelper, this);
case FINISHED:
return null;
default:
throw new IllegalStateException("Unrecognized stage: " + stage);
}
}
private void runGenerators() {
currentThread = Thread.currentThread();
startFetchTime = LogTime.getLogTime();
boolean isStarted = false;
while (!isCancelled
&& currentGenerator != null
&& !(isStarted = currentGenerator.startNext())) {
stage = getNextStage(stage);
currentGenerator = getNextGenerator();
if (stage == Stage.SOURCE) {
reschedule();
return;
}
}
// We've run out of stages and generators, give up.
if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
notifyFailed();
}
// Otherwise a generator started a new load and we expect to be called back in
// onDataFetcherReady.
}
}
/** Where we're trying to decode data from. */
private enum Stage {
/** The initial stage. */
INITIALIZE,
/** Decode from a cached resource. */
RESOURCE_CACHE,
/** Decode from cached source data. */
DATA_CACHE,
/** Decode from retrieved source. */
SOURCE,
/** Encoding transformed resources after a successful load. */
ENCODE,
/** No more viable stages. */
FINISHED,
}
Glide 定义了几个步骤,在枚举类 Stage 里面。
这是每一步的状态,当执行完一个状态的时候,又去获取下一个状态进行执行相应的逻辑。
在 runGenerators 方法里面有一个 while
控制,我们第一步是SourceGenerator,所以会走到 SourceGenerator 的 startNext() 里面。
@Override
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()) {
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;
}
private void startNextLoad(final LoadData> toStart) {
// 这里的fetcher是HttpUrlFetcher
// ⚠️ 一会请求成功之后,还会回来分析 onDataReady
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback
主流程是根据 HttpUrlFetcher 去加载数据,这里就不分析为啥 HttpUrlFetcher,直接来看 HttpUrlFetcher 的 loadData 方法:
// HttpUrlFetcher.java
public void loadData(@NonNull Priority priority,
@NonNull DataCallback super InputStream> callback) {
try {
// 看情况,这个方法是获取到了网络数据了
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
// 将数据返回回去
callback.onDataReady(result);
} catch (IOException e) {
callback.onLoadFailed(e);
} finally {
}
}
private InputStream loadDataWithRedirects(
URL url, int redirects, URL lastUrl, Map headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
// Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352.
stream = urlConnection.getInputStream();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (isHttpRedirect(statusCode)) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
// Closing the stream specifically is required to avoid leaking ResponseBodys in addition
// to disconnecting the url connection below. See #2352.
cleanup();
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == INVALID_STATUS_CODE) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
可以看到 loadData 方法直接跳了 loadDataWithRedirects 方法,咱们只看请求成功的情况,请求不成功还是会继续调用 loadDataWithRedirects 方法。成功的话,跳到了 getStreamForSuccessfulRequest 方法:
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
这个就比较熟悉了,就是通过 HttpURLConnection 进行的网络请求,并且结果就是通过 urlConnection.getInputStream(); 拿到的。
这里拿到了结果后就返回了,然后就是上面的 loadData方法里面的callback.onDataReady(result);
进行回调,callback就是之前说的 SourceGenerator, 我们回到有 ⚠️ 标志的地方,不想返回去看代码的,直接看如下代码:
private void startNextLoad(final LoadData> toStart) {
loadData.fetcher.loadData(
helper.getPriority(),
new DataCallback
获取成功之后,走到 onDataReady
回调里面,这里走到了 * onDataReadyInternal* 方法里面的 cb.onDataFetcherReady,cb 是之前提到的DecodeJob 类,现在来看 DecodeJob 里面的 onDataFetcherReady 方法:
@Override
public void onDataFetcherReady(
Key sourceKey, Object data, DataFetcher> fetcher, DataSource dataSource, Key attemptedKey) {
this.currentSourceKey = sourceKey;
this.currentData = data;
this.currentFetcher = fetcher;
this.currentDataSource = dataSource;
this.currentAttemptingKey = attemptedKey;
if (Thread.currentThread() != currentThread) {
runReason = RunReason.DECODE_DATA;
callback.reschedule(this);
} else {
GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
try {
// 上面的都不看了,会走到这里来
decodeFromRetrievedData();
} finally {
GlideTrace.endSection();
}
}
}
private void decodeFromRetrievedData() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey(
"Retrieved data",
startFetchTime,
"data: "
+ currentData
+ ", cache key: "
+ currentSourceKey
+ ", fetcher: "
+ currentFetcher);
}
Resource resource = null;
try {
//⚠️1:从数据中解码得到资源
resource = decodeFromData(currentFetcher, currentData, currentDataSource);
} catch (GlideException e) {
e.setLoggingDetails(currentAttemptingKey, currentDataSource);
throwables.add(e);
}
if (resource != null) {
//⚠️2
notifyEncodeAndRelease(resource, currentDataSource);
} else {
runGenerators();
}
}
拿到数据之后,到了重点⚠️1处,开始从数据中解码:
private Resource decodeFromData(
DataFetcher> fetcher, Data data, DataSource dataSource) throws GlideException {
try {
if (data == null) {
return null;
}
long startTime = LogTime.getLogTime();
// 这里又把解码包了一下
Resource result = decodeFromFetcher(data, dataSource);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Decoded result " + result, startTime);
}
return result;
} finally {
fetcher.cleanup();
}
}
@SuppressWarnings("unchecked")
private Resource decodeFromFetcher(Data data, DataSource dataSource)
throws GlideException {
LoadPath path = decodeHelper.getLoadPath((Class) data.getClass());
// 将解码交给 LoadPath 对象去搞
return runLoadPath(data, dataSource, path);
}
private Resource runLoadPath(
Data data, DataSource dataSource, LoadPath path)
throws GlideException {
Options options = getOptionsWithHardwareConfig(dataSource);
// 将数据又包装了一下
DataRewinder rewinder = glideContext.getRegistry().getRewinder(data);
try {
// ResourceType in DecodeCallback below is required for compilation to work with gradle.
// 在 LoadPath 对象中 load 解码
return path.load(
rewinder, options, width, height, new DecodeCallback(dataSource));
} finally {
rewinder.cleanup();
}
}
可以看到,包装多次之后,最后将数据交给LoadPath对象去解码:
// LoadPath
public Resource load(
DataRewinder rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback decodeCallback)
throws GlideException {
List throwables = Preconditions.checkNotNull(listPool.acquire());
try {
// 核心代码
return loadWithExceptionList(rewinder, options, width, height, decodeCallback, throwables);
} finally {
listPool.release(throwables);
}
}
private Resource loadWithExceptionList(
DataRewinder rewinder,
@NonNull Options options,
int width,
int height,
DecodePath.DecodeCallback decodeCallback,
List exceptions)
throws GlideException {
Resource result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decodePaths.size(); i < size; i++) {
DecodePath path = decodePaths.get(i);
try {
// 将解码任务又转给了 DecodePath 去做
result = path.decode(rewinder, width, height, options, decodeCallback);
} catch (GlideException e) {
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
又转到 DecodePath 手中去解码,代码如下:
public Resource decode(
DataRewinder rewinder,
int width,
int height,
@NonNull Options options,
DecodeCallback callback)
throws GlideException {
Resource decoded = decodeResource(rewinder, width, height, options);
Resource transformed = callback.onResourceDecoded(decoded);
return transcoder.transcode(transformed, options);
}
@NonNull
private Resource decodeResource(
DataRewinder rewinder, int width, int height, @NonNull Options options)
throws GlideException {
List exceptions = Preconditions.checkNotNull(listPool.acquire());
try {
return decodeResourceWithList(rewinder, width, height, options, exceptions);
} finally {
listPool.release(exceptions);
}
}
@NonNull
private Resource decodeResourceWithList(
DataRewinder rewinder,
int width,
int height,
@NonNull Options options,
List exceptions)
throws GlideException {
Resource result = null;
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = decoders.size(); i < size; i++) {
ResourceDecoder decoder = decoders.get(i);
try {
DataType data = rewinder.rewindAndGet();
if (decoder.handles(data, options)) {
data = rewinder.rewindAndGet();
// 根据 DataType,ResourceType 区别,分发给不同的解码器
result = decoder.decode(data, width, height, options);
}
// Some decoders throw unexpectedly. If they do, we shouldn't fail the entire load path, but
// instead log and continue. See #2406 for an example.
} catch (IOException | RuntimeException | OutOfMemoryError e) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Failed to decode data for " + decoder, e);
}
exceptions.add(e);
}
if (result != null) {
break;
}
}
if (result == null) {
throw new GlideException(failureMessage, new ArrayList<>(exceptions));
}
return result;
}
上面的 decoder 其实是 StreamBitmapDecoder,在初始化 Glide 的时候就初始化好了,一直放那里没用它,来看 StreamBitmapDecoder 的 decode 方法:
// StreamBitmapDecoder
@Override
public Resource decode(
@NonNull InputStream source, int width, int height, @NonNull Options options)
throws IOException {
// Use to fix the mark limit to avoid allocating buffers that fit entire images.
final RecyclableBufferedInputStream bufferedStream;
final boolean ownsBufferedStream;
if (source instanceof RecyclableBufferedInputStream) {
bufferedStream = (RecyclableBufferedInputStream) source;
ownsBufferedStream = false;
} else {
bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
ownsBufferedStream = true;
}
// Use to retrieve exceptions thrown while reading.
// TODO(#126): when the framework no longer returns partially decoded Bitmaps or provides a
// way to determine if a Bitmap is partially decoded, consider removing.
ExceptionCatchingInputStream exceptionStream =
ExceptionCatchingInputStream.obtain(bufferedStream);
// Use to read data.
// Ensures that we can always reset after reading an image header so that we can still
// attempt to decode the full image even when the header decode fails and/or overflows our read
// buffer. See #283.
MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
try {
// 这里又将解码任务交给了 Downsampler
return downsampler.decode(invalidatingStream, width, height, options, callbacks);
} finally {
exceptionStream.release();
if (ownsBufferedStream) {
bufferedStream.release();
}
}
}
// Downsampler
public Resource decode(
InputStream is,
int requestedWidth,
int requestedHeight,
Options options,
DecodeCallbacks callbacks)
throws IOException {
Preconditions.checkArgument(
is.markSupported(), "You must provide an InputStream that supports" + " mark()");
byte[] bytesForOptions = byteArrayPool.get(ArrayPool.STANDARD_BUFFER_SIZE_BYTES, byte[].class);
BitmapFactory.Options bitmapFactoryOptions = getDefaultOptions();
bitmapFactoryOptions.inTempStorage = bytesForOptions;
DecodeFormat decodeFormat = options.get(DECODE_FORMAT);
PreferredColorSpace preferredColorSpace = options.get(PREFERRED_COLOR_SPACE);
DownsampleStrategy downsampleStrategy = options.get(DownsampleStrategy.OPTION);
boolean fixBitmapToRequestedDimensions = options.get(FIX_BITMAP_SIZE_TO_REQUESTED_DIMENSIONS);
boolean isHardwareConfigAllowed =
options.get(ALLOW_HARDWARE_CONFIG) != null && options.get(ALLOW_HARDWARE_CONFIG);
try {
Bitmap result =
decodeFromWrappedStreams(
is,
bitmapFactoryOptions,
downsampleStrategy,
decodeFormat,
preferredColorSpace,
isHardwareConfigAllowed,
requestedWidth,
requestedHeight,
fixBitmapToRequestedDimensions,
callbacks);
// 解码得到 Bitmap 对象后,包装成 BitmapResource 对象返回,
// 通过内部的 get 方法得到 Bitmap 对象
return BitmapResource.obtain(result, bitmapPool);
} finally {
releaseOptions(bitmapFactoryOptions);
byteArrayPool.put(bytesForOptions);
}
}
解码任务又转到了 Downsampler:
private Bitmap decodeFromWrappedStreams(
InputStream is,
BitmapFactory.Options options,
DownsampleStrategy downsampleStrategy,
DecodeFormat decodeFormat,
PreferredColorSpace preferredColorSpace,
boolean isHardwareConfigAllowed,
int requestedWidth,
int requestedHeight,
boolean fixBitmapToRequestedDimensions,
DecodeCallbacks callbacks)
throws IOException {
long startTime = LogTime.getLogTime();
int[] sourceDimensions = getDimensions(is, options, callbacks, bitmapPool);
int sourceWidth = sourceDimensions[0];
int sourceHeight = sourceDimensions[1];
String sourceMimeType = options.outMimeType;
// If we failed to obtain the image dimensions, we may end up with an incorrectly sized Bitmap,
// so we want to use a mutable Bitmap type. One way this can happen is if the image header is so
// large (10mb+) that our attempt to use inJustDecodeBounds fails and we're forced to decode the
// full size image.
if (sourceWidth == -1 || sourceHeight == -1) {
isHardwareConfigAllowed = false;
}
int orientation = ImageHeaderParserUtils.getOrientation(parsers, is, byteArrayPool);
int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
boolean isExifOrientationRequired = TransformationUtils.isExifOrientationRequired(orientation);
int targetWidth =
requestedWidth == Target.SIZE_ORIGINAL
? (isRotationRequired(degreesToRotate) ? sourceHeight : sourceWidth)
: requestedWidth;
int targetHeight =
requestedHeight == Target.SIZE_ORIGINAL
? (isRotationRequired(degreesToRotate) ? sourceWidth : sourceHeight)
: requestedHeight;
ImageType imageType = ImageHeaderParserUtils.getType(parsers, is, byteArrayPool);
calculateScaling(
imageType,
is,
callbacks,
bitmapPool,
downsampleStrategy,
degreesToRotate,
sourceWidth,
sourceHeight,
targetWidth,
targetHeight,
options);
calculateConfig(
is,
decodeFormat,
isHardwareConfigAllowed,
isExifOrientationRequired,
options,
targetWidth,
targetHeight);
boolean isKitKatOrGreater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
if ((options.inSampleSize == 1 || isKitKatOrGreater) && shouldUsePool(imageType)) {
int expectedWidth;
int expectedHeight;
if (sourceWidth >= 0
&& sourceHeight >= 0
&& fixBitmapToRequestedDimensions
&& isKitKatOrGreater) {
expectedWidth = targetWidth;
expectedHeight = targetHeight;
} else {
float densityMultiplier =
isScaling(options) ? (float) options.inTargetDensity / options.inDensity : 1f;
int sampleSize = options.inSampleSize;
int downsampledWidth = (int) Math.ceil(sourceWidth / (float) sampleSize);
int downsampledHeight = (int) Math.ceil(sourceHeight / (float) sampleSize);
expectedWidth = Math.round(downsampledWidth * densityMultiplier);
expectedHeight = Math.round(downsampledHeight * densityMultiplier);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(
TAG,
"Calculated target ["
+ expectedWidth
+ "x"
+ expectedHeight
+ "] for source"
+ " ["
+ sourceWidth
+ "x"
+ sourceHeight
+ "]"
+ ", sampleSize: "
+ sampleSize
+ ", targetDensity: "
+ options.inTargetDensity
+ ", density: "
+ options.inDensity
+ ", density multiplier: "
+ densityMultiplier);
}
}
// If this isn't an image, or BitmapFactory was unable to parse the size, width and height
// will be -1 here.
if (expectedWidth > 0 && expectedHeight > 0) {
setInBitmap(options, bitmapPool, expectedWidth, expectedHeight);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
boolean isP3Eligible =
preferredColorSpace == PreferredColorSpace.DISPLAY_P3
&& options.outColorSpace != null
&& options.outColorSpace.isWideGamut();
options.inPreferredColorSpace =
ColorSpace.get(isP3Eligible ? ColorSpace.Named.DISPLAY_P3 : ColorSpace.Named.SRGB);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
options.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
}
// 通过 BitmapFactory.decodeStream 搞到 Bitmap
Bitmap downsampled = decodeStream(is, options, callbacks, bitmapPool);
callbacks.onDecodeComplete(bitmapPool, downsampled);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logDecode(
sourceWidth,
sourceHeight,
sourceMimeType,
options,
downsampled,
requestedWidth,
requestedHeight,
startTime);
}
Bitmap rotated = null;
if (downsampled != null) {
// If we scaled, the Bitmap density will be our inTargetDensity. Here we correct it back to
// the expected density dpi.
downsampled.setDensity(displayMetrics.densityDpi);
rotated = TransformationUtils.rotateImageExif(bitmapPool, downsampled, orientation);
if (!downsampled.equals(rotated)) {
bitmapPool.put(downsampled);
}
}
return rotated;
}
Glide 在拿到 InputStream 之后各种包装,各种转手,终于还是压缩处理之后通过 BitmapFactory.decodeStream 生成出 Bitmap。
最后这里是交给了 Downsampler 在处理,它的 decode 方法是返回到了 DecodeJob 的 run 方法,然后使用了 notifyEncodeAndRelease() 方法对 Resource 对象进行了回调:
// DecodeJob
private void notifyEncodeAndRelease(Resource resource, DataSource dataSource) {
if (resource instanceof Initializable) {
((Initializable) resource).initialize();
}
Resource result = resource;
LockedResource lockedResource = null;
if (deferredEncodeManager.hasResourceToEncode()) {
lockedResource = LockedResource.obtain(resource);
result = lockedResource;
}
notifyComplete(result, dataSource);
stage = Stage.ENCODE;
try {
if (deferredEncodeManager.hasResourceToEncode()) {
deferredEncodeManager.encode(diskCacheProvider, options);
}
} finally {
if (lockedResource != null) {
lockedResource.unlock();
}
}
// Call onEncodeComplete outside the finally block so that it's not called if the encode process
// throws.
onEncodeComplete();
}
private void notifyComplete(Resource resource, DataSource dataSource) {
setNotifiedOrThrow();
// 这里的 callback 其实是 EngineJob,在初始化 DecodeJob 的时候传入的
callback.onResourceReady(resource, dataSource);
}
开始通过 DecodeJob 进行回调,然后又传到了EngineJob:
//EngineJob.java
private static final Handler MAIN_THREAD_HANDLER =
new Handler(Looper.getMainLooper(), new MainThreadCallback());
public void onResourceReady(Resource resource, DataSource dataSource) {
this.resource = resource;
this.dataSource = dataSource;
MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
}
//MainThreadCallback#handleMessage
public boolean handleMessage(Message message) {
EngineJob> job = (EngineJob>) message.obj;
switch (message.what) {
case MSG_COMPLETE:
//-> 这里
job.handleResultOnMainThread();
break;
case MSG_EXCEPTION:
job.handleExceptionOnMainThread();
break;
case MSG_CANCELLED:
job.handleCancelledOnMainThread();
break;
default:
throw new IllegalStateException("Unrecognized message: " + message.what);
}
return true;
}
void handleResultOnMainThread() {
stateVerifier.throwIfRecycled();
if (isCancelled) {
resource.recycle();
release(false /*isRemovedFromQueue*/);
return;
} else if (cbs.isEmpty()) {
throw new IllegalStateException("Received a resource without any callbacks to notify");
} else if (hasResource) {
throw new IllegalStateException("Already have resource");
}
engineResource = engineResourceFactory.build(resource, isCacheable);
hasResource = true;
// Hold on to resource for duration of request so we don't recycle it in the middle of
// notifying if it synchronously released by one of the callbacks.
engineResource.acquire();
listener.onEngineJobComplete(this, key, engineResource);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = cbs.size(); i < size; i++) {
ResourceCallback cb = cbs.get(i);
if (!isInIgnoredCallbacks(cb)) {
engineResource.acquire();
//关键代码 这里的cb是SingleRequest
cb.onResourceReady(engineResource, dataSource);
}
}
// Our request is complete, so we can release the resource.
engineResource.release();
release(false /*isRemovedFromQueue*/);
}
onResourceReady 回调传回来的时候,通过 handler 切换到主线程,然后通过 SingleRequest 的 onRrsourceReady 方法将数据传递回去:
public void onResourceReady(Resource> resource, DataSource dataSource) {
stateVerifier.throwIfRecycled();
loadStatus = null;
if (resource == null) {
GlideException exception = new GlideException("Expected to receive a Resource with an "
+ "object of " + transcodeClass + " inside, but instead got null.");
onLoadFailed(exception);
return;
}
Object received = resource.get();
if (received == null || !transcodeClass.isAssignableFrom(received.getClass())) {
releaseResource(resource);
GlideException exception = new GlideException("Expected to receive an object of "
+ transcodeClass + " but instead" + " got "
+ (received != null ? received.getClass() : "") + "{" + received + "} inside" + " "
+ "Resource{" + resource + "}."
+ (received != null ? "" : " " + "To indicate failure return a null Resource "
+ "object, rather than a Resource object containing null data."));
onLoadFailed(exception);
return;
}
if (!canSetResource()) {
releaseResource(resource);
// We can't put the status to complete before asking canSetResource().
status = Status.COMPLETE;
return;
}
onResourceReady((Resource) resource, (R) received, dataSource);
}
private void onResourceReady(Resource resource, R result, DataSource dataSource) {
// We must call isFirstReadyResource before setting status.
boolean isFirstResource = isFirstReadyResource();
status = Status.COMPLETE;
this.resource = resource;
isCallingCallbacks = true;
try {
boolean anyListenerHandledUpdatingTarget = false;
if (requestListeners != null) {
for (RequestListener listener : requestListeners) {
anyListenerHandledUpdatingTarget |=
listener.onResourceReady(result, model, target, dataSource, isFirstResource);
}
}
anyListenerHandledUpdatingTarget |=
targetListener != null
&& targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
if (!anyListenerHandledUpdatingTarget) {
Transition super R> animation =
animationFactory.build(dataSource, isFirstResource);
//这里的target是初始化SingleRequest时传入的DrawableImageViewTarget
target.onResourceReady(result, animation);
}
} finally {
isCallingCallbacks = false;
}
notifyLoadSuccess();
}
在 SingleRequest 中做了一些判断,然后将数据传递给DrawableImageViewTarget 去回调处理:
//下面2个方法是DrawableImageViewTarget的父类ImageViewTarget中的
public void onResourceReady(@NonNull Z resource, @Nullable Transition super Z> transition) {
if (transition == null || !transition.transition(resource, this)) {
setResourceInternal(resource);
} else {
maybeUpdateAnimatable(resource);
}
}
private void setResourceInternal(@Nullable Z resource) {
// Order matters here. Set the resource first to make sure that the Drawable has a valid and
// non-null Callback before starting it.
setResource(resource);
maybeUpdateAnimatable(resource);
}
//DrawableImageViewTarget.java
protected void setResource(@Nullable Drawable resource) {
view.setImageDrawable(resource);
}
总结
这一条线已经是最简单的一条线了,中间很多细节都没有去分析,还有很多代码没有贴出来。看代码之前就像开始的图片,好奇心很重,看完之后的心情就像最后那张照片,每条线很清晰,但是,细节很多。
参考链接
点我跳到郭神原文
点我跳到潇风寒月
申明:开始结束的图片来源网络,侵删