专栏首页包子的书架Glide4.8版本中,Glide是如何加载网络美女图片
原创

Glide4.8版本中,Glide是如何加载网络美女图片

扯会蛋

目前市场上主流的图片加载框架就是glide和fresco,个人觉得深入学习一款就可以,glide就是我的选择,在maven上可以看到Glide项目已经到4.11版本了,由于公司项目还是4.8版本,目前就以4.8版本为例,分析一下Glide是如何加载到网游美女图片。写这个文章主要是为了学习Glide的网络图片资源加载流程。

开始假正经

  • 从Glide.with() 方法开始
Glide的with方法.png

以参数是Context为栗子:

public static RequestManager with(@NonNull Context context) {
    return getRetriever(context).get(context);
}

getRetriever方法:

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

get方法就是用来创建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();

    // Right now we're only using this parser for HEIF images, which are only supported on OMR1+.

    // If we need this for other file types, we should consider removing this restriction.

    // Note that order here matters. We want to check the ExifInterface parser first for orientation

    // and then fall back to DefaultImageHeaderParser for other fields.

    if (Build.VERSION.SDK\_INT >= Build.VERSION\_CODES.O\_MR1) {

      registry.register(new ExifInterfaceImageHeaderParser());

    }

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

}

Glide构造函数主要做了几件事:

  1. 设置了图片的编码ARGB_8888
  2. 创建了Registry类,用于管理组件注册以扩展或替换Glide的默认加载,解码和编码逻辑。比如,HttpGlideUrlLoader网络图片的加载,当前我们也可以引入Glide OkHttp 3.x Integration中的OkHttpUrlLoader来替换Glide默认的下载实现。
  • 我们上面提到的with方法是用来管理生命周期的, 在with方法实现返回的是一个RequestManagerRetriever的对象, 当我们调用了 RequestManagerRetriever 的 get() 方法之后,会根据 Context 的类型调用 get() 的各个重载方法。
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);
}

我们以 Activity 为例。如下面的方法所示,当当前位于后台线程的时候,会使用 Application 的 Context 获取 RequestManager,否则会使用无 UI 的 Fragment 进行管理:

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,  null, isActivityVisible(activity));

    }
}

然后就调用到了 fragmentGet() 方法。这里我们从 RequestManagerFragment 中通过 getGlideLifecycle() 获取到了 Lifecycle 对象。Lifecycle 对象提供了一系列的、针对 Fragment 生命周期的方法。它们将会在 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);

    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;

}

然后,我们将该 Lifecycle 传入到 RequestManager中,以 RequestManagerFragment中的四个方法为例,RequestManager 会对 Lifecycle 进行监听,从而达到了对 Fragment 的生命周期进行监听的目的:

@Override
public void onDetach() {
    super.onDetach();
}

@Override
public void onStart() {
    super.onStart();
    lifecycle.onStart();
}

@Override
public void onStop() {
    super.onStop();
    lifecycle.onStop();
}

@Override
public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
}
  • RequestManager的load方法
load的不同类型参数方法.png

以load(String) 为例

public RequestBuilder<Drawable> load(@Nullable String string) {

    return asDrawable().load(string);

}

public RequestBuilder<Drawable> asDrawable() {

    return as(Drawable.class);

}

public <ResourceType> RequestBuilder<ResourceType> as(

      @NonNull Class<ResourceType> resourceClass) {

    return new RequestBuilder<>(glide, this, resourceClass, context);

}

上面代码我们可以发,load方法调用之后通过暗示asDrawable创建了RequestBuilder对象,方法返回的泛型类型是Drawable,其实还有其他的类型如下图,默认创建的都是Drawable;

创建RequestBuidler.png

RequestBuilder对象创建成功后,在调用load方法将图片路径赋值给model这个变量。

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

主要是设置RequestOptions,RequestOptions是图片加载的一些设置选项,包显示的显示的宽高,默认展位图,加载失败展位图等等。

public RequestBuilder<TranscodeType> apply(@NonNull RequestOptions requestOptions) {
    Preconditions.checkNotNull(requestOptions);
    this.requestOptions = getMutableOptions().apply(requestOptions);
    return this;
}
  • 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) {

      // 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. 重新设置了requestOptions;
  2. 创建viewTarget 通过` glideContext.buildImageViewTarget(view, transcodeClass), 由于transcodeClass是一个Drawable类型,所以glideContext.buildImageViewTarget(view, transcodeClass)创建了一个DrawableImageViewTarget对象。
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {

    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }


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

}

DrawableImageViewTarget的源码:

public class DrawableImageViewTarget extends ImageViewTarget<Drawable> {

  public DrawableImageViewTarget(ImageView view) {
    super(view);

  }



  @SuppressWarnings({"unused", "deprecation"})
  @Deprecated
  public DrawableImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }


  @Override
  protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
  }

}

再回到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 (!Preconditions.checkNotNull(previous).isRunning()) {

        previous.begin();

      }

      return target;

    }

    requestManager.clear(target);

    target.setRequest(request);

    requestManager.track(target, request);

    return target;
}

该方法创建一个Request对象,默认是SingleRequest对象,代码中的target就是前面创建的DrawableImageViewTarget对象,通过target对象获取request判断是存在上一个Request请求,存在的话,不是在执行中,复用原来的,如果原来没有,将这个request对象设置到target中,然后由requestManager.track(target, request); track方法启动跟踪request的请求

void track(@NonNull Target<?> target, @NonNull Request request) {

    targetTracker.track(target);

    requestTracker.runRequest(request);
  }

//RequestTracker中
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);

    }
}

开始执行begin方法,这边Request是接口,看到SingleRequest的begin方法:

public void begin() {

    assertNotCallingCallbacks();

    stateVerifier.throwIfRecycled();

    startTime = LogTime.getLogTime();

    if (model == null) {

      if (Util.isValidDimensions(overrideWidth, overrideHeight)) {

        width = overrideWidth;

        height = overrideHeight;

      }



      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 (status == Status.COMPLETE) {
      onResourceReady(resource, DataSource.MEMORY\_CACHE);
      return;
    }

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

    if (IS\_VERBOSE\_LOGGABLE) {
      logV("finished run method in " + LogTime.getElapsedMillis(startTime));
    }
}

如果图片的宽高已经确定就直接调用onSizeReady, 如果未确定宽高,走的target的getSize方法先确定宽高,然后还是调用SingleRequest的onSizeReady方法

void getSize(@NonNull SizeReadyCallback cb) {

      int currentWidth = getTargetWidth();

      int currentHeight = getTargetHeight();

      if (isViewStateAndSizeValid(currentWidth, currentHeight)) {

        cb.onSizeReady(currentWidth, currentHeight);

        return;

      }
      ....................
}

onSizeReady源码, 该方法中最关键的是调用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);
  }

Glide会首先从缓存中获取数据,如果没有的话再从网络获取。EngineJob与DecodeJob两个类非常重要,EngineJob主要进行线程之间的切换,DecodeJob主要是从本地或者网络获取数据的实现,来看EngineJob的start的实现。

public void start(DecodeJob<R> decodeJob) {

    this.decodeJob = decodeJob;

    GlideExecutor executor = decodeJob.willDecodeFromCache()

        ? diskCacheExecuto

        : getActiveSourceExecutor();

    executor.execute(decodeJob);

}

在这里就切换到下载图片线程,由于DecodeJob实现了Runnable接口,所以就来看run的方法

public void run() {

    GlideTrace.beginSectionFormat("DecodeJob#run(model=%s)", model);

    DataFetcher<?> localFetcher = currentFetcher;

    try {

      if (isCancelled) {

        notifyFailed();

        return;

      }

      runWrapped();

    } catch (Throwable t) {

      if (Log.isLoggable(TAG, Log.DEBUG)) {

        Log.d(TAG, "DecodeJob threw unexpectedly"

            + ", isCancelled: " + isCancelled

            + ", stage: " + stage, t);

      }



      if (stage != Stage.ENCODE) {
        throwables.add(t);
        notifyFailed();
      }

      if (!isCancelled) {

        throw t;

      }

    } finally {

      if (localFetcher != null) {

        localFetcher.cleanup();

      }

      GlideTrace.endSection();
    }
}

runWrapped是重点方法,这边根据state去获取哪个生成器,先判断是否有磁盘缓存数据,有的话拿到的是DataCacheGenerator,如果没有本地缓存,拿到的是SourceGenerator();

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 Stage getNextStage(Stage current) {

    switch (current) {

      case INITIALIZE:

        return diskCacheStrategy.decodeCachedResource()

            ? Stage.RESOURCE\_CACHE : getNextStage(Stage.RESOURCE\_CACHE);

      case RESOURCE\_CACHE:

        return diskCacheStrategy.decodeCachedData()

            ? Stage.DATA\_CACHE : getNextStage(Stage.DATA\_CACHE);

      case DATA\_CACHE:

        // Skip loading from source if the user opted to only retrieve the resource from cache.

        return onlyRetrieveFromCache ? Stage.FINISHED : Stage.SOURCE;

      case SOURCE:

      case FINISHED:

        return Stage.FINISHED;

      default:

        throw new IllegalArgumentException("Unrecognized stage: " + current);

    }
  }



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.
  }

runGenerators调用了SourceGenerator的startNext方法,当网络返回数据时则dataToCache不为null,就会存储数据到本地。否则就从网络获取数据。

public boolean startNext() {

    if (dataToCache != null) {

      Object data = dataToCache;

      dataToCache = null;

      cacheData(data);   //注意这边,当网络数据加载回来,会重新走一下startNext(),  这个时候//dataToCache不为空

    }


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

      }

    }
    return started;
}

这里的loadData的实现是MultiModelLoader,fetcher的实现是MultiFetcher,然后调用loadData方法来加载数据

public void loadData(
        @NonNull Priority priority, @NonNull DataCallback<? super Data> callback) {
      ...

     //这里的Fetcher是可以定制的,默认实现是HttpUrlFetche
     fetchers.get(currentIndex).loadData(priority, this);
}

如果是默认的HttpUrlFetcher的load方法:(采用的网络加载是HttpURLConnection)

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

      }
    }
  }

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 {

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

    urlConnection.setInstanceFollowRedirects(false);
    urlConnection.connect();
    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);

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

如果是引用okHttp的Glide OkHttp 3.x Integration的依赖,里面对应的fetcher是HttpUrlFetcher源码是:(采用的网络请求是Okhttp)

Request.Builder requestBuilder = new Request.Builder().url(url.toStringUrl());
for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
  String key = headerEntry.getKey();
  requestBuilder.addHeader(key, headerEntry.getValue());
}

Request request = requestBuilder.build();
this.callback = callback;
call = client.newCall(request);
call.enqueue(this);

图片下载完成后,将数据通过callback.onDataReady(result);返回,这个callback其实就是SourceGenerator

SourceGenerator的onDateReady(result)代码如下:

public void onDataReady(Object data) {

    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();

    if (data != null && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {

      dataToCache = data;  //在这边给dataToCache赋值了 ,这个时候dataToCache 不为空



      cb.reschedule();

    } else {

      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,

          loadData.fetcher.getDataSource(), originalKey);

    }

  }

SourceGenerator中,在上面方法数据加载成功后回来给dataToCache赋值了 ,这个时候dataToCache 不为空这个cb其实就是DecodeJob。(如果DiskCacheStrategy设置为NONE什么都不保存,会直接回调出去)

public void reschedule() {

    runReason = RunReason.SWITCH\_TO\_SOURCE\_SERVICE;
    callback.reschedule(this);

}

这个callback就是EngineJob,再来看它的reschedule方法, 这个callback在一开始构建DecodeJob的时候穿的就是EngineJob对象,EngineJob实现了DecodeJob.Callback的接口

public void reschedule(DecodeJob<?> job) {
    getActiveSourceExecutor().execute(job);
}

这里是切换到缓存数据线程,那么就会执行DecodeJob的run方法,前面介绍过,在该方法内执行的是runWrapped方法,由于前面将runReason的值修改为SWITCH_TO_SOURCE_SERVICE,所以就会直接执行runGenerators然后再次调用SourceGenerator的startNext方法,这个时候dataToCache 不为空,走cacheData方法,切换到DataCacheGenerator;前面在介绍该方法时,说过如果有数据就写入缓存,这时候就会将数据写入缓存并调用DataCacheGenerator的startNext方法。

//SourceGenerator的startNext
@Override
public boolean startNext() {

    if (dataToCache != null) {

      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);

    }
    ............
}

//cacheData 将SourceGenerator切换到DataCacheGenerator,去做数据缓存

private void cacheData(Object dataToCache) {

    long startTime = LogTime.getLogTime();

    try {

      Encoder<Object> encoder = helper.getSourceEncoder(dataToCache);

      DataCacheWriter<Object> writer =

          new DataCacheWriter<>(encoder, dataToCache, helper.getOptions());

      originalKey = new DataCacheKey(loadData.sourceKey, helper.getSignature());

      helper.getDiskCache().put(originalKey, writer);

      if (Log.isLoggable(TAG, Log.VERBOSE)) {

        Log.v(TAG, "Finished encoding source to cache"

            + ", key: " + originalKey

            + ", data: " + dataToCache

            + ", encoder: " + encode

            + ", duration: " + LogTime.getElapsedMillis(startTime));

      }

    } finally {

      loadData.fetcher.cleanup();

    }

    sourceCacheGenerator =
      new DataCacheGenerator(Collections.singletonList(loadData.sourceKey), helper, this);
  }

//DataCacheGenerator的startNext方法
public boolean startNext() {

    ...

    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的实现类是ByteBufferFileLoade
        //loadData.fetcher的实现类是ByteBufferFetche
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

这里就调用了ByteBufferFetcher的loadData方法

@Override
public void loadData(@NonNull Priority priority,
        @NonNull DataCallback<? super ByteBuffer> callback) {

      ByteBuffer result;
      try {
        //从缓存从获取数据
        result = ByteBufferUtil.fromFile(file);
      } catch (IOException e) {

        ...
        //数据获取失败
        callback.onLoadFailed(e);
        return;
      }

      //数据获取成功
      callback.onDataReady(result);
    }

这里的callback就是DataCacheGenerato

@Override
public void onDataReady(Object data) {
    cb.onDataFetcherReady(sourceKey, data, loadData.fetcher, DataSource.DATA\_DISK\_CACHE, sourceKey);

}

这里的cb就是SourceGenerator

public void onDataReady(Object data) {

    DiskCacheStrategy diskCacheStrategy = helper.getDiskCacheStrategy();
    if (data != null 
    && diskCacheStrategy.isDataCacheable(loadData.fetcher.getDataSource())) {

      dataToCache = data;
      cb.reschedule();
    } else {
      cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,
          loadData.fetcher.getDataSource(), originalKey);
    }
}

前面说过SourceGenerator中的cb就是DecodeJob

@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
      DataSource dataSource, Key attemptedKey) {

    this.currentSourceKey = sourceKey;
    this.currentData = data;
    this.currentFetcher = fetcher;
    this.currentDataSource = dataSource;
    this.currentAttemptingKey = attemptedKey;
    if (Thread.currentThread() != currentThread) {

      runReason = RunReason.DECODE\_DATA;
      callback.reschedule(this);

    } else {
      GlideTrace.beginSection("DecodeJob.decodeFromRetrievedData");
      try {
        decodeFromRetrievedData();
      } finally {
        GlideTrace.endSection();
      }
    }
}

private void decodeFromRetrievedData() {

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
      logWithTimeAndKey("Retrieved data", startFetchTime,
          "data: " + currentData
              + ", cache key: " + currentSourceKey
              + ", fetcher: " + currentFetcher);
    }

    Resource<R> resource = null;
    try {
      resource = decodeFromData(currentFetcher, currentData, currentDataSource);
    } catch (GlideException e) {
      e.setLoggingDetails(currentAttemptingKey, currentDataSource);
      throwables.add(e);
    }

    if (resource != null) {
      notifyEncodeAndRelease(resource, currentDataSource);
    } else {
      runGenerators();
    }
  }


private void notifyEncodeAndRelease(Resource<R> resource, DataSource dataSource) {

    if (resource instanceof Initializable) {
      ((Initializable) resource).initialize();
    }


    Resource<R> result = resource;
    LockedResource<R> lockedResource = null;
    if (deferredEncodeManager.hasResourceToEncode()) {
      lockedResource = LockedResource.obtain(resource);
      result = lockedResource;
    }

    notifyComplete(result, dataSource);

    stage = Stage.ENCODE;
    try {
      if (deferredEncodeManager.hasResourceToEncode()) {
        deferredEncodeManager.encode(diskCacheProvider, options);
      }
    } finally {
      if (lockedResource != null) {
        lockedResource.unlock();
      }
    }

    onEncodeComplete();
}


private void notifyComplete(Resource<R> resource, DataSource dataSource) {
    setNotifiedOrThrow();
    callback.onResourceReady(resource, dataSource);
}

notifyComplete方法中的callback就是EncodeJob,所以看EncodeJob的onResourceReady:

@Override
public void onResourceReady(Resource<R> resource, DataSource dataSource) {

    this.resource = resource;
    this.dataSource = dataSource;
    MAIN\_THREAD\_HANDLER.obtainMessage(MSG\_COMPLETE, this).sendToTarget();
}

//处理handler的消息
public boolean handleMessage(Message message) {

      EngineJob<?> job = (EngineJob<?>) message.obj;
      switch (message.what) {

        case MSG\_COMPLETE:

        //切换到主线程,处理结果

          job.handleResultOnMainThread();

          break;

   .......

 }
}

void handleResultOnMainThread() {

    ..................

    for (int i = 0, size = cbs.size(); i < size; i++) {
      ResourceCallback cb = cbs.get(i);
      if (!isInIgnoredCallbacks(cb)) {
        engineResource.acquire();
        cb.onResourceReady(engineResource, dataSource);
      }
    }
    ..................
}

在EngineJob的onResourceReady中切换回主线程。并将数据传给cb的onResourceReady方法,这里的cb是SingleRequest。

public void onResourceReady(Resource<?> resource, DataSource dataSource) {
    ...

    onResourceReady((Resource<R>) resource, (R) received, dataSource);
}

private void onResourceReady(Resource<R> resource, R result, DataSource dataSource) {
    ...
    try {
      ...

      if (!anyListenerHandledUpdatingTarget) {

        Transition<? super R> animation =

            animationFactory.build(dataSource, isFirstResource);

        //展示图片

        target.onResourceReady(result, animation);

      }

    } finally {
      isCallingCallbacks = false;
    }
    notifyLoadSuccess();
}

target的是接口,ImageViewTarget抽象类,实现了target的接口, 并且实现了onResourceReady方法

@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {

    if (transition == null || !transition.transition(resource, this)) {
      setResourceInternal(resource);
    } else {
      maybeUpdateAnimatable(resource);
    }
}

private void setResourceInternal(@Nullable Z resource) {

    setResource(resource);
    maybeUpdateAnimatable(resource);
}

protected abstract void setResource(@Nullable Z resource);

DrawableImageViewTarget继承了抽象类ImageViewTarget, 最后在DrawableImageViewTarget的中实现setResource的抽象方法

@Override
protected void setResource(@Nullable Drawable resource) {
    view.setImageDrawable(resource);
}

到此,就整个Glide的网络加载图片的流程就走完了。

下面是整个加载过程的时序图:

Glide的网络加载的整个时序.png

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++ 指针和句柄的区别

    指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址,指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。 句柄实际上是一种指向某种...

    包子388321
  • Assets和res目录的区别

    立一个Android项目后会产生assets与res的两个文件夹,理论上他们都是存放资源的文件夹,那么他们到底有什么区别呢?

    包子388321
  • MFC创建对话框

    包子388321
  • SpringMVC类图关系

    package org.springframework.web.servlet {

    zhangheng
  • [第22期] React Conf 2019 样式新方案

    会上讲了很多激动人心的技术点,这里先介绍一些我比较感兴趣的点, 希望对大家有所启发。

    用户6900878
  • 使用Nginx代理restful实现SSL链路加密

    1 目标说明 1.1 调研目的 本次调研主要为了解决两个问题: 不需要对restful的web容器做任何配置,实现对restful链路进行加密; 方便restf...

    囚兔
  • JFinal极速开发框架使用笔记(三) 分析Model和ActiveRecord

    JFinal框架的一些新发现的用法: 在JFinal框架中,实体类并不需要设置属性,更不需要配置getset方法就可以很方便的操作数据库,如果需要设置或者获取属...

    二十三年蝉
  • VS2017中运行MySQL的存储过程

    zls365
  • Python MRO

    对于Python中的多继承情况,运行时在搜索对象的属性或方法时,需要遵循一定的顺序规则,这个规则称为:Method Resolution Order (MRO)...

    雪飞鸿
  • 新建SpringBoot项目pom文件第一行报错 Unknown error

    由于代码审计的原因,原来的SpringBoot 2.0.1.RELEASE 需要更新到 2.1.6.RELEASE,替换了版本号以后,功能正常,但eclipse...

    pollyduan

扫码关注云+社区

领取腾讯云代金券