对于Glide解析类的文章,如今互联网也不算不少,由于它的源码太复杂了,而且3.0和4.0版本差别性很大。我阅读源码也有一段时间,这里也作个简单的分享,可是对于Glide源码的阅读,我但愿你们不要去刨根问底追求每个细节,每一行代码,这样你很容易迷茫,咱们抓住主线走一遍源码就能够了。html
咱们首先看看Glide的使用,最基本的使用java
Glide.with(MainActivity.this).load(url).into(imageview);
复制代码
glide的使用很是简单,就是一句话的事,可是这一句话的背后有成吨的任务要处理,咱们今天的解析主要是围绕着这句话展开。缓存
@NonNull
public static RequestManager with(@NonNull Context context) {
return getRetriever(context).get(context);
}
复制代码
在Glide类中,重载了几个with,主要是针对Activity,Fragment,View,Context等等,这一点说明Glide是使用场景很普遍,能够各类载体上去使用。咱们这里分享一种就能够了。咱们看看getRetriever方法。bash
@NonNull
private static RequestManagerRetriever getRetriever(@Nullable Context context) {
// Context could be null for other reasons (ie the user passes in null), but in practice it will
// only occur due to errors with the Fragment lifecycle.
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();
}
复制代码
这里Glide.get是初始化Glide,而后最终返回的RequestManagerRetriever,咱们先看看Glide是怎么被初始化的,看看get方法:网络
@NonNull
public static Glide get(@NonNull Context context) {
if (glide == null) {
synchronized (Glide.class) {
if (glide == null) {
checkAndInitializeGlide(context);
}
}
}
return glide;
}
复制代码
这里使用里单例模式,整个过程只有惟一Glide对象,咱们接着看,最终初始化的会到下面方法中去。app
@SuppressWarnings("deprecation")
private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
Context applicationContext = context.getApplicationContext();
ifestModules = new ManifestParser(applicationContext).parse();
}
...
Glide glide = builder.build(applicationContext);
...
applicationContext.registerComponentCallbacks(glide);
Glide.glide = glide;
}
复制代码
这个方法相对比较长,咱们只看主线,这里的主线就是Glide的初始化,GlideBuilder,它是采用了建造者模式去构建glide对象的,那么咱们直接看看GlideBuilder里面的build()ide
@NonNull
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 =
new Engine(
memoryCache,
diskCacheFactory,
diskCacheExecutor,
sourceExecutor,
GlideExecutor.newUnlimitedSourceExecutor(),
GlideExecutor.newAnimationExecutor(),
isActiveResourceRetentionAllowed);
}
RequestManagerRetriever requestManagerRetriever =
new RequestManagerRetriever(requestManagerFactory);
return new Glide(
context,
engine,
memoryCache,
bitmapPool,
arrayPool,
requestManagerRetriever,
connectivityMonitorFactory,
logLevel,
defaultRequestOptions.lock(),
defaultTransitionOptions);
}
复制代码
这里构建了,线程池,缓存器(内存缓存,磁盘缓存),Engine和RequestManagerRetriever对象。这里不具体展开,接着就new Glide(),把刚刚构建的各类参数传递过去。这样glide就初始化完毕了。工具
@NonNull
public static RequestManager with(@NonNull Activity activity) {
return getRetriever(activity).get(activity);
}
复制代码
get方法是在初始化Glide之后获取RequestManager对象。如今咱们总结一下,Glide.with()作了哪些事:oop
接下来咱们分load方法,还方法在RequestManager中一样重载了不少次,由于load的方式不少,图片能够从网络,文件,本地资源等加载出来。咱们分析加载网络的吧。fetch
/**
* Equivalent to calling {@link #asDrawable()} and then {@link RequestBuilder#load(URL)}.
*
* @return A new request builder for loading a {@link Drawable} using the given model.
*/
@Override
@Deprecated
public RequestBuilder<Drawable> load(@Nullable URL url) {
return asDrawable().load(url);
}
复制代码
它是经过url参数去构建request,返回的图片是drawable。asDrawable会调用下面方法:
public <ResourceType> RequestBuilder<ResourceType> as(
@NonNull Class<ResourceType> resourceClass) {
return new RequestBuilder<>(glide, this, resourceClass, context);
}
复制代码
这里新建了RequestBuilder对象,那么RequestBuilder是干吗的,咱们进去看看。
protected RequestBuilder(Glide glide, RequestManager requestManager,
Class<TranscodeType> transcodeClass, Context context) {
this.glide = glide;
this.requestManager = requestManager;
this.transcodeClass = transcodeClass;
this.defaultRequestOptions = requestManager.getDefaultRequestOptions();
this.context = context;
this.transitionOptions = requestManager.getDefaultTransitionOptions(transcodeClass);
this.requestOptions = defaultRequestOptions;
this.glideContext = glide.getGlideContext();
}
复制代码
这个类就是请求类,它会封装咱们请求的options,好比请求中的图片或者gif,还有glide对象,而后发起请求开始load数据。
public RequestBuilder<TranscodeType> load(@Nullable Object model) {
return loadGeneric(model);
}
@NonNull
private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
this.model = model;
isModelSet = true;
return this;
}
复制代码
这里就是load了,其实它并无干什么事,即便一个中转站而已,保存了请求的url,设置isModelSet为true,这个参数后面会用到。从这里能够看出来。既然load没有干太多的事,那么任务就都交给了into了。
into就是把图片设置到view上面显示出来了,这里面的内容不少,咱们一步一步来分析。上面load返回的类型是RequestBuilder,也就是说into仍然是在这个类中进行的:
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
Util.assertMainThread();
Preconditions.checkNotNull(view);
RequestOptions requestOptions = this.requestOptions;
if (!requestOptions.isTransformationSet()
&& requestOptions.isTransformationAllowed()
&& view.getScaleType() != null) {
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);
}
复制代码
首先获取了options里面的一些值,这些view的参数来决定的。而后来看看buildImageViewTarget
@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
@NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}
复制代码
这里利用参数ImageView,transcodeClass去构建ViewTarget,继续跟进去看看:
public class ImageViewTargetFactory {
@NonNull
@SuppressWarnings("unchecked")
public <Z> ViewTarget<ImageView, Z> buildTarget(@NonNull ImageView view,
@NonNull Class<Z> 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)");
}
}
}
复制代码
这里会对transcodeClass作个判断,表面咱们是请求哪种类型的图片,默认的是Drawable.class,最后咱们设置的View会到达ViewTarget中去:
/**
* Constructor that defaults {@code waitForLayout} to {@code false}.
*/
public ViewTarget(@NonNull T view) {
this.view = Preconditions.checkNotNull(view);
sizeDeterminer = new SizeDeterminer(view);
}
复制代码
这就是这一步主要是建立ViewTarget。那么知道建立target后,咱们返回去看看into,ViewTarget是以参数的形式设置到into里面去的。
private <Y extends Target<TranscodeType>> Y into(
@NonNull Y target,
@Nullable RequestListener<TranscodeType> targetListener,
@NonNull RequestOptions options) {
Util.assertMainThread();
Preconditions.checkNotNull(target);
if (!isModelSet) {
throw new IllegalArgumentException("You must call #load() before calling #into()");
}
options = options.autoClone();
Request request = buildRequest(target, targetListener, options);
Request previous = target.getRequest();
if (request.isEquivalentTo(previous)
&& !isSkipMemoryCacheWithCompletePreviousRequest(options, previous)) {
request.recycle();
// 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;
}
复制代码
这个方法很是重要,咱们分析一下,里面构建了Request,它是资源加载的发起者,咱们来看看buildRequest:
private Request buildRequest(
Target<TranscodeType> target,
@Nullable RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions) {
return buildRequestRecursive(
target,
targetListener,
/*parentCoordinator=*/ null,
transitionOptions,
requestOptions.getPriority(),
requestOptions.getOverrideWidth(),
requestOptions.getOverrideHeight(),
requestOptions);
}
复制代码
构建request最终会调用obtainRequest
private Request obtainRequest(
Target<TranscodeType> target,
RequestListener<TranscodeType> targetListener,
RequestOptions requestOptions,
RequestCoordinator requestCoordinator,
TransitionOptions<?, ? super TranscodeType> transitionOptions,
Priority priority,
int overrideWidth,
int overrideHeight) {
return SingleRequest.obtain(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListener,
requestCoordinator,
glideContext.getEngine(),
transitionOptions.getTransitionFactory());
}
复制代码
能够看到,它的参数很是多,这些参数若是设置那就默认的,咱们能够在使用的时候自定义去设置这些参数。接着看SingleRequest.obtain
public static <R> SingleRequest<R> obtain(
Context context,
GlideContext glideContext,
Object model,
Class<R> transcodeClass,
RequestOptions requestOptions,
int overrideWidth,
int overrideHeight,
Priority priority,
Target<R> target,
RequestListener<R> targetListener,
RequestListener<R> requestListener,
RequestCoordinator requestCoordinator,
Engine engine,
TransitionFactory<? super R> animationFactory) {
@SuppressWarnings("unchecked") SingleRequest<R> request =
(SingleRequest<R>) POOL.acquire();
if (request == null) {
request = new SingleRequest<>();
}
request.init(
context,
glideContext,
model,
transcodeClass,
requestOptions,
overrideWidth,
overrideHeight,
priority,
target,
targetListener,
requestListener,
requestCoordinator,
engine,
animationFactory);
return request;
}
复制代码
它就是新建了SingleRequest,而且作了一些初始化工做init,到此,Requset请求就构建完毕了。咱们回到into方法。它会把刚刚新建的request设置给target,而后开始加载资源:
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 {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Paused, delaying request");
}
pendingRequests.add(request);
}
}
复制代码
显然,这里begin就是开始加载资源了,跟进去看看,request是个接口,咱们这里实现类是SingleRequest,因此到这个类里面去看看begin。
@Override
public void begin() {
if (model == null) {
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
width = overrideWidth;
height = overrideHeight;
}
onLoadFailed(new GlideException("Received null model"), logLevel);
return;
}
if (status == Status.RUNNING) {
throw new IllegalArgumentException("Cannot restart a running request");
}
if (status == Status.COMPLETE) {
onResourceReady(resource, DataSource.MEMORY_CACHE);
return;
}
// Restarts for requests that are neither complete nor running can be treated as new requests
// and can run again from the beginning.
status = Status.WAITING_FOR_SIZE;
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
onSizeReady(overrideWidth, overrideHeight);
} else {
target.getSize(this);
}
if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
&& canNotifyStatusChanged()) {
target.onLoadStarted(getPlaceholderDrawable());
}
}
复制代码
这里主要作了如下几件事:
@Override
public void onSizeReady(int width, int height) {
this.height = maybeApplySizeMultiplier(height, sizeMultiplier);
...
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);
...
}
}
复制代码
Engine出来了,它是Glide的发动机引擎,咱们看到它的load方法中的参数,都是requestOptions类的配置,这些配置都是能够在使用的时候根据本身的状况去配置的。咱们看看Engine的load方法:
public <R> LoadStatus load(
GlideContext glideContext,
Object model,
Key signature,
int width,
int height,
Class<?> resourceClass,
Class<R> transcodeClass,
Priority priority,
DiskCacheStrategy diskCacheStrategy,
Map<Class<?>, Transformation<?>> transformations,
boolean isTransformationRequired,
boolean isScaleOnlyOrNoTransform,
Options options,
boolean isMemoryCacheable,
boolean useUnlimitedSourceExecutorPool,
boolean useAnimationPool,
boolean onlyRetrieveFromCache,
ResourceCallback cb) {
Util.assertMainThread();
long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
resourceClass, transcodeClass, options);
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
cb.onResourceReady(active, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from active resources", startTime, key);
}
return null;
}
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Loaded resource from cache", startTime, key);
}
return null;
}
EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
current.addCallback(cb);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Added to existing load", startTime, key);
}
return new LoadStatus(cb, current);
}
EngineJob<R> engineJob =
engineJobFactory.build(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache);
DecodeJob<R> decodeJob =
decodeJobFactory.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob);
jobs.put(key, engineJob);
engineJob.addCallback(cb);
engineJob.start(decodeJob);
if (VERBOSE_IS_LOGGABLE) {
logWithTimeAndKey("Started new load", startTime, key);
}
return new LoadStatus(cb, engineJob);
}
复制代码
这个类作的几件事
咱们来看看start后调用的run方法:
@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 (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;
}
} 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();
}
}
复制代码
咱们只抓主线,这里的核心是runWrapped,
private void runWrapped() {
switch (runReason) {
case INITIALIZE:
stage = getNextStage(Stage.INITIALIZE);
currentGenerator = getNextGenerator();
runGenerators();
break;
case SWITCH_TO_SOURCE_SERVICE:
runGenerators();
break;
case DECODE_DATA:
decodeFromRetrievedData();
break;
default:
throw new IllegalStateException("Unrecognized run reason: " + runReason);
}
}
复制代码
咱们在初始化DecodeEngine的时候设置了runReason为INITIALIZE,因此这里会走这个分支,而且currentGenerator通过几回演变以后最后是SourceGenerator,因此咱们直接开分析这个类型的
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. } 复制代码
这里的核心是startNext方法,咱们进入SourceGenerator的startNext方法
@Override
public boolean startNext() {
while (modelLoaders == null || !hasNextModelLoader()) {
sourceIdIndex++;
if (sourceIdIndex >= cacheKeys.size()) {
return false;
}
Key sourceId = cacheKeys.get(sourceIdIndex);
// PMD.AvoidInstantiatingObjectsInLoops The loop iterates a limited number of times
// and the actions it performs are much more expensive than a single allocation.
@SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops")
Key originalKey = new DataCacheKey(sourceId, helper.getSignature());
cacheFile = helper.getDiskCache().get(originalKey);
if (cacheFile != null) {
this.sourceKey = sourceId;
modelLoaders = helper.getModelLoaders(cacheFile);
modelLoaderIndex = 0;
}
}
loadData = null;
boolean started = false;
while (!started && hasNextModelLoader()) {
ModelLoader<File, ?> modelLoader = modelLoaders.get(modelLoaderIndex++);
loadData =
modelLoader.buildLoadData(cacheFile, helper.getWidth(), helper.getHeight(),
helper.getOptions());
if (loadData != null && helper.hasLoadPath(loadData.fetcher.getDataClass())) {
started = true;
loadData.fetcher.loadData(helper.getPriority(), this);
}
}
return started;
}
复制代码
这里是Glide的一个难点,咱们怎么从这里到加载网络资源的部分。笔者这里不做详细分析,loadData.fetcher.loadData(helper.getPriority(), this);最终会到HttpGlideUrlLoader类中去,
@Override
public LoadData<InputStream> buildLoadData(@NonNull GlideUrl model, int width, int height,
@NonNull Options options) {
// GlideUrls memoize parsed URLs so caching them saves a few object instantiations and time
// spent parsing urls.
GlideUrl url = model;
if (modelCache != null) {
url = modelCache.get(model, 0, 0);
if (url == null) {
modelCache.put(model, 0, 0, model);
url = model;
}
}
int timeout = options.get(TIMEOUT);
return new LoadData<>(url, new HttpUrlFetcher(url, timeout));
}
复制代码
先判断是否有缓存url,最终会新建LoadData,HttpUrlFetcher就是发送网络请求的核心代码了,这个类中的loadData就是具体的实现方式,通过这些分析,上面的就分析完毕了,咱们如今来看看ttpUrlFetcher的loadData
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super InputStream> callback) {
long startTime = LogTime.getLogTime();
try {
InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
callback.onDataReady(result);
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
} finally {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
}
}
}
复制代码
从网络中去获取图片资源,而后将返回结果给onDataReady。咱们看看核心的loadDataWithRedirects是怎么请求网络的:
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map<String, String> 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<String, String> 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); } } 复制代码
哇哦,就是传统的urlConnection,这里不用详细分析了。其实能分析到这里就已经只很不错了,毕竟看到了网络请求的部分,后面会有decode相关,因为篇幅问题,转移到下篇去介绍吧。