Glide源码解析
在项目中用到了Glide来加载网络图片,于是想写篇文章来分析下Glide的源码,但是Glide里面的源码非常多,下面主要是从面试的角度来把一些重要的点提一下。
在理解glide的源码前,首先看下它怎么用的, 最常见的glide的用法很简单就是下面的这句代码
Glide.with(MainActivity.this).load(url).into(img1)
这篇文章就来分析一下,这句代码的实现原理。
with
首先,查看下 with的源码
/**
* Begin a load with Glide that will tied to the give {@link android.support.v4.app.FragmentActivity}'s lifecycle
* and that uses the given {@link android.support.v4.app.FragmentActivity}'s default options.
*
* @param activity The activity to use.
* @return A RequestManager for the given FragmentActivity that can be used to start a load.
*/
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
就是返回一个 RequestManagerRetriever 对象,这个RequestManagerRetriever 对象是用来图片加载请求的管理类。它继承于LifeCycleListener,这个是用来管理生命周期,而这个生命周期其实是和我们的activity,fragment所绑定的。这样我们才能把我们的reqeust绑定到我们的activity当中。对于请求我们可以甚至可以做暂停和取消操作。RequestManagerRetriever里这个类的主要作用是将我们的activity和我们的request绑定。
Glide的特点就是能够绑定到组件当中,比如我们自定义的fragment,activity等,通过组件的生命周期来进行请求的处理。这个是它的一个非常重要的特性。
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
进入get方法,我们可以看到,调用了Util.isOnBackgroundThread()方法,这个方法里其实就是判断是否在主线程里面的一个方法,里面是通过Android里面的Looper类来进行判断。也就是这里主要判断如果非主线程的话,会通过全局的context来获得requestmanager,如果是在主线程中的话,则会进入assertNotDestroyed方法。
@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");
}
}
这个assertNotDestroyed其实就是判断如果activity被销毁就抛出一个IllegalArgumentException。在glide里面RequestManagerRetriever 都是通过与activity生命周期做绑定的,如果一个actiivty被销毁的话,那么接下来的代码肯定是没必要去执行的。
没被销毁的activity返回一个FragmentManager ,然后通过这个FragmentManger返回一个RequestManager。我们进入RequestManagerRetriever.supportFragmentGet方法里面看一下源码。
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;
}
SupportRequestManagerFragment 这个类是一个封装的fragment对象,在supportFragmentGet方法的第二步里面创建一个新的requestmanager对象,并且在构造函数里面传入lifecycle,这个跟requestmanager是相互关联的。最后返回一个requestManager对象。
这个requestManager类里面具体是怎么实现的,我们进入源码看一下
public class RequestManager implements LifecycleListener {
private final Context context;
private final Lifecycle lifecycle;
private final RequestManagerTreeNode treeNode;
private final RequestTracker requestTracker;
private final Glide glide;
private final OptionsApplier optionsApplier;
private DefaultOptions options;
public RequestManager(Context context, Lifecycle lifecycle, RequestManagerTreeNode treeNode) {
this(context, lifecycle, treeNode, new RequestTracker(), new ConnectivityMonitorFactory());
}
RequestManager(Context context, final Lifecycle lifecycle, RequestManagerTreeNode treeNode,
RequestTracker requestTracker, ConnectivityMonitorFactory factory) {
this.context = context.getApplicationContext();
this.lifecycle = lifecycle;
this.treeNode = treeNode;
this.requestTracker = requestTracker;
this.glide = Glide.get(context);
this.optionsApplier = new OptionsApplier();
ConnectivityMonitor connectivityMonitor = factory.build(context,
new RequestManagerConnectivityListener(requestTracker));
// If we're the application level request manager, we may be created on a background thread. In that case we
// cannot risk synchronously pausing or resuming requests, so we hack around the issue by delaying adding
// ourselves as a lifecycle listener by posting to the main thread. This should be entirely safe.
if (Util.isOnBackgroundThread()) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
lifecycle.addListener(RequestManager.this);
}
});
} else {
lifecycle.addListener(this);
}
lifecycle.addListener(connectivityMonitor);
}
这里在构造参数里面看到传入两个非常重要的东西,一个是Lifecycle对象,另外一个是RequestTracker 。RequestTracker 是用来跟踪我们的请求对象是否取消,是否暂停,是否完成,它会监听其状态。
这里来总结一下,SupportRequestManagerFragment就是一个封装的fagment,它是用来连接fragment和我们的requestmanager,然后另外一个很重要的东西就是RequestManager,它是所有图片的一个管理类。它是用来实现我们生命周期方法的。
好了,with方法里面需要关注的内容就是这些。
##load
/**
* Returns a request builder to load the given {@link java.lang.String}.
* signature.
*
* @see #fromString()
* @see #load(Object)
*
* @param string A file path, or a uri or url handled by {@link com.bumptech.glide.load.model.UriLoader}.
*/
public DrawableTypeRequest<String> load(String string) {
return (DrawableTypeRequest<String>) fromString().load(string);
}
DrawableTypeRequest就是一个管理图片的请求类,不仅可以加载GIF图片也可以直接加载Bitmap。它的父类DrawableRequestBuilder里面,我们看一下他的构造函数
DrawableRequestBuilder(Context context, Class<ModelType> modelClass,
LoadProvider<ModelType, ImageVideoWrapper, GifBitmapWrapper, GlideDrawable> loadProvider, Glide glide,
RequestTracker requestTracker, Lifecycle lifecycle) {
super(context, modelClass, loadProvider, GlideDrawable.class, glide, requestTracker, lifecycle);
// Default to animating.
crossFade();
}
构造函数里面有Lifecycle RequestTracker,这些跟刚才Requestmanager里面差不多的类。load方法里面做的就一些赋值的操作。
##into
当我们做好with和load方法后,最后一步是into,也是最麻烦的一步。
@Override
public Target<GlideDrawable> into(ImageView view) {
return super.into(view);
}
调用了父类GenericRequestBuilder的into,我们来看下GenericRequestBuilder的into方法
public Target<TranscodeType> into(ImageView view) {
Util.assertMainThread();
if (view == null) {
throw new IllegalArgumentException("You must pass in a non null View");
}
if (!isTransformationSet && view.getScaleType() != null) {
switch (view.getScaleType()) {
case CENTER_CROP:
applyCenterCrop();
break;
case FIT_CENTER:
case FIT_START:
case FIT_END:
applyFitCenter();
break;
//$CASES-OMITTED$
default:
// Do nothing.
}
}
return into(glide.buildImageViewTarget(view, transcodeClass));
}
核心是调用了into方法,我们来看下
/**
* Set the target the resource will be loaded into.
*
* @see Glide#clear(com.bumptech.glide.request.target.Target)
*
* @param target The target to load the resource into.
* @return The given target.
*/
public <Y extends Target<TranscodeType>> Y into(Y target) {
Util.assertMainThread();
if (target == null) {
throw new IllegalArgumentException("You must pass in a non null Target");
}
if (!isModelSet) {
throw new IllegalArgumentException("You must first set a model (try #load())");
}
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
requestTracker.removeRequest(previous);
previous.recycle();
}
Request request = buildRequest(target);
target.setRequest(request);
lifecycle.addListener(target);
requestTracker.runRequest(request);
return target;
}
这里有个target,这个方法主要就是要设置我们的target,target我们理解成view就行了。因为我们更新都要放在主线程中去执行,所有第一句代码是断言我们是否在主线程中。通过target.getRequest方法来获得请求。如果之前的请求不为空的话,则清空之前的请求。然后我们再会去创建一个新的request,通过buildRequest方法。
调用setRequest和addListener把我们的imagview跟lifecycler关联起来,调用runRequest。
最后返回这个request对象。
看一下buildRequest方法
private Request buildRequest(Target<TranscodeType> target) {
if (priority == null) {
priority = Priority.NORMAL;
}
return buildRequestRecursive(target, null);
}
private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
if (thumbnailRequestBuilder != null) {
if (isThumbnailBuilt) {
throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, "
+ "consider using clone() on the request(s) passed to thumbnail()");
}
// Recursive case: contains a potentially recursive thumbnail request builder.
if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
thumbnailRequestBuilder.animationFactory = animationFactory;
}
if (thumbnailRequestBuilder.priority == null) {
thumbnailRequestBuilder.priority = getThumbnailPriority();
}
if (Util.isValidDimensions(overrideWidth, overrideHeight)
&& !Util.isValidDimensions(thumbnailRequestBuilder.overrideWidth,
thumbnailRequestBuilder.overrideHeight)) {
thumbnailRequestBuilder.override(overrideWidth, overrideHeight);
}
ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
// Guard against infinite recursion.
isThumbnailBuilt = true;
// Recursively generate thumbnail requests.
Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
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(parentCoordinator);
Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
coordinator.setRequests(fullRequest, thumbnailRequest);
return coordinator;
} else {
// Base case: no thumbnail.
return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
}
}
代码虽然很多,但绝大部分只是逻辑上的判断,最终调用obtainRequest方法
private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
RequestCoordinator requestCoordinator) {
return GenericRequest.obtain(
loadProvider,
model,
signature,
context,
priority,
target,
sizeMultiplier,
placeholderDrawable,
placeholderId,
errorPlaceholder,
errorId,
fallbackDrawable,
fallbackResource,
requestListener,
requestCoordinator,
glide.getEngine(),
transformation,
transcodeClass,
isCacheable,
animationFactory,
overrideWidth,
overrideHeight,
diskCacheStrategy);
}
最终调用父类的obtain方法,我们只用大概知道 在obtain方法里面完成request的各种参数的初始化工作,有深入研究的同学可以再进入到这个方法里面。这里创建request的过程就这样子结束。
回到into方法的代码里面,我们看到有个runRequest方法。找到runRequest方法的源码,在RequestTracker里面。
/**
* Starts tracking the given request.
*/
public void runRequest(Request request) {
requests.add(request);
if (!isPaused) {
request.begin();
} else {
pendingRequests.add(request);
}
}
ok,在这里面请求被放在一个set集合里面进行管理,最后调用请求的begin开始加载。而request .begin的真正实现是在GenericRequest的begin方法里面
@Override
public void begin() {
startTime = LogTime.getLogTime();
if (model == null) {
onException(null);
return;
}
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if (!isComplete() && !isFailed() && canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished run method in " + LogTime.getElapsedMillis(startTime));
}
}
首先赋值了状态,图片的状态设置成Status.WAITING_FOR_SIZE,然后对图片的长宽高进行计算,如果长宽高小于0的话,那么就要重新对其进行计算。如果长宽高都正常,onSizeReady对图片加载做一些准备工作。然后在我们图片调用成功以后,它会调用一个onLoadStart方法。接下来我们来看下onSizeReady的代码:
@Override
public void onSizeReady(int width, int height) {
... ....
status = Status.RUNNING;
width = Math.round(sizeMultiplier * width);
height = Math.round(sizeMultiplier * height);
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
if (dataFetcher == null) {
onException(new Exception("Failed to load model: \'" + model + "\'"));
return;
}
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
}
loadedFromMemoryCache = true;
loadStatus = engine.load(signature, width, height, dataFetcher, loadProvider, transformation, transcoder,
priority, isMemoryCacheable, diskCacheStrategy, this);
loadedFromMemoryCache = resource != null;
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
}
}
这里有个非常重要的方法,engine.load方法,engine对象是一个Engine类,主要用来管理图片开始加载后,一些活动图片资源,缓存图片资源,里面分装了线程池,给我们图片加载请求。来看下Engine类和它里面的load方法:
public class Engine implements EngineJobListener,
MemoryCache.ResourceRemovedListener,
EngineResource.ResourceListener {
private static final String TAG = "Engine";
private final Map<Key, EngineJob> jobs;
private final EngineKeyFactory keyFactory;
private final MemoryCache cache;
private final EngineJobFactory engineJobFactory;
private final Map<Key, WeakReference<EngineResource<?>>> activeResources;
private final ResourceRecycler resourceRecycler;
private final LazyDiskCacheProvider diskCacheProvider;
private ReferenceQueue<EngineResource<?>> resourceReferenceQueue;
... ...
public <T, Z, R> LoadStatus load(Key signature, int width, int height, DataFetcher<T> fetcher,
DataLoadProvider<T, Z> loadProvider, Transformation<Z> transformation, ResourceTranscoder<Z, R> transcoder,
Priority priority, boolean isMemoryCacheable, DiskCacheStrategy diskCacheStrategy, ResourceCallback cb) {
Util.assertMainThread();
long startTime = LogTime.getLogTime();
final String id = fetcher.getId();
EngineKey key = keyFactory.buildKey(id, signature, width, height, loadProvider.getCacheDecoder(),
loadProvider.getSourceDecoder(), transformation, loadProvider.getEncoder(),
transcoder, loadProvider.getSourceEncoder());
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineJob current = jobs.get(key);
if (current != null) {
current.addCallback(cb);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
transcoder, diskCacheProvider, diskCacheStrategy, priority);
EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(runnable);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
在load方法里面,看到enginekey是一个key值,它的作用就是从池中取出。
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
从上面这句代码看到,首先它会从缓存中去加载图片,如果图片获取成功的话,会调用onResouceReady方法。
如果从缓存中没有获取到相应的图片的话,这时候会从活动的图片资源中去获得,执行loadFromActiveResources方法。如果图片从活动资源的缓存中获取到的话,也会进入onResouceReady方法。
EngineJob是一个添加和删除回调接口的管理类。通过创建EngineJob后我们看到最终是创建了一个EngineRunnable对象,EngineRunnable顾名思义就是开启了一个子线程,接着调用它的start方法。看一下EngineJob的start方法。
public void start(DecodeJob<R> decodeJob) {
this.decodeJob = decodeJob;
GlideExecutor executor = decodeJob.willDecodeFromCache()
? diskCacheExecutor
: getActiveSourceExecutor();
executor.execute(decodeJob);
}
执行了GlideExecutor的execute方法,而这个GlideExecutor进去一看。
final class GlideExecutor extends ThreadPoolExecutor
ThreadPoolExecutor是线程池,这样我们就大概地知道了,
到了最终Glide还是将我们的图片请求交给了线程池去处理。这样就大概明白了glide图片加载的原理。
好了,Glide的源码分析到这里结束,由于本人精力和能力有限,无法很深入地进行理解,也没有对细节进行深究,这里只是浅显的分析了基本用法的实现原理,以及glide内部加载图片是调用了ThreadPoolExecutor,交由线程池去处理的。