Glide4.5分析

阿邹 等级 608 0 0
标签: 移动端

Glide4.5分析

Glide的基本流程介绍

常见调用方式 Glide.with(context).load((T)url).into(imageView); 这里调用了三个方法

    1. With
    1. Load
    1. Into

With方法:

首先进入Glide类中调用这个方法

public static RequestManager with(@NonNull Context context) {
  return getRetriever(context).get(context);
}

/**
这里get方法中传进去一个context然后闲进去非空判断
再判断是否再主线程中其次再判断context不能等于全局上下文
    这里说下为什么,如果是全局上下文不利于资源的销毁
然后再走,判断上下午是哪个类型,最后相应的返回。
*/
@NonNull
public RequestManager get(@NonNull Context context) {
  if (context == null) {
    throw new IllegalArgumentException("You cannot start a load on a null Context");
  } else if (Util.isOnMainThread() && !(context instanceof Application)) {
    if (context instanceof FragmentActivity) {
      return get((FragmentActivity) context);
    } else if (context instanceof Activity) {
      return get((Activity) context);
    } else if (context instanceof ContextWrapper) {
      return get(((ContextWrapper) context).getBaseContext());
    }
  }

  return getApplicationManager(context);
}

/**
如果上面的context是全局上下文,则会调用getApplicationManager方法,构造一个ApplicationManager对象返回。
ApplicationLifecycle是生命周期接口
EmptyRequestManagerTreeNode是树节点接口
public Set<RequestManager> getDescendants() {
    return Collections.emptySet();
}
看里面的方法应该是保存了许多RequestManager节点,负责取出
构建完毕返回

*/
private RequestManager getApplicationManager(@NonNull Context context) {
  // Either an application context or we're on a background thread.
  if (applicationManager == null) {
    synchronized (this) {
      if (applicationManager == null) {
        // Normally pause/resume is taken care of by the fragment we add to the fragment or
        // activity. However, in this case since the manager attached to the application will not
        // receive lifecycle events, we must force the manager to start resumed using
        // ApplicationLifecycle.

        // TODO(b/27524013): Factor out this Glide.get() call.
        Glide glide = Glide.get(context.getApplicationContext());
        applicationManager =
            factory.build(
                glide,
                new ApplicationLifecycle(),
                new EmptyRequestManagerTreeNode(),
                context.getApplicationContext());
      }
    }
  }

  return applicationManager;
}

这里又用到了RequestManagerRetriever类。 看这个类的注释 *创建新{@link com.bumptech.glide的静态方法集合。RequestManager}年代或 *从活动和片段中检索已有的。 应该是个工具类。

最后得到一个RequestManager对象

if (context instanceof FragmentActivity) {
  return get((FragmentActivity) context);
} else if (context instanceof Activity) {
  return get((Activity) context);
} else if (context instanceof ContextWrapper) {
  return get(((ContextWrapper) context).getBaseContext());
}
看上面的代码,对不同的activity进行不同的处理,
@NonNull
public RequestManager get(@NonNull FragmentActivity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity);
    FragmentManager fm = activity.getSupportFragmentManager();
    return supportFragmentGet(activity, fm, null /*parentHint*/);
  }
}
@NonNull
public RequestManager get(@NonNull Fragment fragment) {
  Preconditions.checkNotNull(fragment.getActivity(),
        "You cannot start a load on a fragment before it is attached or after it is destroyed");
  if (Util.isOnBackgroundThread()) {
    return get(fragment.getActivity().getApplicationContext());
  } else {
    FragmentManager fm = fragment.getChildFragmentManager();
    return supportFragmentGet(fragment.getActivity(), fm, fragment);
  }
}

@NonNull
public RequestManager get(@NonNull Activity activity) {
  if (Util.isOnBackgroundThread()) {
    return get(activity.getApplicationContext());
  } else {
    assertNotDestroyed(activity);
    android.app.FragmentManager fm = activity.getFragmentManager();
    return fragmentGet(activity, fm, null /*parentHint*/);
  }
}

看上面的: get(FragmentActivity activity) get (Activity activity) 方法都会闲判断一下是否是后台线程 if (Util.isOnBackgroundThread()) 如果是后台线程最后会执行getApplicationManager(Context context)方法,也就是我们的我们上面最开始说的那个方法。否则,则都会创建一个fragment添加到活动中,也就是glide中的SupportRequestManagerFragment类,为什么要弄fragment绑定活动中,简答来看看SupportRequestManagerFragment的代码。

public class SupportRequestManagerFragment extends Fragment {


  /**
   * Returns true if the fragment is a descendant of our parent.
   */
  private boolean isDescendant(Fragment fragment) {
    Fragment root = this.getParentFragmentUsingHint();
    Fragment parentFragment;
    while ((parentFragment = fragment.getParentFragment()) != null) {
      if (parentFragment.equals(root)) {
        return true;
      }
      fragment = fragment.getParentFragment();
    }
    return false;
  }

  private void registerFragmentWithRoot(FragmentActivity activity) {
    unregisterFragmentWithRoot();
    rootRequestManagerFragment = Glide.get(activity).getRequestManagerRetriever()
        .getSupportRequestManagerFragment(activity.getSupportFragmentManager(), null);
    if (!this.equals(rootRequestManagerFragment)) {
      rootRequestManagerFragment.addChildRequestManagerFragment(this);
    }
  }

  private void unregisterFragmentWithRoot() {
    if (rootRequestManagerFragment != null) {
      rootRequestManagerFragment.removeChildRequestManagerFragment(this);
      rootRequestManagerFragment = null;
    }
  }

  @Override
  public void onAttach(Context context) {
    super.onAttach(context);
    try {
      registerFragmentWithRoot(getActivity());
    } catch (IllegalStateException e) {
      // OnAttach can be called after the activity is destroyed, see #497.
      if (Log.isLoggable(TAG, Log.WARN)) {
        Log.w(TAG, "Unable to register fragment with root", e);
      }
    }
  }

  @Override
  public void onDetach() {
    super.onDetach();
    parentFragmentHint = null;
    unregisterFragmentWithRoot();
  }

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

上面的内容有删除,我们这里重点关注一下生命周期里面调用的相应的接口。对这里在监听faagment的生命周期,我们知道activity销毁的时候fragment也会销毁,那么我们监听fargment也就拿到了activity的生命周期,这样在activity销毁的时候我们就可以对图片资源加载进行相应的操作,比如当我们的activity销毁以后他的图片就i没必要再进行加载了,可以进行相应的停止。

Load方法

Load方法用来设置我们的url地址 调用的方法都在RequestBuilder类中的 我们可以看到load方法后调用了loadGeneric方法 最终把赋值给了model字段,然后更改了一个变量的状态,看这个变量名称意思应该是是否设置model,现在是为true。

public RequestBuilder<TranscodeType> load(@Nullable Object model) {
  return loadGeneric(model);
}

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

into方法

into方法开始也是在RequestBuild类中开始调用 可以看到RequestBuild中有泛型,这泛型稍后再去理解。 这边into方法中传了一个imageview对象,根据注释我们可以了解到,当进行into方法的时候就说明开始启动了图片加载,如果这个view之前正在加载中则会取消之前的任务,重新开始任务。 进去into方法以后可以看到首先就行了一些裁剪方面的设置,判断完之后return时候调用的into方法才是重头戏。 Return调用的into方法有三个参数,第二是null,第三个是请求参数构造(请求的选项)第一个参数分析

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

分析一下glideContext.buildImageViewTarget()

@NonNull
public <X> ViewTarget<ImageView, X> buildImageViewTarget(
    @NonNull ImageView imageView, @NonNull Class<X> transcodeClass) {
  return imageViewTargetFactory.buildTarget(imageView, transcodeClass);
}

接着我们就来来到了ImageViewTargetFactory类,里面就这么一个方法bildTarget,来看看这个方法事干嘛的,很显然事返回图片的类型,这里有俩种Bitmap,Drawable。最后返回了一个ViewTaget对象。视图目标对象。

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

分析完第一次参数以后接着走来到了下面的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;
}

先看这个

判断

if (request.isEquivalentTo(previous)
    && !isSkipMemoryCacheWithCompletePreviousRequest(options, previous))

第一个request与previous是否相等,第二个 !options.isMemoryCacheable() && previous.isComplete() 第二个第一个是否内存缓存 第二个第二个previous请求是否完成,只有当不使用内存缓存以及previous请求完成的时候才能过。 进来了就先释放掉request资源,然后再检查previous是否为空,不为空是否在运行,如果不再运行则发起请求。 接着往下面走,先在requestManager管理中清除这个target目标,然后重新发起。 现在我们接着看requestManager.track()方法。

void track(Target<?> target, Request request) {
  targetTracker.track(target);
  requestTracker.runRequest(request);
}
接着看requestTracker.runRequest()方法。
public void runRequest(Request request) {
  requests.add(request);
  if (!isPaused) {
    request.begin();
  } else {
    pendingRequests.add(request);
  }
}

这个方法的注释也明确的告诉了我们 开始跟踪给定的请求。这里有一个参数isPauesd默认事为true,那么我们当启动app第一次进来的时候请求就不会在这里被执行,而是都添加集合中等待执行。这里俩个集合说明一下。 private final Set requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>()); private final List pendingRequests = new ArrayList<>(); 第一个集合requests是请求集合 弱引用 第二个集合pendingRequests是待请求集合 强引用 第一个就是为了发送执行,第二个的作用就是为了保持引用,防止弱引用被回收。

那么上面我说了isPaused这个字段第一次运行默认是为false的,那么虽然你添加进去了,但是这里不会去执行操作,这个开始网络请求的操作在哪里开始执行呢,当我们第一次进来的时候,

public void resumeRequests() {
  isPaused = false;
  for (Request request : Util.getSnapshot(requests)) {
    if (!request.isComplete() && !request.isCancelled() && !request.isRunning()) {
      request.begin();
    }
  }
  pendingRequests.clear();
}

RequestManager:
public void onStart() {
  resumeRequests();
  targetTracker.onStart();
}

没错当我们第一进来的时候是在这里开始执行我们的网络请求的,当我们的生命周期走到了onStart的时候,就会把原本等待队列中的网络请求开始执行。这也对应了我们生命周期的方法,生命周期存在的时候发送网络请求,不然界面都没有你加载图片也没意义,那么当我们生命周期开始走到销毁的时候就会停止一切图片加载,界面都要销毁了你还加载图片干嘛,这不是浪费资源吗。这里对于怎么监听activity的生命周期上面也有介绍就是添加了一个fragment和activity绑定。 当第一次以后我们的ispaues会变成true状态,所以之后我们进行的图片加载会直接发送网络请求,不会进入到强引用队列中等待。

END

Glide的缓存机制刨析

我们自己都知道,如果你来写一个图片加载框架,肯定会想到做缓存减少不必要的网络请求,所以glide也不例外。

待续

收藏
评论区

相关推荐

带你入门前端工程:微前端
什么是微服务?先看看维基百科(https://zh.wikipedia.org/wiki/%E5%BE%AE%E6%9C%8D%E5%8B%99)的定义: 微服务(英语:Microservices)是一种软件架构风格,它是以专注于单一责任与功能的小型功能区块 (Small Building Blocks) 为基础,利用模块化的方式组合出复杂的大型应用
前端 后端的区别
前台:眼睛看到的东西 关乎体验感。 后台:给管理人看的玩意 前端:程序员在进行编程的时候的代码编写。 后端:对应前端而言的,编写代码基本上是提供给前端调用,是不需要处理UI的内容.
Glide4.5分析
Glide4.5分析Glide的基本流程介绍常见调用方式Glide.with(context).load((T)url).into(imageView);这里调用了三个方法 1. With 2. Load 3. Into With方法:首先进入Glide类中调用这个方法javapublic static RequestManage
Android客户端上传文件到服务器端
/** * 上传文件 */ public static String sendFile(String urlPath, String filePath, String newName) throws Exception {
Android端Charles抓包
#### 目录介绍 * 01.下载安装 * 02.抓包代理设置 * 03.抓包Https操作 * 04.抓包原理介绍 * 05.抓包数据介绍 * 06.常见问题总结 * 07.Android拦截抓包 ### 01.下载安装 * 下载地址(下载对应的平台软件即可) * [https://www.charlesp
Apache Flink 结合 Kafka 构建端到端的 Exactly
文章目录: ----- 1. Apache Flink 应用程序中的 Exactly-Once 语义 2. Flink 应用程序端到端的 Exactly-Once 语义 3. 示例 Flink 应用程序启动预提交阶段 4. 在 Flink 中实现两阶段提交 Operator 5. 总结 [Apache Flink](https://www.os
B端和C端销售差异
之前遇到过几个资深做销售的人,讨论起C端和B端销售问题。他们觉得销售是相同的呀,只有这个人销售能力强,销售B端管理软件也没有问题呀。每次都要费尽口舌跟他们解释B端管理软件销售的难点所在。虽然本身不是做销售出身,但在B端管理软件从业那么久,也是深知C端和B端的区别的,下面就具体解释说明一下。 **B端要求更多的专业知识,决策时间长,而C端正好相反** C端
Flink Kafka 端到端 Exactly
**摘要:** 本文基于 Flink 1.9.0 和 Kafka 2.3 版本,对 Flink kafka 端到端 Exactly-Once 进行分析及 notifyCheckpointComplete 顺序,主要内容分为以下两部分: 1.Flink-kafka 两阶段提交源码分析 * TwoPhaseCommitSinkFuncti
Go语言实现UDP服务端和客户端
### Go语言实现UDP服务端和客户端 * [UDP协议](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fccf19881030.blog.csdn.net%2Farticle%2Fdetails%2F109198887%23UDP_2) * [UDP服务端](https://
GraphQL 实战篇之前端Vue+后端
关注 前端瓶子君,回复“交流” 加入我们一起学习,天天进步 ![](https://oscimg.oschina.net/oscnet/26771a2d-b593-4188-a7c3-8e93cc9fe50d.jpg) > 作者:PatWu16 > > https://segmentfault.com/a/1190000039087433 这篇文章
Linux 终端 Pearls
.ssh li@10.175.68.121       #远程登陆 .ssh -l li 10.175.68.121 exit .scp -r /home/li/TEST/unpv13e pi@192.168.31.128:/home/pi/  在主机终端中运行,复制到pi, -r表递归 .ls | grep "$\*"    
Linux终端彩色打印+终端进度条
开发的一个应用程序选择了终端界面, 为了使软件稍微好看些, 研究下Linux终端的彩色打印, 并且基于这个彩色打印实现了几种进度条, 在此总结下: (更多的是觉得这个东西挺好玩的... ![](http://my.oschina.net/js/ke/plugins/emoticons/images/13.gif)) **一. Linux终端色彩打印:**
PC 端多端融合方案
一、 方案选型 ------- 3天时间写了个 PC 端应用程序。先看看结果吧 ![Todo1](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/2020-05-04-TodoApp1.png) ![Todo1](https://raw.githubu
Springboot+websocket实现服务端、客户端
点击上方蓝色字体,选择“标星公众号” 优质文章,第一时间送达 关注公众号后台回复**pay**或**mall**获取实战项目资料+视频 > 作者:IT贱男 > > 来源:https://jiannan.blog.csdn.net/article/details/86352677 一、引言 ---- 小编最近一直在使用springboot框
TP5中手机端和PC端判断
TP5中手机端和PC端判断 ============= 一、使用自定义的判定方法 ------------ 1. 首先在`application>common.php`公共文件中写入用于判定设备登录的ismobile方法: function ismobile() { // 如果有HTTP