原來面試的時候寫精通Glide,這樣問我這樣答

前言

這幾天在很多地方看到有聊Glide的,想起前年還是去年的時候面試的時候也有問到Glide相關的,正好今天趁着週五來聊一聊面試中Glide的那些東西

  • 1 、圖片庫對比
  • 2 、LRUCache 原理
  • 3丶LruCache 底層實現原理
  • 4 、圖片加載原理
  • 5 、自己去實現圖片庫,怎麼做?
  • 6 、Glide 源碼解析
  • 7 、Glide 使用什麼緩存
  • 8 、Glide 內存緩存如何控制大小?
  • 9丶三級緩存原理?
  • 10丶如何設計一個大圖加載框架?

    面試整理學習路線(禁止偷偷拿走O(∩_∩)O)

    Android核心技術面試必備.png
    請查看完整的PDF版
    (更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。)
    可以點擊關於我聯繫我獲取完整PDF

一丶LRUCache 原理

LruCache 是個泛型類,主要原理是:把最近使用的對象用強引用存儲在 LinkedHashMap 中,當緩存滿時,把最近最少使用的對象從內存中移除,並提供 get/put 方法完成緩存的獲取和添加LruCache 是線程安全的,因爲使用了 synchronized 關鍵字。

當調用 put()方法,將元素加到鏈表頭,如果鏈表中沒有該元素,大小不變,如果沒有,需調用 trimToSize 方法判斷是否超過最大緩存量,trimToSize()方法中有一個 while(true)死循環,如果緩存大小大於最大的緩存值,會不斷刪除 LinkedHashMap 中隊尾的元素,即最少訪問的,直到緩存大小小於最大緩存值。當調用 LruCache 的 get 方法時,LinkedHashMap 會調用recordAccess 方法將此元素加到鏈表頭部

二、Glide 源碼解析

1)Glide.with(context)創建了一個 RequestManager,同時實現加載圖片與組件生命週期綁定:
在 Activity 上創建一個透明的 ReuqestManagerFragment 加入到 FragmentManager 中,通過添加的 Fragment 感知Activty\Fragment 的生命週期。因爲添加到 Activity 中的 Fragment 會跟隨Activity 的生命週期。在 RequestManagerFragment 中的相應生命週期方法中通過 liftcycle 傳遞給在 lifecycle 中註冊的 LifecycleListener

2)RequestManager.load(url) 創建了一個 RequestBuilder<T>對象 T 可以是 Drawable 對象或是 ResourceType

3 ) RequestBuilder.into(view)-->into(glideContext.buildImageViewTarget(view, transcodeClass))返 回 的 是 一 個DrawableImageViewTarget, Target 用 來 最 終 展 示 圖 片 的 ,buildImageViewTarget-->ImageViewTargetFactory.buildTarget()根據傳入 class 參數不同構建不同的 Target 對象,這個 Class 是根據構建 Glide 時是否調用了 asBitmap()方法,如果調用了會構建出BitmapImageViewTarget,否則構建的是GlideDrawableImageViewTarget 對象。
-->GenericRequestBuilder.into(Target), 該 方 法 進 行 了 構 建 Request , 並 用RequestTracker.runRequest()-->GenericRequest.begin()

 Request request = buildRequest(target);
         // 構建 Request 對象, 
        target.setRequest(request);
        lifecycle.addListener(target);
 requestTracker.runRequest(request);
       // 判斷 Glide 當前是不是處於暫停狀態

onSizeReady()--> `Engine.load(signature, width, height, dataFetcher, loadProvider,transformation, transcoder,priority, isMemoryCacheable, diskCacheStrategy, this)

a)先構建EngineKey;

b)`loadFromCache 從 緩 存 中 獲 取 EngineResource , 如 果 緩 存 中 獲 取 到 cache 就 調 用`cb.onResourceReady(cached);

c)如果緩存中不存在調用loadFromActiveResources從 active中獲取,如果獲取到就調用cb.onResourceReady(cached);

d)如果 active 中也不存在,調用EngineJob.start(EngineRunnable), 從而調用decodeFromSource()/decodeFromCache()-->如果是調 用decodeFromSource()-->ImageVideoFetcher.loadData()-->HttpUrlFetcher()調 用HttpUrlConnection進 行 網 絡 請 求 資 源 --> 得 於InputStream()後 , 調 用decodeFromSourceData()-->loadProvider.getSourceDecoder().decode() 方 法 解 碼-->GifBitmapWrapperResourceDecoder.decode()-->decodeStream()先從流中讀取 2 個字節判斷是 GIF 還是普通圖,若是 GIF 調用decodeGifWrapper()來解碼,若是普通靜圖則調用decodeBitmapWrapper()來解碼-->bitmapDecoder.decode()`

三丶Glide 使用什麼緩存?

1) 內存緩存: LruResourceCache(memory)+弱引用 activeResources
Map<Key, WeakReference<EngineResource<?>>> activeResources正在使用的資源,當 acquired變量大於 0,說明圖片正在使用,放到 activeResources 弱引用緩存中,經過 release()後,acquired=0,說明圖片不再使用,會把它放進 LruResourceCache

2)磁盤緩存: DiskLruCache,這裏分爲 Source(原始圖片)和 Result(轉換後的圖片)

第一次獲取圖片,肯定網絡取,然後存 active\disk 中,再把圖片顯示出來,第二次讀取相同的圖片,並加載到相同大小的 imageview 中,會先從 memory 中取,沒有再去 active 中獲取。如果 activity 執行到 onStop 時,圖片被回收,active 中的資源會被保存到memory 中,active中的資源被回收。當再次加載圖片時,會從 memory 中取,再放入 active 中,並將 memory中對應的資源回收。

之所以需要 activeResources,它是一個隨時可能被回收的資源,memory 的強引用頻繁讀寫可能造成內存激增頻繁 GC,而造成內存抖動。資源在使用過程中保存在 activeResources 中,而 activeResources 是弱引用,隨時被系統回收,不會造成內存過多使用和泄漏。

四丶Glide 內存緩存如何控制大小?

Glide 內存緩存最大空間(maxSize)=每個進程可用最大內存0.4(低配手機是 每個進程可用最大內存0.33)

磁盤緩存大小是 250MB int DEFAULT_DISK_CACHE_SIZE = 250 1024 1024;

五丶LruCache 底層實現原理:

LruCacheLru 算法的實現就是通過 LinkedHashMap 來實現的。LinkedHashMap繼承於HashMap,它使用了一個雙向鏈表來存儲 Map 中的 Entry 順序關係,對於 get、put、remove 等操作,LinkedHashMap 除了要做 HashMap 做的事情,還做些調整 Entry 順序鏈表的工作。

LruCache 中將 LinkedHashMap 的順序設置爲 LRU 順序來實現 LRU 緩存,每次調用 get(也就是從內存緩存中取圖片),則將該對象移到鏈表的尾端。調用 put 插入新的對象也是存儲在鏈表尾端,這樣當內存緩存達到設定的最大值時,將鏈表頭部的對象(近期最少用到的)移除。

六丶三級緩存原理

當 Android 端需要獲得數據時比如獲取網絡中的圖片,首先從內存中查找(按鍵查找),內存中沒有的再從磁盤文件或 sqlite 中去查找,若磁盤中也沒有才通過網絡獲取

七丶如何設計一個大圖加載框架

圖片加載包含封裝,解析,下載,解碼,變換,緩存,顯示等操作

  • 封裝參數: 從指定來源,到輸出結果,中間可能經歷很多流程,所以第一件事就是封裝參數,這些參數會貫穿整個過程;
  • 解析路徑: 圖片的來源有多種,格式也不盡相同,需要規範化;
  • 讀取緩存: 爲了減少計算,通常都會做緩存;同樣的請求,從緩存中取圖片(Bitmap)即可;
  • 查找文件/下載文件: 如果是本地的文件,直接解碼即可;如果是網絡圖片,需要先下載;
  • 解碼: 這一步是整個過程中最複雜的步驟之一,有不少細節,下個博客會說;
  • 變換: 解碼出Bitmap之後,可能還需要做一些變換處理(圓角,濾鏡等);
  • 緩存: 得到最終bitmap之後,可以緩存起來,以便下次請求時直接取結果;
  • 顯示: 顯示結果,可能需要做些動畫(淡入動畫,crossFade等)
    原來面試的時候寫精通Glide,這樣問我這樣答
    請查看完整的PDF版
    (更多完整項目下載。未完待續。源碼。圖文知識後續上傳github。)
    可以點擊關於我聯繫我獲取完整PDF
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章