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

919 阅读14分钟

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.

image.png 在此之前会根据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

image.png

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方法,在这个方法中,会根据图片的宽高、model(url/file/bitmap/drawable 在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)

image.png

我们使用Glide一样的API就能够加载一张本地图片。

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

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