Glide源码解析

背景

目前得物的图片库主要是基于Fresco的自研图片加载库,但是Glide源码的精妙之处我们也有很多值得学习的地方。今天跟一下源码,和小伙伴们分享下我的理解。

基操

Glide.with(context).load("http://cdnxxx.png").into(imgThumb)

什么导包啥的我们就不赘述了,直接干,拿到ImageView去展示服务端图片。大概步骤如下
在这里插入图片描述
下面我们追踪一下这3个函数:

with()

在这里插入图片描述
跟到一个有代表性的重载方法里面看:

public RequestManager get(@NonNull View view) {
//如果是在子线程,则生命图片加载的生命周期为application
  if (Util.isOnBackgroundThread()) {
    return get(view.getContext().getApplicationContext());
  }
  //.......省略代码.......
  Activity activity = findActivity(view.getContext());
  // The view might be somewhere else, like a service.
  //这个代码也很好理解,不赘述
  if (activity == null) {
    return get(view.getContext().getApplicationContext());
 
 //下面两个是对称的,兼容support库(androidx)中的fragment和.app下面的fragment
  if (activity instanceof FragmentActivity) {
    Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
    return fragment != null ? get(fragment) : get(activity);
  }

  android.app.Fragment fragment = findFragment(view, activity);
  if (fragment == null) {
    return get(activity);
  }
  return get(fragment);
}
private RequestManagerFragment getRequestManagerFragment(
    @NonNull final android.app.FragmentManager fm,
    @Nullable android.app.Fragment parentHint,
    boolean isParentVisible) {
  RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
  if (current == null) {
  //创建一个空白fragment来监听生命周期,和jetpack里面的lifeCycle类似
  //这里考虑到fm的commit是一个异步操作,所以有多层check
  //先从map里面去取,取不到再创建对象,然后塞到map里面,防止此时再Glide.with()进来
  //造成创建多个Fragment来监听生命周期的bug
    current = pendingRequestManagerFragments.get(fm);
    if (current == null) {
      current = new RequestManagerFragment();
      current.setParentFragmentHint(parentHint);
      if (isParentVisible) {
        current.getGlideLifecycle().onStart();
      }
      pendingRequestManagerFragments.put(fm, current);
      fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
      handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
    }
  }
  return current;
}

在这里插入图片描述
空白fragment通过lifecyle感知后通知给到所有实现了该接口的类

load()

在这里插入图片描述
又是一堆重载,但是可以看到很明显有我们常用的load("http://xxxcdn/.png")
跟下来只是在RequestBuilder类里面就结束了。意犹未尽,其实下面的into才是重点

into()
public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
//........省略成吨代码...........
//这里把配置的requestOption、imageViewTarget、线程池往下传 
  return into(
      glideContext.buildImageViewTarget(view, transcodeClass),
      /*targetListener=*/ null,
      requestOptions,
      Executors.mainThreadExecutor());
}
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()");
  }

//这里构建了一个req,即代表有一个图片需要显示
  Request request = buildRequest(target, targetListener, options, callbackExecutor);
  //......省略.......
  requestManager.clear(target);
  target.setRequest(request);
  //跟主线流程>>>>>>>>
  requestManager.track(target, request);

  return target;
}
synchronized void track(@NonNull Target<?> target, @NonNull Request request) {
  targetTracker.track(target);
  requestTracker.runRequest(request);
}
public void runRequest(@NonNull Request request) {
//把当前请求加入到set集合,如果
  requests.add(request);
  //这里的isPaused稍微跟一下代码,就可以发现是activity/fragment的onStop被触发
  if (!isPaused) {
    request.begin();
  } else {
    pendingRequests.add(request);
  }
}
public synchronized void begin() {
//.....省略.....
//状态是已解码完成状态,则直接回调到资源准备好了,可以显示
  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);
  }
}
public synchronized void onSizeReady(int width, int height) {
//.......省略...........
  loadStatus =
      engine.load(
          glideContext,
          //...省略...
          callbackExecutor);
//.......省略.........
}
public synchronized <R> LoadStatus load(
    GlideContext glideContext,
    //...省略....
    Executor callbackExecutor) {
//这里可以看到Glide的Key是怎么生成的!明显可以看到和数据、签名、宽高、配置都有关系
  EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations,
      resourceClass, transcodeClass, options);
//这里第一步就是从活动内存里面看看是否已经缓存了
//final Map<Key, ResourceWeakReference> activeEngineResources = new HashMap<>();
  EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
  if (active != null) {
  //有缓存了,立马回调回去说数据准备好了!
    cb.onResourceReady(active, DataSource.MEMORY_CACHE);
    return null;
  }
//如果活动缓存里面没有,那么从内存缓存里面查找,这里可以看到,会从内存中找到后剪切到活动内存!!
//private EngineResource<?> getEngineResourceFromCache(Key key) {
//Resource<?> cached = cache.remove(key);
//}
//activeResources.activate(key, cached);
  EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
  if (cached != null) {
    cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
    return null;
  }

  EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
  if (current != null) {
    current.addCallback(cb, callbackExecutor);
    return new LoadStatus(cb, current);
  }
//这里多态用的炉火纯青!
  engineJob.start(decodeJob);
  return new LoadStatus(cb, engineJob);
}
public synchronized void start(DecodeJob<R> decodeJob) {
  this.decodeJob = decodeJob;
  GlideExecutor executor = decodeJob.willDecodeFromCache()
      ? diskCacheExecutor
      : getActiveSourceExecutor();
  executor.execute(decodeJob);
}

在这里插入图片描述

public void loadData(@NonNull Priority priority,
    @NonNull DataCallback<? super InputStream> callback) {
    //一顿追踪,终于看到通过url获取到InputStream了!!!
  try {
    InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
    //拿到inputStream后立马回调出去
    callback.onDataReady(result);
  } catch (IOException e) {
    callback.onLoadFailed(e);
  } 
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
    Map<String, String> headers) throws IOException {
    //......省略.......
  urlConnection = connectionFactory.build(url);
  final int statusCode = urlConnection.getResponseCode();
  if (isHttpOk(statusCode)) {
    return getStreamForSuccessfulRequest(urlConnection);
    }
}

终于看到网络接口的请求了,这里用的是HttpUrlConnection,累惨了!绕了几十个弯
在这里插入图片描述

@Override
public void onDataFetcherReady(Key sourceKey, Object data, DataFetcher<?> fetcher,
    DataSource dataSource, Key attemptedKey) {
    try {
    //拿到inputStream之后回调到这里,开始解码图片
      decodeFromRetrievedData();
  }
}
public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
  if (transition == null || !transition.transition(resource, this)) {
    setResourceInternal(resource);
  } else {
    maybeUpdateAnimatable(resource);
  }
}
private void setResourceInternal(@Nullable Z resource) {
  setResource(resource);
}
protected void setResource(@Nullable Drawable resource) {
  view.setImageDrawable(resource);
}

最终。。。。走到了imageview.setImageDrawable()。中间我忽略了很多显而易见的代码,完整的into逻辑方法时序图可以参考下面

时序图

在这里插入图片描述

思考

通过上面代码跟一跟,可以了解我们开发过程中可能遇到的问题

  • 加载图片内存泄漏的问题
  • 图片资源被回收造成的崩溃问题

参考

《享学课堂》Derry老师的Glide讲解
https://muyangmin.github.io/glide-docs-cn/
https://www.jianshu.com/p/9bb50924d42a

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alex_ChuTT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值