Glide作为最近几年比较火热的图片加载结构,简直广泛分布于各类App中,最近这一年都在用Coil,反而很少在用Glide了,之后会进行Coil的源码分析,由于Coil悉数用Kotlin完成的,许多同伴或许不太了解,因而先吃从Glide说起,假如对Coil感兴趣的同伴,重视后续的源码分析。

1 Glide三大主线

假如运用过Glide的同伴们应该了解,在运用Glide的链式调用时,首要分为3大主线:with、load、into

Glide.with(this).load("url").into(iv_image)

就可以轻松完结一张图片的加载,但是其间的原理却是非常杂乱的,并且Glide源码反常巨大,所以在分析源码时一定要挑要点检查。

1.1 with主线

首要咱们看一下with办法,这个办法是Glide中的一个重载办法,在with办法中可以传入Activity或许Fragment

public static RequestManager with(@NonNull FragmentActivity activity) {
  return getRetriever(activity).get(activity);
}

既然可以传入Activity和Fragment,相比是要与其生命周期做关联,后续咱们会详细分析。这儿想说一个之前项目中的线上事故,问题如下:

Glide.with(requireContext()).load("url").into(iv_image)

之前同伴在运用Glide的时分,或许是由于写的太快并且编译器并没有报错,在调用with办法的时分传入了Context上下文,所以Glide没有与页面生命周期绑定,导致页面消失后,恳求仍然在后台运行,终究刷新页面时找不到加载的容器直接崩溃,所以with办法一定是与生命周期有关,闲话不多说持续看源码。

private static RequestManagerRetriever getRetriever(@Nullable Context context) {
  // Context could be null for other reasons (ie the user passes in null), but in practice it will
  // only occur due to errors with the Fragment lifecycle.
  Preconditions.checkNotNull(
      context,
      "You cannot start a load on a not yet attached View or a Fragment where getActivity() "
          + "returns null (which usually occurs when getActivity() is called before the Fragment "
          + "is attached or after the Fragment is destroyed).");
          /** Glide的get办法终究意图便是创立Glide目标*/
  return Glide.get(context).getRequestManagerRetriever();
}

1.1.1 Glide的创立

咱们可以看到,getRetriever办法终究返回了一个RequestManagerRetriever目标,其间咱们重视以下这段代码

Glide.get(context).getRequestManagerRetriever();

其间get办法是Glide中的一个单例,选用双检锁的方法,终究返回了一个Glide目标,也便是在zcheckAndInitializeGlid这个办法中

// Double checked locking is safe here.
@SuppressWarnings("GuardedBy")
public static Glide get(@NonNull Context context) {
  if (glide == null) {
    GeneratedAppGlideModule annotationGeneratedModule =
        getAnnotationGeneratedGlideModules(context.getApplicationContext());
    synchronized (Glide.class) {
      if (glide == null) {
        checkAndInitializeGlide(context, annotationGeneratedModule);
      }
    }
  }
  return glide;
}

咱们顺着这个办法一直找下去,终究调用了Glide的build办法,创立了一个Glide目标,咱们看到在Glide的构造办法中传入了一些像缓存、Bitmap池相关的目标,后边咱们会持续分析。

@NonNull
Glide build(
    @NonNull Context context,
    List<GlideModule> manifestModules,
    AppGlideModule annotationGeneratedGlideModule) {
  if (sourceExecutor == null) {
    sourceExecutor = GlideExecutor.newSourceExecutor();
  }
  if (diskCacheExecutor == null) {
    diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();
  }
  if (animationExecutor == null) {
    animationExecutor = GlideExecutor.newAnimationExecutor();
  }
  if (memorySizeCalculator == null) {
    memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();
  }
  if (connectivityMonitorFactory == null) {
    connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();
  }
  if (bitmapPool == null) {
    int size = memorySizeCalculator.getBitmapPoolSize();
    if (size > 0) {
      bitmapPool = new LruBitmapPool(size);
    } else {
      bitmapPool = new BitmapPoolAdapter();
    }
  }
  if (arrayPool == null) {
    arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes());
  }
  if (memoryCache == null) {
    memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
  }
  if (diskCacheFactory == null) {
    diskCacheFactory = new InternalCacheDiskCacheFactory(context);
  }
  if (engine == null) {
    engine =
        new Engine(
            memoryCache,
            diskCacheFactory,
            diskCacheExecutor,
            sourceExecutor,
            GlideExecutor.newUnlimitedSourceExecutor(),
            animationExecutor,
            isActiveResourceRetentionAllowed);
  }
  if (defaultRequestListeners == null) {
    defaultRequestListeners = Collections.emptyList();
  } else {
    defaultRequestListeners = Collections.unmodifiableList(defaultRequestListeners);
  }
  GlideExperiments experiments = glideExperimentsBuilder.build();
  /**在这儿直接创立了RequestManagerRetriever目标*/
  RequestManagerRetriever requestManagerRetriever =
      new RequestManagerRetriever(requestManagerFactory, experiments);
  return new Glide(
      context,
      engine,
      memoryCache,
      bitmapPool,
      arrayPool,
      requestManagerRetriever,
      connectivityMonitorFactory,
      logLevel,
      defaultRequestOptionsFactory,
      defaultTransitionOptions,
      defaultRequestListeners,
      manifestModules,
      annotationGeneratedGlideModule,
      experiments);
}

再次回到getRetriever办法中,由于Glide的get办法创立了Glide目标,也便是调用了Glide的getRequestManagerRetriever办法返回RequestManagerRetriever目标。

@NonNull
public RequestManagerRetriever getRequestManagerRetriever() {
  return requestManagerRetriever;
}

看到这个办法,也便是说在此之前已经将这个目标初始化了,那么什么时分初始化的呢?便是在创立Glide目标的时分。

Glide(
    /**这儿暂时不重视其他参数*/
    //......
    @NonNull RequestManagerRetriever requestManagerRetriever,
    //......) {
  this.requestManagerRetriever = requestManagerRetriever;
}

1.1.2 RequestManagerRetriever

在Glide的build办法中,是直接创立了RequestManagerRetriever目标

RequestManagerRetriever requestManagerRetriever =
    new RequestManagerRetriever(requestManagerFactory, experiments);

这个目标需求一个requestManagerFactory参数,这个参数可以在外部界说

public RequestManagerRetriever(
    @Nullable RequestManagerFactory factory, GlideExperiments experiments) {
  this.factory = factory != null ? factory : DEFAULT_FACTORY;
  this.experiments = experiments;
  handler = new Handler(Looper.getMainLooper(), this /* Callback */);
  lifecycleRequestManagerRetriever = new LifecycleRequestManagerRetriever(this.factory);
  frameWaiter = buildFrameWaiter(experiments);
}

假如没有界说便是用默认的DEFAULT_FACTORY。

在创立RequestManagerRetriever之后,相当于拿到了一个恳求的管理类,便利后续加载图片。

1.1.3 生命周期管理

在获取到RequestManagerRetriever目标之后,调用了其get办法,这儿咱们看下假如在with办法中传入了Activity,是怎么处理的。

public RequestManager get(@NonNull Activity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else if (activity instanceof FragmentActivity) {
    return get((FragmentActivity) activity);
  } else {
      /**判别当时Activity是否毁掉,假如毁掉了就报错*/
    assertNotDestroyed(activity);
    frameWaiter.registerSelf(activity);
    /**获取当时Activity的事务管理器*/
    android.app.FragmentManager fm = activity.getFragmentManager();
    return fragmentGet(activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
  }
}

首要判别假如是在子线程中,那么会调用另外一个重载的get办法,传入的是Context,而不是Activity了,那么咱们要点重视下主线程中是怎么处理。

private RequestManager fragmentGet(
    @NonNull Context context,
    @NonNull android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
    /**创立了RequestManagerFragment,这是一个空页面*/
  RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);
  /**第一次进来,这个办法返回空*/
  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);
    // This is a bit of hack, we're going to start the RequestManager, but not the
    // corresponding Lifecycle. It's safe to start the RequestManager, but starting the
    // Lifecycle might trigger memory leaks. See b/154405040
    if (isParentVisible) {
      requestManager.onStart();
    }
    current.setRequestManager(requestManager);
  }
  return requestManager;
}

首要,调用了fragmentGet办法,看名字好像是要获取一个Fragment,果然在这个办法中第一步就创立了一个RequestManagerFragment空页面,假如了解LifeCycle源码的同伴应该了解,这个页面大概率便是用来同步当时页面的生命周期的。

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

果然在RequestManagerFragment的生命周期办法中,做了状况的回调。当创立了一个空Fragment之后,会从这个Fragment中获取RequetManager,当然第一次进来的时分肯定是空的,因而会创立一次,并调用setRequestManager塞给RequestManagerFragment。

1.1.4 总结

因而with办法首要干了以下几件事:

(1)创立了Glide目标;

(2)创立了一个空的Fragment,并将生命周期状况回调,相当于将Glide与当时页面的生命周期绑定。

1.2 load主线

经过with源码,咱们知道在调用with办法后,终究获得一个RequetManager目标,相当于调用了它的load办法。

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

其实load办法很简略,asDrawable办法是创立一个RequestBuilder目标

public RequestBuilder<TranscodeType> load(@Nullable String string) {
  return loadGeneric(string);
}

由于load办法能传值许多类型,像url、File、Bitmap等等,因而load办法仅仅将这些需求加载的类型给model赋值,剩余的事情,都是交给into完结。

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

1.3 into主线

在Glide中,除了Glide的创立还有生命周期的绑定,剩下都是交给into主线完结,因而into是Glide中最重要最杂乱的。

public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
  Util.assertMainThread();
  Preconditions.checkNotNull(view);
  BaseRequestOptions<?> requestOptions = this;
  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,
      Executors.mainThreadExecutor());
}

在into办法中,咱们可以看到需求传入的便是ImageView.

Android进阶宝典 -- Glide源码分析
在此之前会依据ImageView的scaleType特点,来给RequestOption仿制,以便操控图片的显现状况。

然后一个比较重要的办法into办法,这个办法是RequestBuilder的into办法,传入了3个参数,别离是代表给容器的一个Target目标,还有一个比较重要的便是线程池,由于网络恳求涉及到线程的上下文切换。

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()");
  }
    /**① 创立一个SingleRequest目标 */
  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赋值 */
  target.setRequest(request);
  /** 建议恳求的开端 */
  requestManager.track(target, request);
  return target;
}

首要在这个办法中,调用buildRequest办法返回一个Request,这儿可以直接跟同伴们说便是SingleRequest

Android进阶宝典 -- Glide源码分析

1.3.1 建议恳求

创立request完结后,调用了RequetManager的track办法。

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

其实终究便是调用了RequetTracker的runRequest办法履行恳求,首要将Request添加到了requests的恳求行列中。

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

然后判别isPaused的状况,这个状况决议了当时是否进行网络恳求加载。其实这个状况便是在绑定页面生命周期后,当页面不行见或许毁掉的时分,就将isPaused设置为true,这时即使恳求到了Glide这边,也不会履行,并且直接调用了clear办法。

public synchronized void onStop() {
    /**这儿就会设置 isPaused = true*/
  pauseRequests();
  targetTracker.onStop();
}

当然假如isPaused为false,那么就直接调用了request的begin办法,也便是SingleRequest的begin办法。

@Override
public void begin() {
  synchronized (requestLock) {
    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, /* isLoadedFromAlternateCacheKey= */ false);
      return;
    }
    // Restarts for requests that are neither complete nor running can be treated as new requests
    // and can run again from the beginning.
    experimentalNotifyRequestStarted(model);
    cookie = GlideTrace.beginSectionAsync(TAG);
    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));
    }
  }
}

假如做过图片加载监听的同伴,应该对几个回调函数比较了解onLoadStartedonResourceReadyonLoadFailed,没错便是在这个办法中做的回调。

if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
  onSizeReady(overrideWidth, overrideHeight);
} else {
  target.getSize(this);
}

当这张图片的宽高尺寸符合标准的时分,就会调用onSizeReady办法,进行加载。

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

在这儿便是调用engine的load办法,在这个办法中,会依据图片的宽高级信息生成一个EngineKey,这个key是唯一的,与加载的图片一一对应。

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,
    Executor callbackExecutor) {
  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(
          glideContext,
          model,
          signature,
          width,
          height,
          resourceClass,
          transcodeClass,
          priority,
          diskCacheStrategy,
          transformations,
          isTransformationRequired,
          isScaleOnlyOrNoTransform,
          options,
          isMemoryCacheable,
          useUnlimitedSourceExecutorPool,
          useAnimationPool,
          onlyRetrieveFromCache,
          cb,
          callbackExecutor,
          key,
          startTime);
    }
  }
  // Avoid calling back while holding the engine lock, doing so makes it easier for callers to
  // deadlock.
  cb.onResourceReady(
      memoryResource, DataSource.MEMORY_CACHE, /* isLoadedFromAlternateCacheKey= */ false);
  return null;
}

1.3.2 三级缓存

经过生成的EngineKey,调用loadFromMemory办法,来获取图片资源EngineResource。

private EngineResource<?> loadFromMemory(
    EngineKey key, boolean isMemoryCacheable, long startTime) {
  if (!isMemoryCacheable) {
    return null;
  }
、/**从活动缓存中取资源*/
  EngineResource<?> active = loadFromActiveResources(key);
  if (active != null) {
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from active resources", startTime, key);
    }
    return active;
  }
    /** 从LRU中取出缓存资源,并放到活动缓存中*/
  EngineResource<?> cached = loadFromCache(key);
  if (cached != null) {
    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Loaded resource from cache", startTime, key);
    }
    return cached;
  }
  return null;
}

咱们可以看到在调用loadFromMemory办法时,首要从loadFromActiveResources、loadFromCache两个当地获取图片资源,别离代表从活动缓存和LRUCache中取资源,关于Glide三级缓存,后边将会有详细的文章介绍。

这样假如都没有找到资源,那么就会调用waitForExistingOrStartNewJob办法,其实便是从网络恳求获取资源。

1.3.3 onResourceReady

当网络恳求完结之后,或许从三级缓存中获取到了资源,都会回调到SingleRequest的onResourceReady办法。

private void onResourceReady(
    Resource<R> resource, R result, DataSource dataSource, boolean isAlternateCacheKey) {
  // We must call isFirstReadyResource before setting status.
  boolean isFirstResource = isFirstReadyResource();
  status = Status.COMPLETE;
  this.resource = resource;
  if (glideContext.getLogLevel() <= Log.DEBUG) {
    Log.d(
        GLIDE_TAG,
        "Finished loading "
            + result.getClass().getSimpleName()
            + " from "
            + dataSource
            + " for "
            + model
            + " with size ["
            + width
            + "x"
            + height
            + "] in "
            + LogTime.getElapsedMillis(startTime)
            + " ms");
  }
  notifyRequestCoordinatorLoadSucceeded();
  isCallingCallbacks = true;
  try {
    boolean anyListenerHandledUpdatingTarget = false;
    if (requestListeners != null) {
      for (RequestListener<R> listener : requestListeners) {
        anyListenerHandledUpdatingTarget |=
            listener.onResourceReady(result, model, target, dataSource, isFirstResource);
      }
    }
    anyListenerHandledUpdatingTarget |=
        targetListener != null
            && targetListener.onResourceReady(result, model, target, dataSource, isFirstResource);
    if (!anyListenerHandledUpdatingTarget) {
      Transition<? super R> animation = animationFactory.build(dataSource, isFirstResource);
      target.onResourceReady(result, animation);
    }
  } finally {
    isCallingCallbacks = false;
  }
  GlideTrace.endSectionAsync(TAG, cookie);
}

在这个办法中,首要会判别是否设置了RequestListener,假如设置了那么就会调用这个接口的onResourceReady办法,终究也会调用target的onResourceReady办法。

其实target可以以为便是ImageView,由于在into中除了可以传值ImageView之外,还可以传值Target

@Override
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
  if (transition == null || !transition.transition(resource, this)) {
    setResourceInternal(resource);
  } else {
    maybeUpdateAnimatable(resource);
  }
}

终究便是调用了setResourceInternal办法,经过调用ImageView的setImageBitmap或许setImageDrawable办法给ImageView显现图片。

protected void setResource(Bitmap resource) {
  view.setImageBitmap(resource);
}

1.3.4 小结

其实在into中,咱们可以分为以下几个大段:

(1)首要创立图片加载恳求,其实便是创立了SingleRequest;

(2)判别当时是否可以履行恳求(isPaused是否为false),假如可以建议恳求,终究调用Engine的load办法;

(3)依据图片的信息生成EngineKey,并拿这个key别离从活动缓存、Lru中获取图片资源,假如获取到,直接回调onResourceReady;假如没有获取到,那么就建议网络恳求获取资源,成功之后加入活泼缓存并回调onResourceReady。

(4)在SingleRequest的onResourceReady办法中,终究其实便是调用了ImageView的setImageBitmap办法或许setImageDrawable显现图片。

2 手写简略Glide结构

假如要完成Glide,那么就需求对Glide的特性有所了解,其间生命周期绑定、三级缓存是其最大的亮点,因而在完成时,也侧重完成这两点。

2.1 生命周期绑定

/**
 * 手写简易Glide图片加载结构
 * 功能包含3大主线:with、load、into
 * 支持生命周期绑定,监听生命周期改变
 * 支持三级缓存
 */
object MyGlide:DefaultLifecycleObserver {
    /**意图为了与页面的生命周期绑定*/
    fun with(lifecycleOwner: LifecycleOwner):MyGlide{
        /**这儿和Glide不同的是,直接传入了LifecycleOwner*/
        lifecycleOwner.lifecycle.addObserver(this)
        return this
    }
    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
    }
    override fun onPause(owner: LifecycleOwner) {
        super.onPause(owner)
    }
    override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
    }
}

首要这儿是跟Glide有点不一样,由于假如想要监听页面的生命周期,只需求在LifeCycle中将这个页面作为被观察,即可获取页面的实时状况,因而没有Glide那么繁琐。

fun load(url: String): MyGlide {
    if (requestOption == null) {
        requestOption = RequestOption()
    }
    requestOption?.params = url
    return this
}
fun load(file: File): MyGlide {
    if (requestOption == null) {
        requestOption = RequestOption()
    }
    requestOption?.params = file
    return this
}
fun load(drawableId: Drawable): MyGlide {
    if (requestOption == null) {
        requestOption = RequestOption()
    }
    requestOption?.params = drawableId
    return this
}

然后load办法也是一个重载办法,支持多种资源加载,因而需求一个RequestOption实体来保存这些恳求参数。

class RequestOption(
    /**恳求的参数类型*/
    var params: Any? = null,
    /**ImageView的ScaleType特点*/
    val scaleType: ImageView.ScaleType = ImageView.ScaleType.FIT_XY,
    /**图片的宽 高*/
    val width: Int = 0,
    val height: Int = 0
)

2.2 建议网络恳求

其间into办法与Glide共同,也是需求传入ImageView控件,在load办法中传入的参数,在这个办法中会依次判别。

fun into(view: ImageView) {
    if (requestOption == null) {
        requestOption = RequestOption()
    }
    requestOption?.scaleType = view.scaleType
    buildTargetRequest(requestOption, object : IRequestListener {
        override fun onLoadStart() {
        }
        override fun onResourceReady(source: Bitmap?) {
            if (isPause) {
                return
            }
            /**只有在页面活泼的时分,才可以加载图片*/
            if (source != null) {
                view.setImageBitmap(source)
            }
        }
    })
}
private fun buildTargetRequest(requestOption: RequestOption?, listener: IRequestListener) {
    if (requestOption == null) return
    /**判别恳求的参数*/
    when (requestOption.params) {
        is String -> {
            doNetRequest(requestOption.params as String, listener)
        }
        is File -> {
            doFileRequest(requestOption.params as File, listener)
        }
        is Int -> {
            doDrawableRequest(requestOption.params as Int)
        }
        /**其他类型暂不处理*/
        else -> {
            null
        }
    }
}
private fun doDrawableRequest(i: Int): Bitmap? {
    return null
}
/**加载文件资源*/
private fun doFileRequest(file: File, listener: IRequestListener) {
    val inputStream = FileInputStream(file)
    listener.onResourceReady(BitmapFactory.decodeStream(inputStream))
}
/**假如是String字符串,那么就需求建议网络恳求*/
private fun doNetRequest(url: String, listener: IRequestListener) {
    /**在子线程中履行*/
    CoroutineScope(Dispatchers.IO).launch(Dispatchers.IO) {
        try {
            val newURL = URL(url)
            val connection = newURL.openConnection() as HttpURLConnection
            connection.requestMethod = "GET"
            connection.connectTimeout = 60 * 1000
            connection.readTimeout = 20 * 1000
            connection.doInput = true
            Log.e("TAG", "code == ${connection.responseCode}")
            connection.connect()
            listener.onLoadStart()
            /**获取图片资源流*/
            val inputStream = connection.inputStream
            withContext(Dispatchers.Main){
                listener.onResourceReady(BitmapFactory.decodeStream(inputStream))
            }
            inputStream.close()
        } catch (e: Exception) {
            Log.e("TAG", "exp===>${e.message}")
        }
    }
}
override fun onDestroy(owner: LifecycleOwner) {
    super.onDestroy(owner)
    isPause = true
}
override fun onPause(owner: LifecycleOwner) {
    super.onPause(owner)
}
override fun onResume(owner: LifecycleOwner) {
    super.onResume(owner)
    isPause = false
}

在buildTargetRequest办法中,判别传入的类型,决议是否需求建议网络恳求。这儿有一点需求留意的便是,Glide中其实对资源获取选用三级缓存的方法,经过EngineKey来从内存中寻觅,这儿没有运用后续会补充上去。

还有一点 便是在进行网络恳求的时分,需求留意主线程的切换,这儿我没有运用线程池而是选用了协程的方法切换,接下来咱们加载一张本地图片看下效果

MyGlide.with(this)
    .load(File("/storage/emulated/0/src.webp"))
    .into(iv_image)

Android进阶宝典 -- Glide源码分析

咱们运用Glide一样的API就可以加载一张本地图片。

MyGlide.with(this)
    .load("https://图片url地址")
    .into(iv_image)

运用url加载网络链接亦是如此