美文网首页
深入理解Glide(一):加载图片流程分析

深入理解Glide(一):加载图片流程分析

作者: melodylzl | 来源:发表于2018-11-30 16:31 被阅读0次
    Glide的使用
    GlideApp.with(this).load("http://goo.gl/gEgYUd").into(imageView);
    
    流程分析
    with()
     public static RequestManager with(@NonNull Context context) {
        return getRetriever(context).get(context);
      }
    

    看到with()方法的入参是一个Context,并把这个context传进getRetriever()方法,点进去看到:

    @NonNull
      private static RequestManagerRetriever getRetriever(@Nullable Context context) {
        ....
        return Glide.get(context).getRequestManagerRetriever();
      }
    

    再进去

    @NonNull
      public static Glide get(@NonNull Context context) {
        if (glide == null) {
          synchronized (Glide.class) {
            if (glide == null) {
              checkAndInitializeGlide(context);
            }
          }
        }
    
        return glide;
      }
    

    可以看到这里是在进行Glide初始化操作,并且是一个单例形式,具体怎么初始化Glide,再进去可看到以下代码:

    private static void initializeGlide(@NonNull Context context, @NonNull GlideBuilder builder) {
        ....
        Glide glide = builder.build(applicationContext);
        ...
        Glide.glide = glide;
      }
    

    这个方法省略了一些代码,可以看到是通过GlideBilder这个类来构造glide实例的;

    Glide build(@NonNull Context context) {
        ...
    
        RequestManagerRetriever requestManagerRetriever =
            new RequestManagerRetriever(requestManagerFactory);
    
        return new Glide(
            context,
            engine,
            memoryCache,
            bitmapPool,
            arrayPool,
            requestManagerRetriever,
            connectivityMonitorFactory,
            logLevel,
            defaultRequestOptions.lock(),
            defaultTransitionOptions);
      }
    

    这里我们看到有个requestManagerRetriever初始化了,后面我们会用到,再看到是new了一个Glide对象出来。具体看下Glide初始化了什么:

    Glide(
          @NonNull Context context,
          @NonNull Engine engine,
          @NonNull MemoryCache memoryCache,
          @NonNull BitmapPool bitmapPool,
          @NonNull ArrayPool arrayPool,
          @NonNull RequestManagerRetriever requestManagerRetriever,
          @NonNull ConnectivityMonitorFactory connectivityMonitorFactory,
          int logLevel,
          @NonNull RequestOptions defaultRequestOptions,
          @NonNull Map<Class<?>, TransitionOptions<?, ?>> defaultTransitionOptions) {
        this.engine = engine;
        this.bitmapPool = bitmapPool;
        this.arrayPool = arrayPool;
        this.memoryCache = memoryCache;
        this.requestManagerRetriever = requestManagerRetriever;
        this.connectivityMonitorFactory = connectivityMonitorFactory;
    
        DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);
        bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);
    
        final Resources resources = context.getResources();
    
        registry = new Registry();  // 1
        registry.register(new DefaultImageHeaderParser());
    
        Downsampler downsampler = new Downsampler(registry.getImageHeaderParsers(),
            resources.getDisplayMetrics(), bitmapPool, arrayPool);
        ByteBufferGifDecoder byteBufferGifDecoder =
            new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), bitmapPool, arrayPool);
        ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
            VideoDecoder.parcel(bitmapPool);
        ByteBufferBitmapDecoder byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
        StreamBitmapDecoder streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
        ResourceDrawableDecoder resourceDrawableDecoder =
            new ResourceDrawableDecoder(context);
        ResourceLoader.StreamFactory resourceLoaderStreamFactory =
            new ResourceLoader.StreamFactory(resources);
        ResourceLoader.UriFactory resourceLoaderUriFactory =
            new ResourceLoader.UriFactory(resources);
        ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
            new ResourceLoader.FileDescriptorFactory(resources);
        ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
            new ResourceLoader.AssetFileDescriptorFactory(resources);
        BitmapEncoder bitmapEncoder = new BitmapEncoder(arrayPool);
    
        BitmapBytesTranscoder bitmapBytesTranscoder = new BitmapBytesTranscoder();
        GifDrawableBytesTranscoder gifDrawableBytesTranscoder = new GifDrawableBytesTranscoder();
    
        ContentResolver contentResolver = context.getContentResolver();
    
        registry
            .append(ByteBuffer.class, new ByteBufferEncoder())
            .append(InputStream.class, new StreamEncoder(arrayPool))
            /* Bitmaps */
            .append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
            .append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
            .append(
                Registry.BUCKET_BITMAP,
                ParcelFileDescriptor.class,
                Bitmap.class,
                parcelFileDescriptorVideoDecoder)
            .append(
                Registry.BUCKET_BITMAP,
                AssetFileDescriptor.class,
                Bitmap.class,
                VideoDecoder.asset(bitmapPool))
            .append(Bitmap.class, Bitmap.class, UnitModelLoader.Factory.<Bitmap>getInstance())
            .append(
                Registry.BUCKET_BITMAP, Bitmap.class, Bitmap.class, new UnitBitmapDecoder())
            .append(Bitmap.class, bitmapEncoder)
            /* BitmapDrawables */
            .append(
                Registry.BUCKET_BITMAP_DRAWABLE,
                ByteBuffer.class,
                BitmapDrawable.class,
                new BitmapDrawableDecoder<>(resources, byteBufferBitmapDecoder))
            .append(
                Registry.BUCKET_BITMAP_DRAWABLE,
                InputStream.class,
                BitmapDrawable.class,
                new BitmapDrawableDecoder<>(resources, streamBitmapDecoder))
            .append(
                Registry.BUCKET_BITMAP_DRAWABLE,
                ParcelFileDescriptor.class,
                BitmapDrawable.class,
                new BitmapDrawableDecoder<>(resources, parcelFileDescriptorVideoDecoder))
            .append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, bitmapEncoder))
            /* GIFs */
            .append(
                Registry.BUCKET_GIF,
                InputStream.class,
                GifDrawable.class,
                new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))
            .append(Registry.BUCKET_GIF, ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
            .append(GifDrawable.class, new GifDrawableEncoder())
            /* GIF Frames */
            // Compilation with Gradle requires the type to be specified for UnitModelLoader here.
            .append(
                GifDecoder.class, GifDecoder.class, UnitModelLoader.Factory.<GifDecoder>getInstance())
            .append(
                Registry.BUCKET_BITMAP,
                GifDecoder.class,
                Bitmap.class,
                new GifFrameResourceDecoder(bitmapPool))
            /* Drawables */
            .append(Uri.class, Drawable.class, resourceDrawableDecoder)
            .append(
                Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitmapPool))
            /* Files */
            .register(new ByteBufferRewinder.Factory())
            .append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
            .append(File.class, InputStream.class, new FileLoader.StreamFactory())
            .append(File.class, File.class, new FileDecoder())
            .append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
            // Compilation with Gradle requires the type to be specified for UnitModelLoader here.
            .append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
            /* Models */
            .register(new InputStreamRewinder.Factory(arrayPool))
            .append(int.class, InputStream.class, resourceLoaderStreamFactory)
            .append(
                int.class,
                ParcelFileDescriptor.class,
                resourceLoaderFileDescriptorFactory)
            .append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
            .append(
                Integer.class,
                ParcelFileDescriptor.class,
                resourceLoaderFileDescriptorFactory)
            .append(Integer.class, Uri.class, resourceLoaderUriFactory)
            .append(
                int.class,
                AssetFileDescriptor.class,
                resourceLoaderAssetFileDescriptorFactory)
            .append(
                Integer.class,
                AssetFileDescriptor.class,
                resourceLoaderAssetFileDescriptorFactory)
            .append(int.class, Uri.class, resourceLoaderUriFactory)
            .append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
            .append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
            .append(String.class, InputStream.class, new StringLoader.StreamFactory())
            .append(String.class, ParcelFileDescriptor.class, new StringLoader.FileDescriptorFactory())
            .append(
                String.class, AssetFileDescriptor.class, new StringLoader.AssetFileDescriptorFactory())
            .append(Uri.class, InputStream.class, new HttpUriLoader.Factory())
            .append(Uri.class, InputStream.class, new AssetUriLoader.StreamFactory(context.getAssets()))
            .append(
                Uri.class,
                ParcelFileDescriptor.class,
                new AssetUriLoader.FileDescriptorFactory(context.getAssets()))
            .append(Uri.class, InputStream.class, new MediaStoreImageThumbLoader.Factory(context))
            .append(Uri.class, InputStream.class, new MediaStoreVideoThumbLoader.Factory(context))
            .append(
                Uri.class,
                InputStream.class,
                new UriLoader.StreamFactory(contentResolver))
            .append(
                Uri.class,
                ParcelFileDescriptor.class,
                 new UriLoader.FileDescriptorFactory(contentResolver))
            .append(
                Uri.class,
                AssetFileDescriptor.class,
                new UriLoader.AssetFileDescriptorFactory(contentResolver))
            .append(Uri.class, InputStream.class, new UrlUriLoader.StreamFactory())
            .append(URL.class, InputStream.class, new UrlLoader.StreamFactory())
            .append(Uri.class, File.class, new MediaStoreFileLoader.Factory(context))
            .append(GlideUrl.class, InputStream.class, new HttpGlideUrlLoader.Factory())
            .append(byte[].class, ByteBuffer.class, new ByteArrayLoader.ByteBufferFactory())
            .append(byte[].class, InputStream.class, new ByteArrayLoader.StreamFactory())
            .append(Uri.class, Uri.class, UnitModelLoader.Factory.<Uri>getInstance())
            .append(Drawable.class, Drawable.class, UnitModelLoader.Factory.<Drawable>getInstance())
            .append(Drawable.class, Drawable.class, new UnitDrawableDecoder())
            /* Transcoders */
            .register(
                Bitmap.class,
                BitmapDrawable.class,
                new BitmapDrawableTranscoder(resources))
            .register(Bitmap.class, byte[].class, bitmapBytesTranscoder)
            .register(
                Drawable.class,
                byte[].class,
                new DrawableBytesTranscoder(
                    bitmapPool, bitmapBytesTranscoder, gifDrawableBytesTranscoder))
            .register(GifDrawable.class, byte[].class, gifDrawableBytesTranscoder);
    
        ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();  
        glideContext =
            new GlideContext(
                context,
                arrayPool,
                registry,
                imageViewTargetFactory,
                defaultRequestOptions,
                defaultTransitionOptions,
                engine,
                logLevel);  
      }
    

    1处是初始化了一个注册器,这个注册器的作用后面在请求网络数据和解码图片数据时会用到,我们看到这个注册器在后面注册了很多操作。
    回到这里:

    @NonNull
      private static RequestManagerRetriever getRetriever(@Nullable Context context) {
        ....
        return Glide.get(context).getRequestManagerRetriever();
      }
    

    我们知道在Glide初始化过程中生成了一个RequestManagerRetriever对象,我们调用这个对象的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) {
            return get(((ContextWrapper) context).getBaseContext());
          }
        }
    
        return getApplicationManager(context);
      }
    

    这里主要是根据传入的上下文Context分为FragmentActivity、Activity还是ApplicationContext这三种进行处理,并且返回对应的RequestManager。具体作了什么样的处理,我们可以进去看到:
    以Activity为例

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

    看到1处,是拿到了activity的Fragment的管理类并且还在进行其他操作:

    private RequestManager fragmentGet(@NonNull Context context,
          @NonNull android.app.FragmentManager fm,
          @Nullable android.app.Fragment parentHint,
          boolean isParentVisible) {
        RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible); // 1
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
          // TODO(b/27524013): Factor out this Glide.get() call.
          Glide glide = Glide.get(context);
          requestManager =
              factory.build(
                  glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
          current.setRequestManager(requestManager);
        }
        return requestManager;
      }
    

    看到1处是获得了一个RequestManagerFragment 的实例,RequestManagerFragment是Fragment的子类。
    再看getRequestManagerFragment():

    @SuppressWarnings("deprecation")
      @NonNull
      private RequestManagerFragment getRequestManagerFragment(
          @NonNull final android.app.FragmentManager fm,
          @Nullable android.app.Fragment parentHint,
          boolean isParentVisible) {
        RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
        if (current == null) {
          current = pendingRequestManagerFragments.get(fm);
          if (current == null) {
            current = new RequestManagerFragment();
            current.setParentFragmentHint(parentHint);
            if (isParentVisible) {
              current.getGlideLifecycle().onStart();
            }
            pendingRequestManagerFragments.put(fm, current);
            fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
            handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
          }
        }
        return current;
      }
    

    可以看到这里的处理是先判断Activity有没有添加过RequestManagerFragment这个fragment,如果没有,就给activity添加这个fragment,glide可以通过这个添加的fragment监听到Activity的生命周期变化,并且初始化一个requestManger对象返回。
    至此,整个with()方法的流程都走完。总结一下,with()方法主要是做了Glide的初始化操作(如果之前还没初始化的话),并且根据传入的context判断是否fragmentActivity,还是activity,还是applicationContext,如果是前两者,则要添加glide自定义的fragment以监听到对应activity生命周期的变化,并且最后返回requestManger。

    load()
    @NonNull
      @CheckResult
      @Override
      public RequestBuilder<Drawable> load(@Nullable String string) {
        return asDrawable().load(string);
      }
    

    asDrawable()最终会返回一个requestBuidler对象

    @NonNull
      @CheckResult
      public <ResourceType> RequestBuilder<ResourceType> as(
          @NonNull Class<ResourceType> resourceClass) {
        return new RequestBuilder<>(glide, this, resourceClass, context);
      }
    

    然后rqeuestBuilder调用load()方法设置model:

    @NonNull
      private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
        this.model = model;
        isModelSet = true;
        return this;
      }
    

    load()方法主要是初始了requestBuilder对象,并且设置好要加载的数据源。

    into()

    into()是glide最关键的方法,流程也相对复杂,要边画图边做记录,多看几遍才能搞清逻辑。

    @NonNull
      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) {
          // 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); // 1
      }
    

    可以看到1处是生成了一个ImageViewTarget,默认是DrawableImageViewTarget,当然也可以设置成其他类,并且再传进深一层的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);  // 1
    
        Request previous = target.getRequest();  // 2
        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);  // 3
    
        return target;
      }
    

    这段代码主要看1处,构造我们真正要请求加载图片的request。2处是判断是否存在以前的request,如果有,再进行其他处理,这里我们暂时忽略。3处点击进去可以看见其中一处代码是执行了rquest的begin()方法。

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

    Request只是一个接口,我们要看到底是构建了一个怎样的request,因此我们从上面的1处代码点击进去:

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

    再点进去:

    private Request buildRequestRecursive(
          Target<TranscodeType> target,
          @Nullable RequestListener<TranscodeType> targetListener,
          @Nullable RequestCoordinator parentCoordinator,
          TransitionOptions<?, ? super TranscodeType> transitionOptions,
          Priority priority,
          int overrideWidth,
          int overrideHeight,
          RequestOptions requestOptions) {
    
        // Build the ErrorRequestCoordinator first if necessary so we can update parentCoordinator.
        ErrorRequestCoordinator errorRequestCoordinator = null;
        if (errorBuilder != null) {
          errorRequestCoordinator = new ErrorRequestCoordinator(parentCoordinator);
          parentCoordinator = errorRequestCoordinator;
        }
    
        Request mainRequest =
            buildThumbnailRequestRecursive(
                target,
                targetListener,
                parentCoordinator,
                transitionOptions,
                priority,
                overrideWidth,
                overrideHeight,
                requestOptions);   // 1
    
        if (errorRequestCoordinator == null) {
          return mainRequest;
        }
    
        int errorOverrideWidth = errorBuilder.requestOptions.getOverrideWidth();
        int errorOverrideHeight = errorBuilder.requestOptions.getOverrideHeight();
        if (Util.isValidDimensions(overrideWidth, overrideHeight)
            && !errorBuilder.requestOptions.isValidOverride()) {
          errorOverrideWidth = requestOptions.getOverrideWidth();
          errorOverrideHeight = requestOptions.getOverrideHeight();
        }
    
        Request errorRequest = errorBuilder.buildRequestRecursive(
            target,
            targetListener,
            errorRequestCoordinator,
            errorBuilder.transitionOptions,
            errorBuilder.requestOptions.getPriority(),
            errorOverrideWidth,
            errorOverrideHeight,
            errorBuilder.requestOptions);
        errorRequestCoordinator.setRequests(mainRequest, errorRequest);
        return errorRequestCoordinator;
      }
    

    这个方法很长,实际上我们只需要看到1处,再点进去看:

    private Request buildThumbnailRequestRecursive(
          Target<TranscodeType> target,
          RequestListener<TranscodeType> targetListener,
          @Nullable RequestCoordinator parentCoordinator,
          TransitionOptions<?, ? super TranscodeType> transitionOptions,
          Priority priority,
          int overrideWidth,
          int overrideHeight,
          RequestOptions requestOptions) {
        if (thumbnailBuilder != null) {
          ....
        } else if (thumbSizeMultiplier != null) {
             ...
        } else {
          // Base case: no thumbnail.
          return obtainRequest(
              target,
              targetListener,
              requestOptions,
              parentCoordinator,
              transitionOptions,
              priority,
              overrideWidth,
              overrideHeight);
        }
      }
    

    这里省略了部分代码,省略的部分主要是缩略图的处理,我们只需要看到最后的else的代码,再进去:

    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,我们看SingleRequest的begin()方法:

    @Override
      public void begin() {
        assertNotCallingCallbacks();
        stateVerifier.throwIfRecycled();
        startTime = LogTime.getLogTime();
        if (model == null) {
          if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
            width = overrideWidth;
            height = overrideHeight;
          }
          // Only log at more verbose log levels if the user has set a fallback drawable, because
          // fallback Drawables indicate the user expects null models occasionally.
          int logLevel = getFallbackDrawable() == null ? Log.WARN : Log.DEBUG;
          onLoadFailed(new GlideException("Received null model"), logLevel);
          return;
        }
    
        if (status == Status.RUNNING) {
          throw new IllegalArgumentException("Cannot restart a running request");
        }
    
        // If we're restarted after we're complete (usually via something like a notifyDataSetChanged
        // that starts an identical request into the same Target or View), we can simply use the
        // resource and size we retrieved the last time around and skip obtaining a new size, starting a
        // new load etc. This does mean that users who want to restart a load because they expect that
        // the view size has changed will need to explicitly clear the View or Target before starting
        // the new load.
        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); // 1
        } else {
          target.getSize(this);
        }
    
        if ((status == Status.RUNNING || status == Status.WAITING_FOR_SIZE)
            && canNotifyStatusChanged()) {
          target.onLoadStarted(getPlaceholderDrawable());
        }
        if (IS_VERBOSE_LOGGABLE) {
          logV("finished run method in " + LogTime.getElapsedMillis(startTime));
        }
      }
    

    看1处,当获得合适的宽度和高度后,会进入到OnSizeReady()方法内部:

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

    看到engin.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);  // 1
    
        DecodeJob<R> decodeJob =
            decodeJobFactory.build(
                glideContext,
                model,
                key,
                signature,
                width,
                height,
                resourceClass,
                transcodeClass,
                priority,
                diskCacheStrategy,
                transformations,
                isTransformationRequired,
                isScaleOnlyOrNoTransform,
                onlyRetrieveFromCache,
                options,
                engineJob);  // 2
    
        jobs.put(key, engineJob);
    
        engineJob.addCallback(cb);
        engineJob.start(decodeJob);  // 3
    
        if (VERBOSE_IS_LOGGABLE) {
          logWithTimeAndKey("Started new load", startTime, key);
        }
        return new LoadStatus(cb, engineJob);
      }
    

    1处和2处分别初始化了EngineJob和DecodeJob这两个对象,DecodeJob是一个Runnable对象,3处调用线程池执行DecodeJob,我们看DecodeJob的run()方法,run()方法里再调了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);
        }
      }
    

    这个方法根据不同的runReason执行不同的的操作:INITIALIZE,初始化的操作;SWITCH_TO_SOURCE_SERVICE:转到获取源数据的线程;DECODE_DATA:将获取到的源数据解码。一开始是进入到INITIALIZE条件里:getNextStage()主要是获取下一步操作的状态,而getNextGenerator()是根据stage分发不同的Generator来处理不同的操作,具体的Generator有:

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

    如果对于一个没缓存过的图片,我们最后会用到SourceGenerator,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.
      }
    

    实际上是调用了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;
            loadData.fetcher.loadData(helper.getPriority(), this); // 1
          }
        }
        return started;
      }
    

    我们看到1处这行码,实际上这里的fetcher是HttpUrlFetcher这个类,为什么是这个类,接下来的文章会继续分析,这里我们先知道是这个类,那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());
          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));
          }
        }
      }
    

    进去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);
        }
      }
    

    可以看到这里才是Glide真正获取图片数据的地方,是通过HttpUrlConnection请求来获取图片的。
    获取完图片后,怎样把获取到的图片数据显示到控件上呢,主要是通过之前设置的回调接口一步一步的回调。具体不分析了,可以看下图:


    Glide加载图片流程时序图.jpg
    小结

    1、Glide是通过添加自定义的fragment来监听要加载图片控件所在的activity的生命周期变化的。
    2、Glide请求网络图片使用的是HttpUrlConnection。

    相关文章

      网友评论

          本文标题:深入理解Glide(一):加载图片流程分析

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