前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Glide 如何实现正确加载图片而没有错位

Glide 如何实现正确加载图片而没有错位

作者头像
夏洛克的猫
发布于 2018-10-18 04:05:12
发布于 2018-10-18 04:05:12
1.8K00
代码可运行
举报
文章被收录于专栏:移动开发移动开发
运行总次数:0
代码可运行

我们在平时的项目使用下面的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 GlideApp
                .with(context)
                .load(url)
                .into(imageView);

当我们在常见的列表界面中(如 recycleview 实现的列表),使用上面的代码,在我们快速滑动中,glide 是如何实现正确加载图片,而没有导致图片内容的错位或者是不正确呢?

要达到这样的效果,简而言之,就是要执行上面的代码后,glide 要把最新的图片加载到正确的对象上,而取消对象之前关联的图片加载请求。

我们首先从 into() 这个方法进行分析。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  /**
   * Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into
   * the view, and frees any resources Glide may have previously loaded into the view so they may    *  be reused.
   *
   * @see RequestManager#clear(Target)
   *
   * @param view The view to cancel previous loads for and load the new resource into.
   * @return The
   * {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link ImageView}.
   */
  @NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);

    RequestOptions requestOptions = this.requestOptions;
    if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()
        && view.getScaleType() != null) {
      // Clone in this method so that if we use this RequestBuilder to load into a View and then
      // into a different target, we don't retain the transformation applied based on the previous
      // View's scale type.
      switch (view.getScaleType()) {
        case CENTER_CROP:
          requestOptions = requestOptions.clone().optionalCenterCrop();
          break;
        case CENTER_INSIDE:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case FIT_CENTER:
        case FIT_START:
        case FIT_END:
          requestOptions = requestOptions.clone().optionalFitCenter();
          break;
        case FIT_XY:
          requestOptions = requestOptions.clone().optionalCenterInside();
          break;
        case CENTER:
        case MATRIX:
        default:
          // Do nothing.
      }
    }

    return into(
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions);
  }

其实从方法的注释上就已经证明了我上面的说法。注释大意如下:

给 ImagView 设置将要被加载的资源,取消任何已存在的与 ImageView x相关的加载,释放 Glide 之前可能给该 View 加载的资源,这样他们可以被复用。

可以看到 45 行的代码是关键,具体分析下代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 the request is completed, beginning again will ensure the result is re-delivered,
      // triggering RequestListeners and Targets. If the request is failed, beginning again will
      // restart the request, giving it another chance to complete. If the request is already
      // running, we can let it continue running without interruption.
      if (!Preconditions.checkNotNull(previous).isRunning()) {
        // Use the previous request rather than the new one to allow for optimizations like skipping
        // setting placeholders, tracking and un-tracking Targets, and obtaining View dimensions
        // that are done in the individual Request.
        previous.begin();
      }
      return target;
    }

    requestManager.clear(target);
    target.setRequest(request);
    requestManager.track(target, request);

    return target;
  }

由于 Glide 的代码还是相当复杂的,这里我不会一行一行的分析具体实现,大家可以对感兴趣的地方自己去探索下,这里我们主要看下上面提到主要流程的实现。

可以看到 15 行代码,构建了一个 Request 并持有了 target 。这样便可以将结果通知给 target。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 Request request = buildRequest(target, targetListener, options);

上面的入参 target 是由

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
glideContext.buildImageViewTarget(view, transcodeClass)

构建,具体的实现这里不分析,我们只需知道 Target 是 Glide 对我们要加载目标的一个封装和抽象。

下面贴一下接口定义和实现帮助大家稍微理解下。实现其实有很多种,这里贴的是我们常用用法最容易使用到的内部实现。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * An interface that Glide can load a resource into and notify of relevant lifecycle events during a
 * load.
 *
 * <p> The lifecycle events in this class are as follows: <ul> <li>onLoadStarted</li>
 * <li>onResourceReady</li> <li>onLoadCleared</li> <li>onLoadFailed</li> </ul>
 *
 * The typical lifecycle is onLoadStarted -> onResourceReady or onLoadFailed -> onLoadCleared.
 * However, there are no guarantees. onLoadStarted may not be called if the resource is in memory or
 * if the load will fail because of a null model object. onLoadCleared similarly may never be called
 * if the target is never cleared. See the docs for the individual methods for details. </p>
 *
 * @param <R> The type of resource the target can display.
 */
public interface Target<R> extends LifecycleListener {
  /**
   * Indicates that we want the resource in its original unmodified width and/or height.
   */
  int SIZE_ORIGINAL = Integer.MIN_VALUE;

  /**
   * A lifecycle callback that is called when a load is started.
   *
   * <p> Note - This may not be called for every load, it is possible for example for loads to fail
   * before the load starts (when the model object is null).
   *
   * <p> Note - This method may be called multiple times before any other lifecycle method is
   * called. Loads can be paused and restarted due to lifecycle or connectivity events and each
   * restart may cause a call here.
   *
   * <p>You must ensure that any current Drawable received in {@link #onResourceReady(Object,
   * Transition)} is no longer displayed before redrawing the container (usually a View) or
   * changing its visibility.
   *
   * @param placeholder The placeholder drawable to optionally show, or null.
   */
  void onLoadStarted(@Nullable Drawable placeholder);

  /**
   * A lifecycle callback that is called when a load fails.
   *
   * <p> Note - This may be called before {@link #onLoadStarted(android.graphics.drawable.Drawable)
   * } if the model object is null.
   *
   * <p>You must ensure that any current Drawable received in {@link #onResourceReady(Object,
   * Transition)} is no longer displayed before redrawing the container (usually a View) or
   * changing its visibility.
   *
   * @param errorDrawable The error drawable to optionally show, or null.
   */
  void onLoadFailed(@Nullable Drawable errorDrawable);

  /**
   * The method that will be called when the resource load has finished.
   *
   * @param resource the loaded resource.
   */
  void onResourceReady(@NonNull R resource, @Nullable Transition<? super R> transition);

  /**
   * A lifecycle callback that is called when a load is cancelled and its resources are freed.
   *
   * <p>You must ensure that any current Drawable received in {@link #onResourceReady(Object,
   * Transition)} is no longer displayed before redrawing the container (usually a View) or
   * changing its visibility.
   *
   * @param placeholder The placeholder drawable to optionally show, or null.
   */
  void onLoadCleared(@Nullable Drawable placeholder);

  /**
   * A method to retrieve the size of this target.
   *
   * @param cb The callback that must be called when the size of the target has been determined
   */
  void getSize(@NonNull SizeReadyCallback cb);

  /**
   * Removes the given callback from the pending set if it's still retained.
   *
   * @param cb The callback to remove.
   */
  void removeCallback(@NonNull SizeReadyCallback cb);

  /**
   * Sets the current request for this target to retain, should not be called outside of Glide.
   */
  void setRequest(@Nullable Request request);

  /**
   * Retrieves the current request for this target, should not be called outside of Glide.
   */
  @Nullable
  Request getRequest();
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * A base {@link com.bumptech.glide.request.target.Target} for displaying resources in {@link
 * android.widget.ImageView}s.
 *
 * @param <Z> The type of resource that this target will display in the wrapped {@link
 *            android.widget.ImageView}.
 */
// Public API.
@SuppressWarnings("WeakerAccess")
public abstract class ImageViewTarget<Z> extends ViewTarget<ImageView, Z>
    implements Transition.ViewAdapter {

  @Nullable
  private Animatable animatable;

  public ImageViewTarget(ImageView view) {
    super(view);
  }

  /**
   * @deprecated Use {@link #waitForLayout()} instead.
   */
  @SuppressWarnings({"deprecation"})
  @Deprecated
  public ImageViewTarget(ImageView view, boolean waitForLayout) {
    super(view, waitForLayout);
  }

  /**
   * Returns the current {@link android.graphics.drawable.Drawable} being displayed in the view
   * using {@link android.widget.ImageView#getDrawable()}.
   */
  @Override
  @Nullable
  public Drawable getCurrentDrawable() {
    return view.getDrawable();
  }

  /**
   * Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
   * android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
   *
   * @param drawable {@inheritDoc}
   */
  @Override
  public void setDrawable(Drawable drawable) {
    view.setImageDrawable(drawable);
  }

  /**
   * Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
   * android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
   *
   * @param placeholder {@inheritDoc}
   */
  @Override
  public void onLoadStarted(@Nullable Drawable placeholder) {
    super.onLoadStarted(placeholder);
    setResourceInternal(null);
    setDrawable(placeholder);
  }

  /**
   * Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
   * android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
   *
   * @param errorDrawable {@inheritDoc}
   */
  @Override
  public void onLoadFailed(@Nullable Drawable errorDrawable) {
    super.onLoadFailed(errorDrawable);
    setResourceInternal(null);
    setDrawable(errorDrawable);
  }

  /**
   * Sets the given {@link android.graphics.drawable.Drawable} on the view using {@link
   * android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
   *
   * @param placeholder {@inheritDoc}
   */
  @Override
  public void onLoadCleared(@Nullable Drawable placeholder) {
    super.onLoadCleared(placeholder);
    if (animatable != null) {
      animatable.stop();
    }
    setResourceInternal(null);
    setDrawable(placeholder);
  }

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

  @Override
  public void onStart() {
    if (animatable != null) {
      animatable.start();
    }
  }

  @Override
  public void onStop() {
    if (animatable != null) {
      animatable.stop();
    }
  }

  private void setResourceInternal(@Nullable Z resource) {
    // Order matters here. Set the resource first to make sure that the Drawable has a valid and
    // non-null Callback before starting it.
    setResource(resource);
    maybeUpdateAnimatable(resource);
  }

  private void maybeUpdateAnimatable(@Nullable Z resource) {
    if (resource instanceof Animatable) {
      animatable = (Animatable) resource;
      animatable.start();
    } else {
      animatable = null;
    }
  }

  protected abstract void setResource(@Nullable Z resource);
}

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
into(
      @NonNull Y target,
      @Nullable RequestListener<TranscodeType> targetListener,
      @NonNull RequestOptions options) 

方法中 第 15 至 19 行中,判断 Taget 中的之前的 Request 和最新构建的 Request 是否相同,如果相同回收最新的 Request ,让旧的 Request 继续运行。如果不同,就取消之前的 Request 和 target 的关联。

具体逻辑代码为 31 行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 requestManager.clear(target);

最终会触发下面的代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  private void untrackOrDelegate(@NonNull Target<?> target) {
    boolean isOwnedByUs = untrack(target);
    // We'll end up here if the Target was cleared after the RequestManager that started the request
    // is destroyed. That can happen for at least two reasons:
    // 1. We call clear() on a background thread using something other than Application Context
    // RequestManager.
    // 2. The caller retains a reference to the RequestManager after the corresponding Activity or
    // Fragment is destroyed, starts a load with it, and then clears that load with a different
    // RequestManager. Callers seem especially likely to do this in retained Fragments (#2262).
    //
    // #1 is always an error. At best the caller is leaking memory briefly in something like an
    // AsyncTask. At worst the caller is leaking an Activity or Fragment for a sustained period of
    // time if they do something like reference the Activity RequestManager in a long lived
    // background thread or task.
    //
    // #2 is always an error. Callers shouldn't be starting new loads using RequestManagers after
    // the corresponding Activity or Fragment is destroyed because retaining any reference to the
    // RequestManager leaks memory. It's possible that there's some brief period of time during or
    // immediately after onDestroy where this is reasonable, but I can't think of why.
    if (!isOwnedByUs && !glide.removeFromManagers(target) && target.getRequest() != null) {
      Request request = target.getRequest();
      target.setRequest(null);
      request.clear();
    }
  }

可以看到, target 对应的 request 被置 null, 而旧的 request 被 “clear”。旧的 Request 被 clear 后,又是如何让资源没有去加载到关联的 Target 上的? 我们看其中 SingleRequest 的实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  /**
   * Cancels the current load if it is in progress, clears any resources held onto by the request
   * and replaces the loaded resource if the load completed with the placeholder.
   *
   * <p> Cleared requests can be restarted with a subsequent call to {@link #begin()} </p>
   *
   * @see #cancel()
   */
  @Override
  public void clear() {
    Util.assertMainThread();
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    if (status == Status.CLEARED) {
      return;
    }
    cancel();
    // Resource must be released before canNotifyStatusChanged is called.
    if (resource != null) {
      releaseResource(resource);
    }
    if (canNotifyCleared()) {
      target.onLoadCleared(getPlaceholderDrawable());
    }
    // Must be after cancel().
    status = Status.CLEARED;
  }

可以看到 clear() 方法中先是 执行了 cancel(),该方法会取消加载资源请求与该 Request 的回调关联。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  /**
   * Cancels the current load but does not release any resources held by the request and continues
   * to display the loaded resource if the load completed before the call to cancel.
   *
   * <p> Cancelled requests can be restarted with a subsequent call to {@link #begin()}. </p>
   *
   * @see #clear()
   */
  void cancel() {
    assertNotCallingCallbacks();
    stateVerifier.throwIfRecycled();
    target.removeCallback(this);
    status = Status.CANCELLED;
    if (loadStatus != null) {
      loadStatus.cancel();
      loadStatus = null;
    }
  }

LoadStatus 实际上只是持有了回调和 EngineJob。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * A callback that listens for when a resource load completes successfully or fails due to an
 * exception.
 */
public interface ResourceCallback {

  /**
   * Called when a resource is successfully loaded.
   *
   * @param resource The loaded resource.
   */
  void onResourceReady(Resource<?> resource, DataSource dataSource);

  /**
   * Called when a resource fails to load successfully.
   *
   * @param e a non-null {@link GlideException}.
   */
  void onLoadFailed(GlideException e);
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  /**
   * Allows a request to indicate it no longer is interested in a given load.
   */
  public static class LoadStatus {
    private final EngineJob<?> engineJob;
    private final ResourceCallback cb;

    LoadStatus(ResourceCallback cb, EngineJob<?> engineJob) {
      this.cb = cb;
      this.engineJob = engineJob;
    }

    public void cancel() {
      engineJob.removeCallback(cb);
    }
  }

EngineJob 是负责加载资源,并加载成功后回调回去,这里 SingleRequest 实现了回调,所以它便可得知资源加载完成并获取到。这里不再分析 EngineJob 实现,以免偏离主流程太远。

所以 cancel() 调用后,即使旧的加载请求完成也不会回调到 Tareget 上。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    target.setRequest(request);
    requestManager.track(target, request);

方法中,Target 持有了最新的 request , requestManager.track() 方法测触发了 request 的加载请求,实际是由内部 Engine 和 EngineJob 负责。当顺利加载成功后便回调到 Target 对象上,触发 target.onResourceReady(result, animation) 方法,图片便被正确显示出来了。

实际上,还是有很多细节流程。这里只是大概介绍了主流程,希望对大家有所帮助。

以上代码基于 glide v4.7.1 版本

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018年08月26日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Glide4.8版本中,Glide是如何加载网络美女图片
目前市场上主流的图片加载框架就是glide和fresco,个人觉得深入学习一款就可以,glide就是我的选择,在maven上可以看到Glide项目已经到4.11版本了,由于公司项目还是4.8版本,目前就以4.8版本为例,分析一下Glide是如何加载到网游美女图片。写这个文章主要是为了学习Glide的网络图片资源加载流程。
包子388321
2020/06/17
1.9K0
锦囊篇|一文摸懂Glide
和之前的文章会有一定的不同,这主要是因为Glide自身的源码量导致的问题,因为我是最后写的前言,你会发现在文章刚开始时会代码复制的比较完全,后面就比较零散,而且一部分我直接用自己话去进行了表述。如果真的要看懂,建议还是对着Glide的源码进行查看,这样会帮助你更好去理解GLide的它的实现流程。
ClericYi
2020/06/23
9710
Glide源码之基本流程加载
做android的我想大家都知道Glide,他的api很简单,让开发者可以很简单就实现加载一张图片,里面的下载,缓存都是框架内部处理好了,开发者可以快速处理自己的业务,Glide虽然用起来简单,可以源码一点都不简单,看Glide源码一点都不轻松,里面的代码很复杂,这一篇先来理清Glide的图片基本加载流程。
HelloJack
2021/11/24
8620
Android | 《看完不忘系列》之Glide
《看完不忘系列》将以从树干到细枝的思路来分析一些技术框架,本文是开篇文章,将对开源项目Glide图片加载库进行介绍。如果老铁们看完还是忘了,就 回来揍我一顿 点赞收藏加关注,多看两遍~
Holiday
2020/08/10
6720
Android Glide加载图片、网络监听、设置资源监听
  在日常开发中使用url加载图片是常见的。这也是Glide图片加载框架这么受欢迎的原因。当然本文如果只是简单的加载一个图片出来那就完全没有必要了,自然要搞点花里胡哨的事情才行。补充知识:Glide (音译:哥来德)
晨曦_LLW
2021/01/05
5.7K0
Android Glide加载图片、网络监听、设置资源监听
Glide源码阅读理解一小时
这篇图、文、表、代码一起组成的 Glide 源码分析的文章是在上一篇文章 Android-Universal-Image-Loader源码分析 中之后的又一篇图片加载框架源码解析,它也具备了 ImageLoader 中讲述了Android一个图片加载库所需要的一些基础必备的:MemoryCahce、DiskCahce Decoder DownLoader 和Executor 等部分。这篇 Glide 的代码分析量可以说至少是 ImageLoader 的3倍多,本来想对 Glide 代码进行拆分,细化每个部分进行讲解这个每个部分讲的更加清楚一些。但最终还是打算整体一篇文章讲完,因为我觉得整体性的学习能更深的的了解到 Glide 的框架的设计之美。
静默加载
2020/05/29
2.5K0
Glide源码解析(三)
本篇是 Glide 系列的最后一篇,主要讲一下 into 方法里面的逻辑。into 的逻辑也是最多最复杂的,可能需要反复阅读源码才能搞清楚。
俞其荣
2019/08/12
1.4K0
Glide源码分析(一)
Glide作为一个图片加载框架深受开发者喜欢,包体积小,加载速度快,以及加载圆角等。作为一名开发者我们有必要去了解Glide图片加载机制,它是如何把图片加载出来的?以及在图片加载过程中它都做了什么?
CatEatFish
2020/07/09
1.5K0
Glide源码分析(一)
Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程
用户1158055
2018/01/08
2.7K0
Glide4.0源码全解析(三),into()方法背后的故事
接下来直接看.into(),注释中会详细讲解 代码精简过,按照点用顺序依次排列,方便大家阅读。
先知先觉
2019/01/21
1.5K0
Android源码分析:这是一份详细的图片加载库Glide源码讲解攻略
注:从上面可看出,Glide不仅解决了 图片异步加载 的问题,还解决了Android加载图片时的一些常见问题,功能十分强大。
Carson.Ho
2019/02/22
1.3K0
[Glide4源码解析系列] — 2.Glide数据模型转换与数据抓取
上一篇文章,我们梳理了一遍Glide的初始化流程,看到了Gilde在Glide#with一句简单的代码背后做了巨大的准备工作,而这所有的准备工作,都是为了接下来顺利地完成数据解析和显示做了铺垫。
开发的猫
2020/04/01
1K0
懵了,面试官突然问我:Glide是干啥的?我对着那Glide新版本就是一顿暴讲
不是因为没时间,是 Glide 涉及太过广泛,内部逻辑太过犀利。一直没能找到一个合适的制高点来俯览全身。
Android技术干货分享
2020/11/13
3K0
懵了,面试官突然问我:Glide是干啥的?我对着那Glide新版本就是一顿暴讲
深入解析Glide源码
Glide 是 Google的开源项目, Glide具有获取、解码和展示视频剧照、图片、动画等功能,它还有灵活的API,这些API使开发者能够将Glide应用在几乎任何网络协议栈里。创建Glide的主要目的有两个,一个是实现平滑的图片列表滚动效果,另一个是支持远程图片的获取、大小调整和展示。本篇博客,我们一起深入分析Glide的源码。
老马的编程之旅
2022/06/22
8090
深入解析Glide源码
源码阅读--Glide
参考资料:http://www.apkbus.com/blog-705730-60158.html 用法:
提莫队长
2019/02/21
5460
Glide 4.0.0 RC0 使用详解
demo下载地址: http://download.csdn.net/detail/github_33304260/9863653
先知先觉
2019/01/21
1.1K0
Android图片加载框架最全解析(八),带你全面了解Glide 4的用法
用户1158055
2018/01/08
2.7K0
Android图片加载框架最全解析(八),带你全面了解Glide 4的用法
android点击全屏预览照片第三方库使用
移动端我们经常会遇到放大预览照片,如果是一张照片,那就全屏展示图片就好了,但是如果是一个列表,滑动查看,我们一般会借助viewpager进行实现,但是每次自己弄,感觉效率很低,今天给大家推荐一个第三方库,很轻松实现,扩展也还可以哦。
用户2235302
2018/08/10
1.4K0
android点击全屏预览照片第三方库使用
Android图片加载框架最全解析(四),玩转Glide的回调与监听
用户1158055
2018/01/08
2.7K0
Android图片加载框架最全解析(四),玩转Glide的回调与监听
glide的三个坑
崩溃log只有系统层面的堆栈,这个问题在我之前文章已经有分析过了,原因是因为glide主动回收了bitmap导致的(当然也有可能是其他代码异常,不过我之前项目线上的这种崩溃,最终排查,都是glide导致的)
韦东锏
2021/09/29
2.1K0
glide的三个坑
相关推荐
Glide4.8版本中,Glide是如何加载网络美女图片
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文