一些收藏

开源框架 | Glide 使用流程源码解析

2020-09-22  本文已影响0人  南子李

Glide 强大的图片加载库

1. 基本使用

  RequestOptions options = new RequestOptions()
        .placeHolder(R.id.laoding)
        .skipMemeoryCache(true)
        .error(R.id.error)
        .override(100, 100); 
  Glide.with(context)
        .asBitmap()
        .load(imgUrl)
        .transform(new CircleCrop())
        .apply(options)
        .into(imageView);

2. 源码流程分析

2.1 with()
  1. 返回一个 RequestManager 对象;
  2. 根据 with() 传入的参数确定图片加载的生命周期;
2.2 load()

load() 方法用于返回一个 RequestBuilder 对象,Glide 中有 load() 有多个重载方法,表示 Glide 支持加载的图片资源类型,可以是 File、二进制流、Uri、应用资源(R.drawable.icon)、网络图片等,这里以最常使用的网络图片为例:

  public RequestBuilder<Drawable> load(@Nullable File file) {
    return asDrawable().load(file);
  }
  public RequestBuilder<Drawable> load(@Nullable byte[] model) {
    return asDrawable().load(model);
  }
  public RequestBuilder<Drawable> load(@Nullable Uri uri) {
    return asDrawable().load(uri);
  }
  public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
    return asDrawable().load(resourceId);
  }
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

不管是哪种类型的图片,load() 方法里都会执行 asDrawable().load() 这行代码,asDrawable() 返回一个RequestBuilder 对象;

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

load() 方法比较简单,load() 方法里面传入的 String 类型的参数表示图片的网络地址,设置为 RequestBuilder 的 mode 属性,这个 mode 也就是图片资源的路径,然后设置 isModeSet 为 true,表示我们已经对图片的资源路径进行了设置,也就是说可以通过这个 mode 加载图片,这个后面会用到。

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

进入 into() 方法,首先是和图片转换相关的内容,这部分后面再讲,直接来看最后一行代码,glideContext.buildImageViewTarget(view, transcodeClass) 用于返回一个 ViewTarget<ImageView, Z>,ImageView 即加载图片的 View,Z 表示图片资源类型,可以是 Bitmap 或者 Drawable:

//GlideContext
  public <X> ViewTarget<ImageView, X> buildImageViewTarget(
      @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
    return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
  }
//ImageViewTargetFactory
  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)");
    }
  }

可以看到 buildTarget() 根据传入的 clazz 的参数来构建不同的 Target 对象,这个 clazz 参数是如何获取的,这里比较复杂,如果调用了 asBitmap() 方法,这里就会构建出 BitmapImageViewTarget 对象,否则会构建 DrawableImageViewTarget 对象。

回到前面的 into() 方法,获取到 target 对象后继续调用 RequestBuilder 内部私有的 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;
  }

看关键代码,首先调用 buildRequest() 创建了一个 Request,buildRequest() -> buildRequestRecursive() -> buildThumbnailRequestRecursive() -> obtainRequest() -> SingleRequest.obtain(),这基本上是创建 Request 主要的流程,buildThumbnailRequestRecursive() 处理了图片压缩相关的操作,最后调用 SingleRequest.obtain() 创建了一个 Request 并返回,这里只是创建了一个 Request,什么时候执行这个请求?后面调用了 requestManager.track(),进入看看:

// RequestManager
  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 { //停止状态
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Paused, delaying request");
      }
      pendingRequests.add(request);
    }
  }

track() 方法里比较简单,直接调用了 requestTracker.runRequest(),如果当前 Request 不处于暂停状态,调用 begin() 方法开始执行,如果处于停止状态,先将这个请求加入到 pendingRequests 集合中等到暂停状态解除再执行。Request 是一个接口类,前面创建 Request 时了解到创建的是一个 SingleRequest 对象,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));
    }
  }

如果当前请求状态不是 RUNNING 并且之前也没有执行完成过,那么就要执行这个请求,先会判断图片的宽高值是否有效,有效则调用 onSizeReady(),否则调用 target.getSize() 通过 ImageView 的 layout_width 和 layout_height 确定图片宽高,最后还是会调用到 onSizeReady() 方法:

  public void onSizeReady(int width, int height) {
    stateVerifier.throwIfRecycled();
    if (IS_VERBOSE_LOGGABLE) {
      logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
    if (status != Status.WAITING_FOR_SIZE) {
      return;
    }
    status = Status.RUNNING;

    float sizeMultiplier = requestOptions.getSizeMultiplier();
    this.width = maybeApplySizeMultiplier(width, sizeMultiplier);
    this.height = maybeApplySizeMultiplier(height, sizeMultiplier);

    if (IS_VERBOSE_LOGGABLE) {
      logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
    }
    loadStatus = engine.load(
        glideContext,
        model,
        requestOptions.getSignature(),
        this.width,
        this.height,
        requestOptions.getResourceClass(),
        transcodeClass,
        priority,
        requestOptions.getDiskCacheStrategy(),
        requestOptions.getTransformations(),
        requestOptions.isTransformationRequired(),
        requestOptions.isScaleOnlyOrNoTransform(),
        requestOptions.getOptions(),
        requestOptions.isMemoryCacheable(),
        requestOptions.getUseUnlimitedSourceGeneratorsPool(),
        requestOptions.getUseAnimationPool(),
        requestOptions.getOnlyRetrieveFromCache(),
        this);

    if (status != Status.RUNNING) {
      loadStatus = null;
    }
    if (IS_VERBOSE_LOGGABLE) {
      logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
    }
  }

onSizeReady() 方法比较简单,调用 engine.load() 传入了一大堆和请求相关的参数,可以知道这里面应该就是 Glide 图片加载的核心了:

  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) {
    ...

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

省略部分主要做了一些缓存处理,这部分后面再分析,先来看这里的 engineJob 和 decodeJob,先是分别创建了 EngineJob 和 DecodeJob 对象,然后调用 engineJob.start(decodeJob) 开始执行图片解码相关的任务:

  public void start(DecodeJob<R> decodeJob) {
    this.decodeJob = decodeJob;
    GlideExecutor executor = decodeJob.willDecodeFromCache()
        ? diskCacheExecutor
        : getActiveSourceExecutor();
    executor.execute(decodeJob);
  }

EngineJob 中使用线程池 GlideExecutor 执行 decodeJob,GlideExecutor 线程池只有一个线程,decodeJob 是一个 Runnable 对象,也就是执行 DecodeJob 的 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();
    }
  }

先看 try 里面调用的 runWrappen() 方法:

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

第一次请求,runReson 在 DecodeJob 中默认为 INITIALIZE,getNextStage() 根据当前阶段获取下一个需要进行的阶段,如果第一次是 INITIALIZE 并且不希望从缓存中获取图片资源,返回的值为 SOURCE,接下来执行 getNextGenerator():

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

前面从 getNextStage() 中返回的是 SOURCE,进入到 getNextGenerator() 中返回一个 SourceGenerator 对象,回到 runWraapered() 中,继续来看 runGenerators() 方法:

  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;
      }
    }
    if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
      notifyFailed();
    }
  }

重点看 while 循环,循环体内还是根据当前的 stage 值获取下一阶段的 stage 和 currentGenerator,这里类似一个遍历过程,循环判断条件里面调用了 currentGenerator.starNext(),前面我们拿到的是 SourceGenerator 对象,所以直接进去看看:

  public boolean startNext() {
    if (dataToCache != null) {
      Object data = dataToCache;
      dataToCache = null;
      cacheData(data);
    }

    if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {
      return true;
    }
    sourceCacheGenerator = null;

    loadData = null;
    boolean started = false;
    while (!started && hasNextModelLoader()) {
      loadData = helper.getLoadData().get(loadDataListIndex++);
      if (loadData != null
          && (helper.getDiskCacheStrategy().isDataCacheable(loadData.fetcher.getDataSource())
          || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {
        started = true;
        loadData.fetcher.loadData(helper.getPriority(), this);
      }
    }
    return started;
  }

重点看while循环,首先通过 helper.getLoadData() 获取一个请求列表,接着仍然是判断是否需要加载缓存,不需要则调用 Fetcher 的 loadData() 方法加载,由于我们是加载的网络图片,这里的 Fetcher 就是 HttpUrlFetcher,进入它的 loadData():

  public void loadData(@NonNull Priority priority,
      @NonNull DataCallback<? super InputStream> callback) {
    long startTime = LogTime.getLogTime();
    try {
      InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
      callback.onDataReady(result);
    } catch (IOException e) {
      if (Log.isLoggable(TAG, Log.DEBUG)) {
        Log.d(TAG, "Failed to load data for url", e);
      }
      callback.onLoadFailed(e);
    } finally {
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime));
      }
    }
  }

调用 loadDataWithRedirects() 拿到了请求返回的输入流,继续进去:

  private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
      Map<String, String> headers) throws IOException {
    if (redirects >= MAXIMUM_REDIRECTS) {
      throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
    } else {
      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);
    }
  }

网络请求链接可能存在重定向,这里先是判断重定向的次数,接着使用 Android 自带的 HttpURLConnection 执行请求,如果是重定向继续请求,否则直接调用 getStreamForSuccessfulRequest() 获取请求结果,具体怎么拿到结果的我们就不再进去探讨了。

这就是一次网络图片的请求流程,我们可以知道在 Glide 中发起的网络请求是使用的 Android 自带的 HttpURLConnection,当然也可以使用 OKHttp,需要我们添加相关依赖,目前为止,我们还仅仅是拿到的一个请求返回的 InputStream,怎么进行解码操作最后显示在指定的 ImageView 呢?

拿到图片流了,接着就进入了图片的编码阶段 DECODE_DATA,回到 runWrapped() 里当 RunReason 为 DECODE_DATA 时调用流程如下:decodeFromRetrievedData() -> decodeFromData() -> decodeFromFetcher() -> runLoadPath() -> path.load() -> loadWithExceptionList() -> path.decode() -> decodeResource() -> decodeResourceWithList() -> decoder.decode(),最后调用到 decoder.decode(),由于我们获得的是 InputStream 对象,这里就是调用的就是 StreamBitmapDecoder 的 decode():

  public Resource<Bitmap> decode(@NonNull InputStream source, int width, int height,
      @NonNull Options options)
      throws IOException {
    final RecyclableBufferedInputStream bufferedStream;
    final boolean ownsBufferedStream;
    if (source instanceof RecyclableBufferedInputStream) {
      bufferedStream = (RecyclableBufferedInputStream) source;
      ownsBufferedStream = false;
    } else {
      bufferedStream = new RecyclableBufferedInputStream(source, byteArrayPool);
      ownsBufferedStream = true;
    }
    ExceptionCatchingInputStream exceptionStream =
        ExceptionCatchingInputStream.obtain(bufferedStream);
    MarkEnforcingInputStream invalidatingStream = new MarkEnforcingInputStream(exceptionStream);
    UntrustedCallbacks callbacks = new UntrustedCallbacks(bufferedStream, exceptionStream);
    try {
      return downsampler.decode(invalidatingStream, width, height, options, callbacks);
    } finally {
      exceptionStream.release();
      if (ownsBufferedStream) {
        bufferedStream.release();
      }
    }
  }

首先会对我们的请求返回的 InputStream 进行多次的封装,封装是为了便于我们在处理输入流的时候可以拿到异常并处理异常,接着调用了 downSamper.decode(),这里就是最终执行解码操作的地方了,具体解码的细节不是我们关注的重点,就不再跟进去看了。

通过解码拿到图片了,还没有把图片设置给 ImageView,继续来看,回到解码流程开始的地方 decodeFromRetrievedData():

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

解码流程返回了一个 Resource 对象,不为空则调用 notifyEncodeAndRelease() 发送解码完成的通知并释放资源:

  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(); //释放资源
  }

先是调用 notifyComplete() 发送通知,再调用 onEncodeComplete() 释放资源,资源释放比较简单,重点来看 notifyComplete():

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

继续调用了 callback.onResourceReady() ,callback 就是我们的 EngineJob,跟进去看看怎么发送这个通知的:

  public void onResourceReady(Resource<R> resource, DataSource dataSource) {
    this.resource = resource;
    this.dataSource = dataSource;
    MAIN_THREAD_HANDLER.obtainMessage(MSG_COMPLETE, this).sendToTarget();
  }

这里一下就明白了吧,又回到了最基本的线程切换的地方,使用一个属于主线程的 Handler 发送了一条 what 为 MSG_COMPLETE 的消息给主线程,让主线程去为 ImageView 设置图片,来看这个 Handler 是怎么创建的:

  private static final Handler MAIN_THREAD_HANDLER =
      new Handler(Looper.getMainLooper(), new MainThreadCallback());

通过 Handler 的构造方法,传入了主线程的 Looper 和一个自定义的 Callback,创建了一个属于主线程的 Handle,MainThreadCallback 就是我们主线程具体执行的操作了:

  private static class MainThreadCallback implements Handler.Callback {
    @Synthetic
    @SuppressWarnings("WeakerAccess")
    MainThreadCallback() { }
    @Override
    public boolean handleMessage(Message message) {
      EngineJob<?> job = (EngineJob<?>) message.obj;
      switch (message.what) {
        case MSG_COMPLETE:
          job.handleResultOnMainThread();
          break;
        case MSG_EXCEPTION:
          job.handleExceptionOnMainThread();
          break;
        case MSG_CANCELLED:
          job.handleCancelledOnMainThread();
          break;
        default:
          throw new IllegalStateException("Unrecognized message: " + message.what);
      }
      return true;
    }
  }

这里可以看到这个 MAIN_THREAD_HANDLER 还可以接受请求异常和请求取消的消息,前面发送过来的消息的 what 是 MSG_COMPLETE,调用了 job.handleResultOnMainThread() -> SingleRequest.onResourceReady(),又进入到了 SingleRequest 的 onResourceReady(),紧接着调用了 onResourceReady() 的重载方法,这里有关键代码:

  target.onResourceReady(result, animation);

我们是给 ImageView 设置图片,这里的 target 就是 ImageViewTarget,再继续跟踪下去就可以看到这样一句熟悉的代码:

view.setImageDrawable(resource);

一步一步下来,这里就是真正将图片设置给 ImageView 的关键地方了。

只是请求一张网络图片并加载到 ImageView 的流程,先是需要解析load() 方法传入的 url,获取 RequestOptions 中的相关参数封装到 Request 请求中,如果没有缓存通过线程池执行网络请求,请求到的图片只是一个 InputStream,又通过解码得到图片资源,接着使用 Handler 发送消息给主线程,在主线程将图片加载到 ImageView 中;

这一流程中包含了大量的参数配置和封装过程,有关图片压缩和缓存部分,以及通过其他方式获取图片资源又会对应着不同的编码和解码流程,一句简单的调用:Glide.with(this).load(url).into(imageView),背后涉及到的流程复杂程度是极大的,不得不感叹 Glide 强大的封装能力。

3. 解决的问题

3.1 异步处理

图片资源的获取可能通过网络请求也可能需要从文件中读取,对图片进行编码和解码的过程以及显示的图片如果是 gif 需要执行动画效果时都是比较耗时的任务,Glide 使用线程池去执行这些耗时任务。

一个线程池够吗?
一个线程池不够,当同时有大量图片需要加载时,网络请求会阻塞线程,所以需要单独的一个线程池,Glide 如果结合 OkHttp 进行网络请求则直接使用 OkHttp 自带的线程池,读取磁盘缓存需要一个线程池,如果图片资源是 gif,还需要一个线程池执行动画:

public final class GlideBuilder {
  private GlideExecutor sourceExecutor; //加载源文件,包括网络
  private GlideExecutor diskCacheExecutor; //加载磁盘缓存
  private GlideExecutor animationExecutor; //执行动画
...
}
3.2 线程切换

异步处理开启了子线程去执行耗时任务,任务执行完成需要将消息返回到主线程进行 UI 更新,Android 中线程间通信就是 Handler 了:

class EngineJob<R> implements DecodeJob.Callback<R>,
    Poolable {
  private static final Handler MAIN_THREAD_HANDLER =
      new Handler(Looper.getMainLooper(), new MainThreadCallback());
}
3.3 缓存策略

Glide 中的两种缓存 LruCache 和 DiskLruCache,Glide 默认使用内存缓存,如果要使用磁盘缓存或者屏蔽内存缓存可以如下设置:

   RequestOptions options = new RequestOptions()
            .skipMemoryCache(true)
            .diskCacheStrategy(DiskCacheStrategy.ALL);
   Glide.with(fragment)
            .load(url)
            .apply(options)
            .into(imageView);
3.4 内存溢出

Android 对应用进程的内存分配是有限的,尽管随着技术的发展,手机厂商允许应用进程的最大内存空间在逐渐增大,但是手机的屏幕分辨率在增加,手机相机像素也在提升,图片所需要占据的内存空间也越来越大,存储一张图片不可避免是需要占据大量的内存空间的,当我们加载大量的大尺寸图片时,就很容易产生内存溢出问题,如何解决呢?

3.5 内存泄漏

内存泄漏的产生是因为存在引用关系,导致应该被回收的资源无法回收。

我们在使用 Glide 时传入了 ImageView 对象,设想如果我们要结束一个 Activity,当前 Activity 中有一个 ImageView 正在加载图片,由于 ImageView 需要加载图片,而 ImageView 和 Activity 存在引用关系,这将导致我们的 Activity 结束时无法回收 ImageView 而内存泄漏,如何解决呢?

因为存在引用关系,很简单会想到使用弱引用,这就能保证我们的 ImageView 一定可以被回收,但是图片加载还没停止呢?这样并不是理想的解决方案,我们应该确保 Activity 退出时还没执行完成的图片加载也能够一起停止。

Glide 使用接口类 LifecycleListener 可以感知 Activity、Fragment、Application 的生命周期,通过对它们生命周期的监听,在结束退出 Activity 或者 Fragment 时,调用 LifecycleListener.onDestroy() 方法结束未完成的请求任务,该方法的具体实现在 RequestManager 中。

4. 参考
上一篇 下一篇

猜你喜欢

热点阅读