Android Glide的源码三线主线分析

待兔
• 阅读 8

Here's the Markdown version of your article:

# Glide流程分析

说到图片加载框架,大家最熟悉的莫过于Glide了,**但我却不推荐简历上写熟悉Glide,** 除非你熟读它的源码,或者参与Glide的开发和维护。然而很多小伙伴对于Glide的流程及其源码解读总是无从下手,**本篇就从三条主线来分析一下Glide流程及源码!**

## 第一条主线

**加入队列流程:**

```java
RequestManager with = Glide.with(this);
RequestBuilder<Drawable> load = with.load(url);
load.into(iv);   // 前面的暂时先不看,当调用into方法后,说明加载图片的请求才真正开始

继续调用

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

继续跟踪,会发现以下代码

requestManager.clear(target);
target.setRequest(request);
requestManager.track(target, request);//发送请求开始的地方

void track(Target<?> target, Request request) {
  targetTracker.track(target);
  requestTracker.runRequest(request);//从名字看叫运行请求
}

继续跟踪

通过该方法得知Glide也有两个队列;运行队列和等待队列;
public void runRequest(Request request) {
  requests.add(request);//加入运行队列;
  if (!isPaused) {
    request.begin();//开始执行
  } else {
    pendingRequests.add(request);//加入等待队列
  }
}

第二条主线

请求如何运行?

在第一条主线中,request.begin()方法就是真正开始执行请求的时候;先找到request的实现类:SingleRequest,找到其begin方法;

为什么找到的是SingleRequest?

在第一条主线的RequestBuilder.into方法中有一句代码;

Request request = buildRequest(target, targetListener, options);

继续跟踪它

buildRequestRecursive() 找到构建request的方法;

在该方法中,又能跟踪到

Request mainRequest = buildThumbnailRequestRecursive()

继续跟踪

Request fullRequest =
    obtainRequest(
        target,
        targetListener,
        requestOptions,
        coordinator,
        transitionOptions,
        priority,
        overrideWidth,
        overrideHeight);

上述代码块最终调用的是SingleRequest.obtain()方法,从而得到一个SingleRequest对象;所以能得出结论,request.begin()方法被调用时,即调用了SingleRequestbegin方法;继续跟踪begin方法,会发现onSizeReady方法;

onSizeReady(overrideWidth, overrideHeight);

begin方法中跟踪到engine.load方法,如下(只抽取了部分代码):

// 从活动缓存中获取
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
    if (active != null) {
      cb.onResourceReady(active, DataSource.MEMORY_CACHE);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Loaded resource from active resources", startTime, key);
      }
      return null;
    }

// 从内存缓存中获取
    EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
    if (cached != null) {
      cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Loaded resource from cache", startTime, key);
      }
      return null;
    }
// 硬盘缓存,硬盘缓存也是io操作,所以也使用了线程池;动画线程池
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb);
      if (Log.isLoggable(TAG, Log.VERBOSE)) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }

    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); //具体的加载,engineJob为加载管理类,decodeJob则为将返回的图片数据进行编码管理的类;

调用engineJob.start()方法后,则会执行以下代码:

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

继续跟踪找到DecodeJobrun方法;

DecodeJob.run() 继续调用 runWrapped(); 再继续调用getNextGenerator()

private DataFetcherGenerator getNextGenerator() {
    switch (stage) {
      case RESOURCE_CACHE:
        return new ResourceCacheGenerator(decodeHelper, this);
      case DATA_CACHE:
        return new DataCacheGenerator(decodeHelper, this);
      case SOURCE:
      // 根据主线我们目前都先不去处理跟Cache相关的类,直接进入SourceGenerator;这里使用了设计模式-状态模式;(请自行根据第二节内容进行查询)
        return new SourceGenerator(decodeHelper, this);
      case FINISHED:
        return null;
      default:
        throw new IllegalStateException("Unrecognized stage: " + stage);
    }
  }

继续跟踪到SourceGenerator类中的startNext方法;

loadData.fetcher.loadData(helper.getPriority(), this);

根据fetcher找到HttpUrlFetcher,并找到对应的loadData方法;最终发现Glide是通过HttpUrlConnection访问的服务器,并返回最终的stream

问题来了?我怎么知道是这个类的?为什么不是其他类?在这里代码就看不懂了,怎么办?猜测;

既然应该不是再继续从缓存拿,而应该要去访问网络了;所以找到具体访问网络的;发现找不到,怎么办?

Android Glide的源码三线主线分析

找它的实现类,有一个HttpUrlFetcher,那它在哪里初始化的?

Android Glide的源码三线主线分析

通过Find Usages找到哪里调用了--->找到了HttpGlideUrlLoader

再看这个方法HttpGlideUrlLoader哪里调用了;

Android Glide的源码三线主线分析

找到了Glide,继续往上寻找,找打了Glide种的build方法 ,找就能找到Glide.get(context);方法

第三条主线

队列怎么维护的?在MainActivity中我们调用了如下代码:

RequestManager with = Glide.with(this);

继续跟踪到

getRetriever(activity).get(activity)//这里得到了一个RequestManagerRetriever对象,再通过RequestManagerRetriever调用get方法得到RequestManager

继续往下

androidx.fragment.app.FragmentManager fm = activity.getSupportFragmentManager();
return this.supportFragmentGet(activity, fm, (Fragment)null);

通过this.supportFragmentGet方法(如下代码),最终我们得到SupportRequestManagerFragment对象;

private RequestManager supportFragmentGet(@NonNull Context context, @NonNull androidx.fragment.app.FragmentManager fm, @Nullable Fragment parentHint) {
    SupportRequestManagerFragment current = this.getSupportRequestManagerFragment(fm, parentHint);//这段代码的内部如果能够得到Fragment就得到,得不到就重新new一个,并且这个fragment中没有进行任何的UI处理;
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
        Glide glide = Glide.get(context);
        requestManager = this.factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
        current.setRequestManager(requestManager);
    }

    return requestManager;
}

得到Fragment对象后,再将RequestManager对象赋值进去,如果RequestManager为空,则帮助创建;

RequestManager对象则是生命周期管理的重要一环,因为它实现了LifecycleListener接口,并且在创建RequestManager的时候,会将这个接口设置给自己;也就意味着,Glide创建了一个无UI的fragment,这个fragment又与RequestManager进行绑定;当用户的activity或者fragment被调用,系统会自动调用fragment的生命周期方法;而生命周期方法中又会回调LifecycleListener的方法,进而调用RequestManagerRequestManager则也拥有了生命周期;

RequestManageronStart方法被调用后,会通过一系列的调用,将运行中的请求全部放开,进行访问;

onStop方法被调用时,则将运行中队列的数据取出来,如果当前请求正在运行则暂停,然后将所有的数据从运行队列中添加到等待队列中去;

onDestory方法被调用时,则将运行队列和等待队列中的数据全部清除;再将监听移除;将requestManager从Glide中的绑定关系解除;

点赞
收藏
评论区
推荐文章
非常实用的GitHub项目
CoilCoil是Android上的一个全新的图片加载框架,它的全名叫做coroutineimageloader,即协程图片加载库。与传统的图片加载库Glide,Picasso或Fresco等相比。该具有轻量(只有大约1500个方法)、快、易于使用、更现代的API等优势。它支持GIF和SVG,并且可以执行四个默认转换:模糊,圆形裁剪,灰度和圆角。示例如
希望的天 希望的天
4年前
Android-图片加载库Coil使用教程
框架介绍Coil是Android上的一个全新的图片加载框架,它的全名叫做coroutineimageloader,即协程图片加载库。与传统的图片加载库Glide,Picasso或Fresco等相比。该具有轻量(只有大约1500个方法)、快、易于使用、更现代的API等优势。它支持GIF和SVG,并且可以执行四个默认转换:模糊,圆形裁剪,灰度和圆角。
Wesley13 Wesley13
3年前
volley 实现验证码功能
  公司的项目,为防止机器注册,需增加验证码功能,开始以为只是一张图片,通过glide加载个地址就好,但接口的同事说验证码接口是返回一个流,因为服务端不应该做验证码图片的存储,因为验证是不停变动的,但公司的接口都是https,有相应的证书校验环节及头部信息校验,而项目中现存的网络请求是封装的volleyjson请求,只应该返回json的接口,之前了解的
阿邹 阿邹
4年前
Glide4.5分析
Glide4.5分析Glide的基本流程介绍常见调用方式Glide.with(context).load((T)url).into(imageView);这里调用了三个方法1.With2.Load3.IntoWith方法:首先进入Glide类中调用这个方法javapublicstaticRequestManage
Stella981 Stella981
3年前
Glide Golang包管理
Golang的包管理乱得不行,各种工具横空出世,各显神通啊。用了几个下来,发现Glide是比较好用的,使用了vender来进行管理,多个开发环境的版本不冲突,功能强大,配置文件也足够简单。初始化一个已有的工程想要引入glide进行管理→glidecreate这时,Glide会扫描工程中所有的文件并分析出依
Stella981 Stella981
3年前
Golang依赖管理工具:glide从入门到精通使用
介绍不论是开发Java还是你正在学习的Golang,都会遇到依赖管理问题。Java有牛逼轰轰的Maven和Gradle。Golang亦有godep、govendor、glide、gvt、gopack等等,本文主要给大家介绍gilde(https://www.oschina.net/action/GoToLink?urlhttps%3
Stella981 Stella981
3年前
Android:通过Glide保存图片到本地,并同步到相册
1save.setOnClickListener(newView.OnClickListener(){2@Override3publicvoidonClick(Viewv){4Glide.w
Stella981 Stella981
3年前
Android 网络url设置View背景图
imgstr为url网络图片地址,topllay是要设置背景的控件;方法1.AndroidGlide设置View背景图Glide.with(this).load(imgStr).asBitmap()//签到整体背景.into(newSimpleTarget<Bitmap(){
Stella981 Stella981
3年前
Glide
1.with函数可以传入activity,fragment,context,传入的参数会决定glide加载图片的生命周期,当activity,fragment被销毁的时候,加载就会停止  with函数首先判断了是否在主线程中使用,如果不在主线程中使用,那么传入的参数就相当于applicationContext,在主线程中使用,就要判断是不是applica
Wesley13 Wesley13
3年前
Linux服务器下的HTTP抓包分析
说到抓包分析,最简单的办法莫过于在客户端直接安装一个Wireshark或者Fiddler了,但是有时候由于客户端开发人员(可能是第三方)知识欠缺或者其它一些原因,无法顺利的在客户端进行抓包分析,这种情况下怎么办呢?本文中,我们将给大家介绍在服务端进行抓包分析的方法,使用tcpdump抓包,配合Wireshark对HTTP请求进行分析,非常简单有效。本