當前位置:首頁 » 操作系統 » 手撕源碼

手撕源碼

發布時間: 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核心技術進階手冊、實戰筆記、面試題綱資料

熱點內容
java返回this 發布:2025-10-20 08:28:16 瀏覽:585
製作腳本網站 發布:2025-10-20 08:17:34 瀏覽:881
python中的init方法 發布:2025-10-20 08:17:33 瀏覽:574
圖案密碼什麼意思 發布:2025-10-20 08:16:56 瀏覽:761
怎麼清理微信視頻緩存 發布:2025-10-20 08:12:37 瀏覽:677
c語言編譯器怎麼看執行過程 發布:2025-10-20 08:00:32 瀏覽:1005
郵箱如何填寫發信伺服器 發布:2025-10-20 07:45:27 瀏覽:250
shell腳本入門案例 發布:2025-10-20 07:44:45 瀏覽:108
怎麼上傳照片瀏覽上傳 發布:2025-10-20 07:44:03 瀏覽:799
python股票數據獲取 發布:2025-10-20 07:39:44 瀏覽:705