Glide源码解析

Glide源码解析(一) 简介 由于在Android项目开发中我们经常会用到图片加载,你会选择什么第三库来加载图片,今天让我们来学习一下Glide图片加载库的源码吧,之前文章有讲解到Glide的简单使用。 简单使用 这里就不说添加依赖那些了,大家可以看官方文档,或者我之前的文章Glide的简单使用,但是版本现在最新的版本是4.8.0。 接下来我们看看Glide是如何加载图片的,如下代码 public class MainA... 阅读详情

Glide 源码解析



如果想看 Glide缓存机制的话, 点击这里。

当源码量大难懂的时候,看源码的思路:

  1. 只管主线,不管支线,不管支线,不管支线!(这次只看主线)
  2. 重点在主线,稍微瞄一眼支线。
  3. 稍微分析支线,回顾主线。
  4. 分析支线细节,回顾主线。
  5. 分析支线细节,寻找答案。

本次解析是基于Glide-4.11.0版本。

Glide.with(this).load(url).into(imageView).
  • with 创建一个空白的Fragment,来管理生命周期。

  • load 创建一个RequestBuilder对象。

  • into 主要做了一下功能:

  1. 运行队列,等待队列
  2. 活动缓存
  3. 内存缓存
  4. 网络模型

Glide的核心是:生命周期,缓存机制

Glide流程简化图

Glide流程简化图

问题
  1. 为什么去监听生命周期?

答:方便资源的及时回收。

  1. 项目中大量使用Glide 时,偶尔会出现内存泄漏的问题,大概是什么原因?

答:在用with的时候,尽量使用非application作用域,才能进行生命周期的监听,及时回收及释放等操作。

  1. Glide.with()在子线程加载时有什么问题?

答:不会添加空fragment监听生命周期,主线程才会添加。

  1. Glide的缓存为什么要有活动缓存和内存缓存?

答:活动缓存:页面正在显示的图片,必须访问的。内存缓存:LRU机制。都是运行时缓存。

如果页面在显示内存里的缓存图片,但这时LRU机制删除了这个缓存,彻底释放资源,就会报错。

所以制定了活动缓存,来存放页面必须用到的缓存。如果用到的缓存在内存缓存则会先移动到活动缓存中。

如果页面关闭掉,活动缓存中数据会再次移动到内存缓存中,等待下次使用。

with

子线程:application作用域,不会创建空白的Fragment。

主线程:非application作用域,会创建空白的Fragment。

绑定Fragment的时候,第一层判定会先从栈中通过TAG获取是否有fragment, map对fragment进行缓存作为第二层判定。

@NonNull
  private SupportRequestManagerFragment getSupportRequestManagerFragment(
      @NonNull final FragmentManager fm, 
      @Nullable Fragment parentHint, 
      boolean isParentVisible) {
    
    SupportRequestManagerFragment current =
        (SupportRequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);
    // 第一层判定
    if (current == null) {
      current = pendingSupportRequestManagerFragments.get(fm);
      // 第二次判定
      if (current == null) {
        current = new SupportRequestManagerFragment();
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        // 放入集合
        pendingSupportRequestManagerFragments.put(fm, current);
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        /*发送handler通知移除集合,因为fragment的commit就是通过handler执行,
          当handleMessage接收到移除集合的消息,说明fragment commit已经执行了。
          并且避免了内存泄漏。
        */
        handler.obtainMessage(ID_REMOVE_SUPPORT_FRAGMENT_MANAGER,fm).sendToTarget();
      }
    }
    return current;
  }

lifecycle (add,remove LifecycleListener)–>ApplicationLifecycle(绑定Application生命周期),ActivityFragmentLifecycle(绑定空白Fragment生命周期)

RequestManager 实现LifecycleListener接口,去通知其他类生命周期的改变。

public class RequestManager implements LifeCycleListener{
  
  /**
  * 开始所有任务
  * 运行队列全部开始执行,等待队列全部清空
  */
  @Override
  public synchronized void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  /**
   * 暂停所有任务
   * 运行队列全部停止,把任务转移到等待队列
   */
  @Override
  public synchronized void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }

  /**
   * 清空所有任务
   */
  @Override
  public synchronized void onDestroy() {
    targetTracker.onDestroy();
    for (Target<?> target : targetTracker.getAll()) {
      clear(target);
    }
    targetTracker.clear();
    requestTracker.clearRequests();
    lifecycle.removeListener(this);
    lifecycle.removeListener(connectivityMonitor);
    mainHandler.removeCallbacks(addSelfToLifecycle);
    glide.unregisterRequestManager(this);
  }
}
Load

主要返回一个 RequestBuilder 对象。待完善…

into

主线大致流程:

  1. ImageViewTarget(显示图片), 无论走多远,一定回到这里。
@NonNull
  public ViewTarget<ImageView, TranscodeType> into(@NonNull ImageView view) {
    Util.assertMainThread();
    Preconditions.checkNotNull(view);
    // 省略...
		
    return into(
      // 构建ImageViewTarget对象
        glideContext.buildImageViewTarget(view, transcodeClass),
        /*targetListener=*/ null,
        requestOptions,
        Executors.mainThreadExecutor());
  }

在上面的return的into方法中,会有下面的代码:

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

而buildRequest最终创建的是SingleRequest。这里面层级很深。这里只展示最终创建SingleRequest的地方。

  private Request obtainRequest(
      Target<TranscodeType> target,
      RequestListener<TranscodeType> targetListener,
      BaseRequestOptions<?> requestOptions,
      RequestCoordinator requestCoordinator,
      TransitionOptions<?, ? super TranscodeType> transitionOptions,
      Priority priority,
      int overrideWidth,
      int overrideHeight,
      Executor callbackExecutor) {
    // 享元设计模式
    return SingleRequest.obtain(
        context,
        glideContext,
        model,
        transcodeClass,
        requestOptions,
        overrideWidth,
        overrideHeight,
        priority,
        target,
        targetListener,
        requestListeners,
        requestCoordinator,
        glideContext.getEngine(),
        transitionOptions.getTransitionFactory(),
        callbackExecutor);
  }
  1. 经过requestManager.track()–>requestTracker.runRequest(request)–>request.begin()–>Request的实现类SingleRequest.begin()方法。在begin()方法中,会有下面的判断:
// 用户是否设置了宽和高
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {
   onSizeReady(overrideWidth, overrideHeight); // 设置了宽高
} else {
  // 用户没有设置宽和高,会再次测量-->再次调用onSizeReady
   target.getSize(this);
}
  1. onSizeReady()–>engine.load(),load里面会有加载缓存的判断:
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,
      Executor callbackExecutor) {
    long startTime = VERBOSE_IS_LOGGABLE ? LogTime.getLogTime() : 0;
// 1.构建key
    EngineKey key =
        keyFactory.buildKey(
            model,
            signature,
            width,
            height,
            transformations,
            resourceClass,
            transcodeClass,
            options);

    EngineResource<?> memoryResource;
    synchronized (this) {
      // 2.loadFromMemory会先后从 活动缓存和内存缓存 获取缓存
      memoryResource = loadFromMemory(key, isMemoryCacheable, startTime);

      if (memoryResource == null) {
        // 没有缓存
        return waitForExistingOrStartNewJob(
            glideContext,
            model,
            signature,
            width,
            height,
            resourceClass,
            transcodeClass,
            priority,
            diskCacheStrategy,
            transformations,
            isTransformationRequired,
            isScaleOnlyOrNoTransform,
            options,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache,
            cb,
            callbackExecutor,
            key,
            startTime);
      }
    }

    // 命中缓存,回调给外界
    cb.onResourceReady(memoryResource, DataSource.MEMORY_CACHE);
    return null;
  }

其中,活动缓存:页面正在显示的图片,必须访问的。内存缓存:LRU机制。(这里不详细说了)

5.waitForExistingOrStartNewJob()方法中会有

  private <R> LoadStatus waitForExistingOrStartNewJob(
      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,
      Executor callbackExecutor,
      EngineKey key,
      long startTime) {
// 1.是否有正在运行的任务
    EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
    if (current != null) {
      current.addCallback(cb, callbackExecutor);
      if (VERBOSE_IS_LOGGABLE) {
        logWithTimeAndKey("Added to existing load", startTime, key);
      }
      return new LoadStatus(cb, current);
    }
// 1.EngineJob是线程池大管家
    EngineJob<R> engineJob =
        engineJobFactory.build(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache);
// 2.执行的任务
    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, callbackExecutor);
    engineJob.start(decodeJob);

    if (VERBOSE_IS_LOGGABLE) {
      logWithTimeAndKey("Started new load", startTime, key);
    }
    return new LoadStatus(cb, engineJob);
  }

  1. engineJob.start(decodeJob)会执行 decodeJob中的run方法,run()-->runWrapped()-->getNextGenerator()默认会返回SourceGenerator–>runGenerators()–>SourceGenerator .startNext()–>helper.getLoadData() {–>modelLoader.buildLoadData()–>HttpGlideUrlLoader.buildLoadData()–>new LoadData<>(url, new HttpUrlFetcher(url, timeout))- HttpUrlFetcher 做网络请求 }
  2. 从上面的SourceGenerator .startNext()继续-->startNextLoad()-->HttpUrlFetcher.loadData()
public void loadData() {
  //loadDataWithRedirects方法就是网络请求
    InputStream result = loadDataWithRedirects(
      glideUrl.toURL(), 0, null, glideUrl.getHeaders());
    callback.onDataReady(result);
}
  1. result会一层一层向上回调,一层一层包装,onDataReady(result)-->SourceGenerator .onDataReadyInternal()-->DecodeJob.onDataFetcherReady()–>decodeFromRetrievedData()–>decodeFromData()-->decodeFromFetcher()–>runLoadPath()–>path.load()–>loadWithExceptionList()–>path.decode()–>decodeResource()–>decodeResourceWithList()–>decoder.decode()–>StreamBitmapDecoder.decode() 返回值Resource<Bitmap> 终于把stream转变为了Bitmap。
// 这里的Resource<Transcode>会被转成Resource<Bitmap>
  public Resource<Transcode> decode(
      DataRewinder<DataType> rewinder,
      int width,
      int height,
      @NonNull Options options,
      DecodeCallback<ResourceType> callback)
      throws GlideException {
    // 把stream转变为了Bitmap
    Resource<ResourceType> decoded = decodeResource(rewinder, width, height, options);
    // 回调
    Resource<ResourceType> transformed = callback.onResourceDecoded(decoded);
    return transcoder.transcode(transformed, options);
  }
  1. 然后回调会一层一层向上,并且会对bitmap进行各种操作,具体的链路不写了(妈的已经崩溃了…)。最终会会掉到ImageViewTarget去显示图片。
总结

Glide的源码看着很痛苦,非常多细节没有补充,有可能有不对的地方,希望大家能指正。在后续的学习中也会不断的完善。

读源码-Glide源码解析 文章目录1-初始化with()2-加载准备load()3-加载显示图片 into()4-Glide三级缓存机制5-总结 本文基于Glide版本: com.github.bumptech.glide:glide:4.11.0 Glide最常用的一行代码如下,也概括了Glide的初始化、加载图片(本地、缓存、网络图片)、绑定显示的流程。本文就从该行代码开启Glide的源码之旅。 Glide.with(this).load(url).into(mainPic); 1-初始化with() 首先是Glide.with 阅读详情

相关推荐

【Android】Glide基本用法及With方法源码解析

Glide是一个快速高效的Android图片加载库。专门用来帮助开发者从网络、文件系统或应用资源中加载图片,并显示在 `ImageView` 或其他 UI 组件上。它具备高效的缓存机制和解码能力,能够处理图片、GIF、视频缩略图等多种媒体文件。

Patrick_yuxuan的博客 2402

android gilde生命周期管理,Glide看点(二)巧用Fragment管理生命周期

Motivation/动机图片的一般是异步的,异步经常面临的问题是内存泄露和异步加载回来view已经销毁导致的空指针问题。而Glide在使用的时候只要求传入当前加载的view或者context,且没有用setLifeCyclelistener什么的方法就实现了生命周期的管理,它是如何做到的呢?Fragment Trick我们知道Fragment和View最大区别其实就是多了生命周期,Glide采用...

weixin_28810315的博客 664

Glide 源码解析与原理总结——Glide.with

Glide.with 原理:Glide 初始化并且生成RequestManager,RequestManager 与 空白Fragment 的 lifecycle 生命周期绑定来管理后续的请求。

xiaobaaidaba123的专栏 1485

Glide源码分析

目录Glide的源码分析Glide的使用With方法load方法into方法(重点)总结流程(简化)生命周期绑定的作用 不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵! Glide的源码分析 面试题:为什么不能在子线程调用with方法? 答:因为在子线程调用,不会产生一个空白的Fragment和Activity进行生命周期的绑定;可能导致Activity销毁了,图片请求资源仍然存在,导致内存泄漏。 面试题:为什么使用Glide框架需要网络请求权限? 答:如果活动缓存和内存缓存

小叮当不懒的博客 1985

Glide4 源码解析和原理

glide 4.8.0 1、with 方法 public static GlideRequests with(@NonNull Context context) public static GlideRequests with(@NonNull Activity activity) public static GlideRequests with(@NonNull FragmentAct...

xinnian25的专栏 502

Android-Glide源码解析(二)线程管理

Glide源码解析-线程管理

HuangLin的博客 1688

Glide图片框架源码解析

Glide图片框架源码解析

xuyin1204的博客 1636

glide源码解析

一.glide基础三步走:先with(),然后load(),最后into()。 1.with():可以传入activity,fragment,context 在Glide中调用RequestManagerRetriever的get方法,在get方法中传入context,或activity或fragment,最终会返回一个RequestManager对象。 如果是applicationContext则不需要特殊操作直接调用getApplicationManager创建RequestManager对象,生

龚礼鹏的博客 539

Glide:用法、原理和源码解析

Glide是一款强大的Android图片加载库,它提供了简单易用的API和高效的图片加载功能。本文将介绍Glide的用法、原理和源码解析,帮助开发者更好地理解和使用这个优秀的图片加载库。

陆业聪 5867

【可穿戴算法开发】-PPG信号检测方法

文章目录一、PPG(光电容积脉搏波法)信号的检测原理二.PPG技术目前存在的问题(挑战)三. **心电信号产生的原理** 随着经济的发展,在人们生活水平提高的同时,高脂肪摄入和低运动量成为了人们普遍的生活模式,近年来高血压、高血脂已经发展为危险性最高的病症。心电、血压、血氧等生理参数是预防和分析心血管疾病的重要生理参数。 常用的心电测量仪器有:多导ECG心电图仪、PPG心率检测器、心率带等; 常...

wxq_1993的博客 2万+

基于RTL8367S与XS2184的工业级POE交换机硬件设计全解析

本文深入解析基于RTL8367S与XS2184芯片的工业级POE交换机硬件设计,涵盖核心价值、芯片选型、原理图设计、PCB布局及可靠性验证等关键环节。重点探讨双芯片协同工作优势,如RTL8367S的自适应调度算法和XS2184的智能功率分配功能,为工业环境下的稳定供电与数据传输提供专业解决方案。

weixin_42614269的博客 458

Android Glide源码解析

功能介绍使用文章介绍以及和Picasso的对比分析请参考Introduction to Glide, Image Loader Library for Android, recommended by Google由于这篇文章使用glide的老版本,因此有些使用方法可能不太一致了。 本文基于github上Glide最新代码4.0.0版本做解析。 最基本的使用方式如下:Glide.with(this

u014592587的专栏 5750

电力系统设计实战:如何用ETAP绘制一次接线图并优化供电方案

本文详细介绍了如何使用ETAP软件进行电力系统一次接线图的绘制与供电方案优化。从基础环境配置到主接线拓扑构建,再到典型接线方式的对比分析与智能优化,为工业与民用电力系统设计提供实用指南。通过ETAP的可靠性分析和动态仿真功能,帮助工程师选择最优供电方案,提升系统可靠性和运维效率。

weixin_29250573的博客 205

glide-源码解析-2

glide-源码解析-2

dbs1215的专栏 757

IEEE Transactions on Industrial Informatics(TII)投稿全流程解析:从准备到成功提交

本文详细解析了向IEEE Transactions on Industrial Informatics(TII)投稿的全流程,涵盖从投稿前的格式准备、内容结构优化到在线提交系统实操等关键环节。重点强调了遵守TII严格的格式规范、双盲评审要求以及如何构建有说服力的学术叙述,旨在帮助研究者提升稿件质量,顺利通过评审。

weixin_28396629的博客 1863

11-4 AEB 算法原理及系统组成

本专题旨在探讨如何利用仿真软件开发自动紧急制动系统AEB,主要通过整理开发流程,实现从需求到验证的全流程掌握,为后续开发提供支持;同时,研究了如何在汽车自动驾驶仿真工具中开发自己的算法,并解决了从ISO、E-CNAP、GB/T以及交通法规等提炼出功能需求的问题,以快速实现原型模型的开发。本专题重点讲解了AEB的工作原理、系统/软件架构和需求、算法模型及实现、CCRs/CCRb/CCRm建模与仿真、VRU-AEB-B/P/M建模与仿真,同时还提供了测试用例,期待将AEB算法成功地产品化。

AIgraphX 8551

Glide 4.11主线源码解析

Glide(4.11)主线源码解析 主线流程三部曲 (with-load-into) Glide.with(context).load(url).into(imageview) with 源码: 方法调用链:with() —> getRetriever(activity) -> Glide.get() -> checkAndInitializeGlide() -> getRetriever(activity).get(activity) —> supportFragme

weixin_44352679的博客 1016

触摸芯片RM1015B基本使用方法

RM1015B触摸芯片使用指南:该芯片采用4通道设计(实际使用3通道),通过I2C接口通信(设备地址0x2B)。硬件设计需注意中断/复位引脚复用,需上拉处理。软件配置包括初始化、中断处理和定时器扫描,通过设置各通道阈值(0-300W)调节灵敏度,值越小越灵敏。调试时可调用rm1015B_debug_func()输出波形数据。典型应用场景包括接近检测和手势识别,中断触发后需在8ms内响应。特别注意不同通道的寄存器映射关系及校准流程,确保检测准确性。

云淡风轻 176

Glide4.0源码全解析(一),GlideAPP和.with()方法背后的故事

前言 在上一篇文章中我们体验了Glide-4.0的强大,以及更加简便的API,很多童鞋已经开始迁移了,那么接下来我们一起探索一下他的神奇之处: 首先我们来看一下4.0的基本用法: GlideApp.with(this) .load(R.raw.large_giphy_logo) .into(giphyLogoView); 就这么简简单单的一句代码,其实G

李彬博客专栏 1万+

拆解全球18大“支付清算”体系:CLS、中国、美国、欧洲、日本、mBridge...

拆解全球18大“支付清算”体系:CLS、中国、美国、欧洲、日本、mBridge...

银行信息系统应用架构详解 883

【100%通过率】2024华为OD机试真题E卷 C++语言 实现【流浪地球】

流浪地球计划在赤道上均匀部署了N个转向发动机,按位置顺序编号为0~N-1。 1) 初始状态下所有的发动机都是未启动状态; 2) 发动机起动的方式分为“手动启动”和“关联启动”两种方式; 3) 如果在时刻1一个发动机被启动,下一个时刻2与之相邻的两个发动机就会被“关联启动”; 4) 如果准备启动某个发动机时,它已经被启动了,则什么都不用做; 5) 发动机0与发动机N-1是相邻; 地球联合政府准备挑选某些发动机在某些时刻进行“手动启动”,当然最终所有的发动机都会被启动。 哪些发动机最晚被启动呢?

MISAYAONE的博客 4210

Glide源码解析(二)从源码的角度理解Glide的执行流程

转载自 guolin,略有删减改动 Glide源码解析(二)从源码的角度理解Glide的执行流程如何阅读源码源码下载开始阅读1. with()2. load()3. into()总结 在本系列的上一篇文章中,我们学习了Glide的基本用法,体验了这个图片加载框架的强大功能,以及它非常简便的API。还没有看过上一篇文章的朋友,建议先去阅读 Android图片加载框架最全解析(一),Glide的基本用法 。 在多数情况下,我们想要在界面上加载并展示一张图片只需要一行代码就能实现,如下所示: Glide.with

aha_jasper的博客 444

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

源码解析,Glide加载GIF图的原理竟然这么简单(1)

其实很简单就下面这张图,含概了Android所有需要学的知识点,一共8大板块:架构师筑基必备技能Android框架体系架构(高级UI+FrameWork源码)360°Androidapp全方位性能调优设计思想解读开源框架NDK模块开发移动架构师专题项目实战环节移动架构师不可不学习微信小程序混合开发的flutterAndroid学习的资料我呢,把上面八大板块的分支都系统的做了一份学习系统的资料和视频,大概就下面这些,我就不全部写出来了,不然太长了影响大家的阅读。

2401_84123557的博客 919

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

上一篇: Java自定义注解
下一篇: Glide缓存机制
coder_yao
博客等级 码龄10年 9粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值