美文网首页
Glide整体流程(仅到网络加载部分)

Glide整体流程(仅到网络加载部分)

作者: 竹叶儿青 | 来源:发表于2020-06-07 17:16 被阅读0次

    1. 基本使用

     Glide.with(context)
                    .load(url)
                    .centerCrop()
                    .diskCacheStrategy(DiskCacheStrategy.RESOURCE)
                    .transform(new RoundedCorners(20))
                    .error(R.drawable.error)
                    .into(imageView);
    

    2. with方法

    with方法有多个重载,可以接受context、activity、fragment、app下的fragment和view,不过app包下的fragment已经标为@Deprecated:

     @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);
      }
    
    @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方法里调用getRetriever方法去获取RequestManagerRetriever对象,然后调用get方法获取RequestManager。
    get方法也有很多重载:

    @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)) {
          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());
          }
        }
    
        return getApplicationManager(context);
      }
    
    @NonNull
      public RequestManager get(@NonNull FragmentActivity activity) {
        if (Util.isOnBackgroundThread()) {
          return get(activity.getApplicationContext());
        } else {
          assertNotDestroyed(activity);
          FragmentManager fm = activity.getSupportFragmentManager();
          return supportFragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
        }
      }
    
      @NonNull
      public RequestManager get(@NonNull Fragment fragment) {
        Preconditions.checkNotNull(
            fragment.getContext(),
            "You cannot start a load on a fragment before it is attached or after it is destroyed");
        if (Util.isOnBackgroundThread()) {
          return get(fragment.getContext().getApplicationContext());
        } else {
          FragmentManager fm = fragment.getChildFragmentManager();
          return supportFragmentGet(fragment.getContext(), fm, fragment, fragment.isVisible());
        }
      }
    
      @SuppressWarnings("deprecation")
      @NonNull
      public RequestManager get(@NonNull Activity activity) {
        if (Util.isOnBackgroundThread()) {
          return get(activity.getApplicationContext());
        } else {
          assertNotDestroyed(activity);
          android.app.FragmentManager fm = activity.getFragmentManager();
          return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
        }
      }
    
      @SuppressWarnings("deprecation")
      @NonNull
      public RequestManager get(@NonNull View view) {
        if (Util.isOnBackgroundThread()) {
          return get(view.getContext().getApplicationContext());
        }
    
        Preconditions.checkNotNull(view);
        Preconditions.checkNotNull(
            view.getContext(), "Unable to obtain a request manager for a view without a Context");
        Activity activity = findActivity(view.getContext());
        // The view might be somewhere else, like a service.
        if (activity == null) {
          return get(view.getContext().getApplicationContext());
        }
    
        // Support Fragments.
        // Although the user might have non-support Fragments attached to FragmentActivity, searching
        // for non-support Fragments is so expensive pre O and that should be rare enough that we
        // prefer to just fall back to the Activity directly.
        if (activity instanceof FragmentActivity) {
          Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
          return fragment != null ? get(fragment) : get((FragmentActivity) activity);
        }
    
        // Standard Fragments.
        android.app.Fragment fragment = findFragment(view, activity);
        if (fragment == null) {
          return get(activity);
        }
        return get(fragment);
      }
    

    可以看到,当在非ui线程调用的时候,统一调用getApplicationManager获取应用级别的applicationManager,glide的生命周期和应用程序一样,无需特殊处理。
    否则就调用相应的getSupportFragmentManager、getChildFragmentManager、getFragmentManager,通过向传入的fragment、activity中添加SupportRequestManagerFragment来管理glide请求,如SupportRequestManagerFragment所述,添加fragment用于管理glide所在activity、fragment的请求,使得glide的生命周期和所在容器的生命周期一致,避免出现activity销毁,而glide还在加载的情况:

    **
     * A view-less {@link androidx.fragment.app.Fragment} used to safely store an {@link
     * com.bumptech.glide.RequestManager} that can be used to start, stop and manage Glide requests
     * started for targets within the fragment or activity this fragment is a child of.
     *
     * @see com.bumptech.glide.manager.RequestManagerFragment
     * @see com.bumptech.glide.manager.RequestManagerRetriever
     * @see com.bumptech.glide.RequestManager
     */
    public class SupportRequestManagerFragment extends Fragment {...}
    

    总的来说,with方法就是为了得到RequestManager对象,并通过多种with方法确定glide的图片请求的生命周期。

    3. load方法

    由于with方法得到的是RequestManager对象,那么显然load方法是RequestManager的方法了。
    RequestManager#load方法同样有很多重载:

    @NonNull
      @CheckResult
      @Override
      public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
        return asDrawable().load(bitmap);
      }
    
    @NonNull
      @CheckResult
      @Override
      public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
        return asDrawable().load(drawable);
      }
    
     @NonNull
      @CheckResult
      @Override
      public RequestBuilder<Drawable> load(@Nullable String string) {
        return asDrawable().load(string);
      }
    
    ...
    
    @NonNull
      @CheckResult
      public RequestBuilder<Drawable> asDrawable() {
        return as(Drawable.class);
      }
    
    /**
       * Attempts to load the resource using any registered {@link
       * com.bumptech.glide.load.ResourceDecoder}s that can decode the given resource class or any
       * subclass of the given resource class.
       *
       * @param resourceClass The resource to decode.
       * @return A new request builder for loading the given resource class.
       */
      @NonNull
      @CheckResult
      public <ResourceType> RequestBuilder<ResourceType> as(
          @NonNull Class<ResourceType> resourceClass) {
        return new RequestBuilder<>(glide, this, resourceClass, context);
      }
    

    在RequestManager#load方法中首先调用了asDrawable方法,将Drawable.class传递给as方法,用于创建RequestBuilder对象,资源类型就是传递进来的Drawable.class。如果调用了asGif或者asBitmap方法,则会指明资源类型为gif或者bitmap。
    确定资源类型之后,RequestManager#load方法又调用了RequestBuilder#load方法,同样有很多重载:

     @Override
      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;
      }
    
     @NonNull
      @CheckResult
      @Override
      public RequestBuilder<TranscodeType> load(@Nullable Bitmap bitmap) {
        return loadGeneric(bitmap).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
      }
    
    @NonNull
      @CheckResult
      @Override
      public RequestBuilder<TranscodeType> load(@Nullable Drawable drawable) {
        return loadGeneric(drawable).apply(diskCacheStrategyOf(DiskCacheStrategy.NONE));
      }
    
     @NonNull
      @Override
      @CheckResult
      public RequestBuilder<TranscodeType> load(@Nullable String string) {
        return loadGeneric(string);
      }
    

    还有参数为Uri、File、Integer、byte[]的重载,这些重载基本都是调用loadGeneric方法,而对参数为bitmap和drawable的重载附加指明了磁盘缓存策略为DiskCacheStrategy.NONE,即不缓存任何数据。
    其中loadGeneric方法仅仅是将参数isModelSet设为true,将model赋值,其在into方法中会调用,如下:

     private <Y extends Target<TranscodeType>> Y into(
          @NonNull Y target,
          @Nullable RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> options,
          Executor callbackExecutor) {
        Preconditions.checkNotNull(target);
        if (!isModelSet) {
          throw new IllegalArgumentException("You must call #load() before calling #into()");
        }
    ...
    

    除了load方法之外,其他的常见处理方法也在RequestBuilder类中,如:

    /**
     * A generic class that can handle setting options and staring loads for generic resource types.
     *
     * @param <TranscodeType> The type of resource that will be delivered to the {@link
     *     com.bumptech.glide.request.target.Target}.
     */
    // Public API.
    @SuppressWarnings({"unused", "WeakerAccess"})
    public class RequestBuilder<TranscodeType> extends BaseRequestOptions<RequestBuilder<TranscodeType>>
        implements Cloneable, ModelTypes<RequestBuilder<TranscodeType>> {
    ...
    
     @NonNull
      public RequestBuilder<TranscodeType> error(@Nullable RequestBuilder<TranscodeType> errorBuilder) {
        this.errorBuilder = errorBuilder;
        return this;
      }
    
     @NonNull
      @CheckResult
      @SuppressWarnings("unchecked")
      public RequestBuilder<TranscodeType> thumbnail(
          @Nullable RequestBuilder<TranscodeType> thumbnailRequest) {
        this.thumbnailBuilder = thumbnailRequest;
    
        return this;
      }
    
    ...
    

    可以看到,RequestBuilder继承自BaseRequestOptions,点进去可以看到各种常见接口:

    @NonNull
      @CheckResult
      public T placeholder(@Nullable Drawable drawable) {
        if (isAutoCloneEnabled) {
          return clone().placeholder(drawable);
        }
    
        this.placeholderDrawable = drawable;
        fields |= PLACEHOLDER;
    
        placeholderId = 0;
        fields &= ~PLACEHOLDER_ID;
    
        return selfOrThrowIfLocked();
      }
    
    @NonNull
      @CheckResult
      public T skipMemoryCache(boolean skip) {
        if (isAutoCloneEnabled) {
          return clone().skipMemoryCache(true);
        }
    
        this.isCacheable = !skip;
        fields |= IS_CACHEABLE;
    
        return selfOrThrowIfLocked();
      }
    
    @NonNull
      @CheckResult
      public T override(int width, int height) {
        if (isAutoCloneEnabled) {
          return clone().override(width, height);
        }
    
        this.overrideWidth = width;
        this.overrideHeight = height;
        fields |= OVERRIDE;
    
        return selfOrThrowIfLocked();
      }
    
     @NonNull
      @CheckResult
      public T centerCrop() {
        return transform(DownsampleStrategy.CENTER_OUTSIDE, new CenterCrop());
      }
    
    @NonNull
      @CheckResult
      public T fitCenter() {
        return scaleOnlyTransform(DownsampleStrategy.FIT_CENTER, new FitCenter());
      }
    ...
    

    这里的泛型T就是我们在RequestBuilder中传入的TranscodeType,也就是RequestManager的as方法中设置的resourceClass,即Drawable或者调用asGif方法后的GifDrawable、asBitmap方法后的Bitmap。

    3. into方法

    RequestBuilder中对into方法有3个重载:

     @NonNull
      public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {
        return into(target, /*targetListener=*/ null, Executors.mainThreadExecutor());
      }
    
      @NonNull
      @Synthetic
      <Y extends Target<TranscodeType>> Y into(
          @NonNull Y target,
          @Nullable RequestListener<TranscodeType> targetListener,
          Executor callbackExecutor) {
        return into(target, targetListener, /*options=*/ this, callbackExecutor);
      }
    
      private <Y extends Target<TranscodeType>> Y into(
          @NonNull Y target,
          @Nullable RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> options,
          Executor callbackExecutor) {
        Preconditions.checkNotNull(target);
        if (!isModelSet) {
          throw new IllegalArgumentException("You must call #load() before calling #into()");
        }
    
        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;
      }
    

    可以看到,前2个into方法都会调用最后一个into方法,第三个into方法调用buildRequest构建了request,然后调用requestManager#track方法进行跟踪和发送:

     synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
        targetTracker.track(target);
        requestTracker.runRequest(request);
      }
    

    这里的runRequest方法会调用request#begin方法开始运行:

     /** 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);
        }
      }
    

    这里的request是在buildRequest中构建的:

    private Request buildRequest(
          Target<TranscodeType> target,
          @Nullable RequestListener<TranscodeType> targetListener,
          BaseRequestOptions<?> requestOptions,
          Executor callbackExecutor) {
        return buildRequestRecursive(...);
    
    private Request buildRequestRecursive(...) {
     Request mainRequest =
            buildThumbnailRequestRecursive(...)
    ...}
    
    private Request buildThumbnailRequestRecursive(...) {
     //handle thumbnail request
    ...
    else {
          // Base case: no thumbnail.
          return obtainRequest(
              requestLock,
              target,
              targetListener,
              requestOptions,
              parentCoordinator,
              transitionOptions,
              priority,
              overrideWidth,
              overrideHeight,
              callbackExecutor);
        }
    
     private Request obtainRequest(...) {
        return SingleRequest.obtain(...);
    
     public static <R> SingleRequest<R> obtain(...) {
    return new SingleRequest<>(
            context,
            glideContext,
            requestLock,
            model,
            transcodeClass,
            requestOptions,
            overrideWidth,
            overrideHeight,
            priority,
            target,
            targetListener,
            requestListeners,
            requestCoordinator,
            engine,
            animationFactory,
            callbackExecutor);
    }
    

    因此,buildRequest方法构建的是SingleRequest对象, 前面的request#begin方法调用的就是SingleRequest的begin方法:

     @Override
      public void begin() {
        synchronized (requestLock) {
        ...
    
          // 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);
          }
    
       ...
          }
        }
      }
    

    其中调用了onSizeReady方法:

    @Override
      public void onSizeReady(int width, int height) {
    ...
    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);
    ...}
    

    这里调用了engine#load方法进行加载:

    public <R> LoadStatus load(...) {
        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(...);
          }
        }
    
    ...
    
    private <R> LoadStatus waitForExistingOrStartNewJob(...) {
     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, callbackExecutor);
        engineJob.start(decodeJob);
    
    public synchronized void start(DecodeJob<R> decodeJob) {
        this.decodeJob = decodeJob;
        GlideExecutor executor =
            decodeJob.willDecodeFromCache() ? diskCacheExecutor : getActiveSourceExecutor();
        executor.execute(decodeJob);
      
      }
    

    先尝试从内存加载,没找到就调用waitForExistingOrStartNewJob方法加载,这里构建了engineJob和decodeJob对象,接着调用start方法运行decodeJob的run方法:

     @Override
      public void run() {
       ...
        DataFetcher<?> localFetcher = currentFetcher;
        try {
          if (isCancelled) {
            notifyFailed();
            return;
          }
          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);
        }
      }
    
     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.
      }
    
    

    在DecodeJob的run方法中调用了runWrapped方法,然后得到了SourceGenerator,接着调用了getNextGenerator和runGenerators方法,最后是currentGenerator#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) {
        loadData.fetcher.loadData(
            helper.getPriority(),
            new DataCallback<Object>() {
              @Override
              public void onDataReady(@Nullable Object data) {
                if (isCurrentRequest(toStart)) {
                  onDataReadyInternal(toStart, data);
                }
              }
    
              @Override
              public void onLoadFailed(@NonNull Exception e) {
                if (isCurrentRequest(toStart)) {
                  onLoadFailedInternal(toStart, e);
                }
              }
            });
      }
    
    

    可以看到,startNext方法最终调用了startNextLoad方法,然后在startNextLoad中调用了loadData#fetcher#loadData方法,这里看一下HttpUrlFetcher的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());
    ...
    
    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);
        }
      }
    

    到这里才算真正看到了网络请求的代码。

    相关文章

      网友评论

          本文标题:Glide整体流程(仅到网络加载部分)

          本文链接:https://www.haomeiwen.com/subject/yywttktx.html