当前位置:首页 » 操作系统 » 手撕源码

手撕源码

发布时间: 2025-08-08 09:50:42

① Android 【手撕Glide】--Glide缓存机制(面试)

本文源码解析基于Glide 4.6.1

系列文章
Android 【手撕Glide】--Glide缓存机制
Android 【手撕Glide】--Glide缓存机制(面试)
Android 【手撕Glide】--Glide是如何关联生命周期的?

Glide缓存分为内存缓存和磁盘缓存,其中内存缓存是由弱引用+LruCache组成。

取的顺序是:弱引用、LruCache、磁盘
存的顺序是:磁盘、弱引用、LruCache

这张亲手制作的图片,方便大家更直观的理解缓存机制的整体流程,结合文末总结效果更佳。喜欢的记得点赞!

概述

1、弱引用是由这样一个HashMap维护,key是缓存的key,这个key由图片url、width、height等10来个参数组成;value是图片资源对象的弱引用形式。

2、LruCache是由一个LinkedHashMap维护,根据Lru算法来管理图片。大致的原理是利用linkHashMap链表的特性,把最近使用过的文件插入到列表头部,没使用的图片放在尾部;然后当图片大小到达预先设置的一个阀值的时候 ,按算法删除列表尾部的部分数据。由于篇幅有限,这里不讲解LruCache和DiskLruCache的底层原理,这里推荐一篇 图解LinkedHashMap原理

这是Glide自定义的LruCache

存取原理
取数据
在内存缓存中有一个概念叫图片引用计数器 ,具体来说是在 EngineResource 中定义一个 acquired 变量用来记录图袜指正片被引用的次数,调用 acquire() 方法会让变量加1,调用 release() 方法会让变量减1。

获取图片资源是先从弱引用取缓存,拿到的话,引用计数+1;没有的话从LruCache中拿缓存,拿到的话,引用计数逗枝也是+1,同时把图片从LruCache缓存转移到弱应用缓存池中;再没有的话就通过 EngineJob 开启线程池去加载图片,拿到的话,引用计数也是+1,会把图片放到弱引用。

存数据
很明显,这是加载图片之后的事情。通过 EngineJob 开启线程池去加载图片,取到数据之后,会回调到主线程,把图片存到弱引用。当图片不再使用的时候,比如说暂停请求或者加载完毕或者清除资源时,就会将其从弱引用中转移到告悔 LruCache 缓存池中。 总结一下,就是正在使用中的图片使用 弱引用 来进行缓存,暂时不用的图片使用 LruCache 来进行缓存的功能;同一张图片只会出现在 弱引用 和 LruCache 中的一个。

为什么要引入软引用?
1、分压策略,减少Lrucache 中 trimToSize 的概率。如果正在remove的是张大图,lrucache正好处在临界点,此时remove操作,将延缓Lrucache的 trimToSize 操作;
2 提高效率:弱引用用的是 HashMap ,Lrucache用的是 LinkedHashMap ,从访问效率而言,肯定是 HashMap 更高。

Glide磁盘缓存策略(4.x)

如果在内存缓存中没获取到数据会通过 EngineJob 开启线程池去加载图片,这里有2个关键类: DecodeJob 和 EngineJob 。 EngineJob 内部维护了线程池,用来管理资源加载,当资源加载完毕的时候通知回调; DecodeJob 是线程池中的一个任务。

磁盘缓存是通过 DiskLruCache 来管理的,根据缓存策略,会有2种类型的图片, DATA (原始图片)和 RESOURCE (转换后的图片)。磁盘缓存依次通过 ResourcesCacheGenerator 、 SourceGenerator 、 DataCacheGenerator 来获取缓存数据。 ResourcesCacheGenerator 获取的是转换过的缓存数据; SourceGenerator 获取的是未经转换的原始的缓存数据; DataCacheGenerator 是通过网络获取图片数据再按照按照缓存策略的不同去缓存不同的图片到磁盘上。

Glide缓存分为 弱引用+ LruCache+ DiskLruCache ,其中读取数据的顺序是:弱引用 > LruCache > DiskLruCache>网络;写入缓存的顺序是:网络 --> DiskLruCache--> LruCache-->弱引用

内存缓存分为弱引用的和 LruCache ,其中正在使用的图片使用弱引用缓存,暂时不使用的图片用 LruCache缓存,这一点是通过 图片引用计数器(acquired变量)来实现的,详情可以看内存缓存的小结。

磁盘缓存就是通过DiskLruCache实现的,根据缓存策略的不同会获取到不同类型的缓存图片。它的逻辑是:先从转换后的缓存中取;没有的话再从原始的(没有转换过的)缓存中拿数据;再没有的话就从网络加载图片数据,获取到数据之后,再依次缓存到磁盘和弱引用。

参考:
面试官:简历上最好不要写Glide,不是问源码那么简单
原来面试的时候写精通Glide,这样问我这样答

② Framework事件机制——手撕Android事件处理的三种方法

Android的事件处理的三种方法:

setOnClickListener,setOnLongClickListener、setOnTouchListener

注意:如果onTouchEvent方法return true,则单击事件和长摁事件不再执行;若onLongClick方法返回true,则单击事件不再处理。

需要定义继承组件的类,重写回调方法Touch方法执行时,先被Activity捕获,DispatchTouchEvent方法处理。return false,交给上层的onTouchEvent方法处理;return super.dispatchTouchEvent(ev),则传递给最外层的View。

View用Dispatch方法处理,return false,由上层的onTouchEvent方法处理。如果返回super.dispatchTouchEvent(ev),则本层的onInterceptTouchEvent拦截,如果拦截true,则拦截,false不拦截,传递给子View的DispatchTouchEvent处理。

常用的回调方法:onKeyDown,onKeyLongPress,onKeyUp,onTouchEvent,onTrackballEvent(轨迹球事件)监听和回调同时存在时,先调用监听。

流程模型图:

Event source 事件源
Event 事件
Event Listener 事件监听器
下面我们来看一下点击事件和触摸事件的监听三要素具体是那部分:

由于点击事件比较简单,系统已经帮我们处理了,并没有找到具体事件是哪个。

View.OnClickListener 单击事件监听器必须实现的接⼝
View.OnCreateContextMenuListener 创建上下⽂菜单事件
View.OnFocusChangeListener 焦点改变事件
View.OnKeyListener 按键事件监听器
View.OnLongClickListener 长按事件监听器
View.OnTouchListener 触摸屏事件监听器

⾸先,事件监听机制中由事件源,事件,事件监听器三类对象组成。
事件监听器处理流程:

在此以OnClickListener单击事件为例使用intent来实现页面的跳转

监听事件处理是事件源与事件监听器分开的而基于回调的事件处理UI组件不但是事件源,而且还是事件监听器,通过组件的相关回调方法处理对应的事件。

Ⅰ. 自定义View类,继承自需要的View UI类。ex :自定义 MyButton按钮类 extends 基础Button类

Ⅱ. 复写回调函数。ex:public boolean onTouchEvent(MotionEvent event)

每一个事件回调方法都会返回一个boolean值,①.如果返回true:表示该事件已被处理,不再继续向外扩散,②.如果返回false:表示事件继续向外扩散

而说到基于回调就离不开监听机制

几乎所有基于回调的事件处理方法都有一个boolean类型的返回值,该返回值用于表示该处理方法是否能完全处理该事件。
如果处理事件的回调方法返回true,表明该处理方法已经完全处理改事件,该事件不会传播出去。
如果处理事件的回调方法返回false,表明该处理方法并未完全处理该事件,该事件会传播出去。
对于基于回调的时间传播而言,某组件上所发生的事件不仅会激发该组件上的回调方法,也会触发该组件所在Activity的回调方法——只要事件能传播到该Activity。

这里是在模拟器里进行的测试,这里按下键盘(而不是点击),会看到 logcat 中的输出,如下:

View类实现了KeyEvent.Callback接口中的一系列回调函数,因此,基于回调的事件处理机制通过自定义View来实现,自定义View时重写这些事件处理方法即可。

Handler是一个消息分发对象。

Handler是Android系统提供的一套用来更新UI的机制,也是一套消息处理机制,可以通过Handler发消息,也可以通过Handler处理消息。

在下面介绍Handler机制前,首先得了解以下几个概念:

在子线程执行完耗时操作,当Handler发送消息时,将会调用 MessageQueue.enqueueMessage ,向消息队列中添加消息。 当通过 Looper.loop 开启循环后,会不断地从消息池中读取消息,即调用 MessageQueue.next , 然后调用目标Handler(即发送该消息的Handler)的 dispatchMessage 方法传递消息, 然后返回到Handler所在线程,目标Handler收到消息,调用 handleMessage 方法,接收消息,处理消息。

从上面可以看出,在子线程中创建Handler之前,要调用 Looper.prepare() 方法,Handler创建后,还要调用 Looper.loop() 方法。而前面我们在主线程创建Handler却不要这两个步骤,因为系统帮我们做了。

初始化Looper

从上可以看出,不能重复创建Looper,每个线程只能创建一个。创建Looper,并保存在 ThreadLocal 。其中ThreadLocal是线程本地存储区(Thread Local Storage,简称TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

开启Looper

发送消息

post方法:

send方法:

在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。

本文讲解了三个方面;Android事件机制;基于监听、基于回调以及Handler消息处理。还有许多没有讲解到的知识点,我总结在了整理的一套Android进阶笔记里面;需要学习进阶的同学可以前往获取: Frame Work源码解析手册 、 Android核心技术进阶手册、实战笔记、面试题纲资料

热点内容
sql表的复制 发布:2025-09-18 11:02:25 浏览:67
三位密码锁忘记密码如何重置 发布:2025-09-18 10:21:52 浏览:184
linux怎么编译c文件 发布:2025-09-18 09:55:16 浏览:305
python安装后无法运行 发布:2025-09-18 09:45:57 浏览:239
安卓手机怎么剪辑音乐 发布:2025-09-18 09:44:16 浏览:784
服务器地址修改在哪找 发布:2025-09-18 09:42:41 浏览:444
sntp服务器地址 发布:2025-09-18 09:28:36 浏览:555
phpunit 发布:2025-09-18 09:25:19 浏览:573
怎么改服务器的ip地址吗 发布:2025-09-18 09:24:33 浏览:14
编译703n固件 发布:2025-09-18 08:50:59 浏览:541