BBWebImage 設計思路

来源:https://www.cnblogs.com/silence-cnblogs/archive/2019/02/27/10442984.html
-Advertisement-
Play Games

BBWebImage 設計思路 BBWebImage 是 Swift 圖片組件,用於圖片下載、緩存、編解碼、編輯與展示。 GitHub 地址: "https://github.com/Silence GitHub/BBWebImage" 效果圖 下載、展示並緩存原圖 下載、漸進式解碼、編輯圖片,緩存 ...


BBWebImage 設計思路

BBWebImage 是 Swift 圖片組件,用於圖片下載、緩存、編解碼、編輯與展示。

GitHub 地址: https://github.com/Silence-GitHub/BBWebImage

效果圖

下載、展示並緩存原圖

下載、漸進式解碼、編輯圖片,緩存編輯後的圖片至記憶體 (Memory)、緩存原圖數據至磁碟 (Disk)

  • 添加濾鏡

  • 繪製圓角、邊框

為什麼寫這個圖片組件

寫 BBWebImage 最開始的目的是要解決現有圖片組件中圖片編輯與動圖的問題。

做過的項目中,圖片組件都主要用 SDWebImage,顯示 WebP、APNG 等格式動圖用 YYWebImage。這些圖片組件都非常優秀,能滿足大多數使用場景的需求。YYWebImage 支持的圖片格式很多,但是功能和可自定義程度不如 SDWebImage (例如自定義圖片解碼器)。當 BBWebImage 第一版 0.1.0 發佈時,SDWebImage 的最新正式版是 4.4.3,還沒有圖片編輯模塊。有些時候需要展示編輯後的圖片,例如添加濾鏡、繪製圓角和邊框 (防止 CALayer 設置圓角造成頓卡) 等,也需要緩存編輯後的圖片。如果用 SDWebImage 下載圖片並編輯,會有以下問題:

  1. 如果只緩存編輯後的圖片,則展示原圖需要再次下載。
  2. 假設原圖數據緩存至磁碟。如果不緩存編輯後的圖片,需要在每次展示前重覆編輯原圖這個步驟。如果把編輯後的圖片緩存至記憶體和磁碟,為了與原圖區分,需要維護 cache key (在緩存中一個 key 對應原圖,另一個 key 對應編輯後的圖片)。如果把編輯後的圖片只緩存至記憶體,則為了區分從緩存中取出的圖片是否經過編輯,需要判斷是從記憶體還是磁碟取到的圖片。
  3. 如果用 Core Graphics 框架編輯圖片,SDWebImage 的圖片解壓縮 (Decompress) 是不必要的。編輯和解壓縮步驟類似:創建 CGContext、繪製圖片、創建新圖片。需要在編輯前禁用圖片解壓縮,完成後再啟用。

另外,SDWebImage 的圖片降採樣 (Downsample) 用了統一處理的方式,圖片解析度大於固定閾值是降採樣的必要條件。問題就在於閾值是固定的,遇到多張大圖的情況,這個閾值還是太大,導致記憶體占用過多而崩潰。如果圖片組件中有圖片編輯模塊,可以把圖片降採樣放入編輯模塊,就可以自定義降採樣參數,從而解決記憶體占用過多問題。

關於動圖,SDWebImage 用 FLAnimatedImage 來展示 GIF,但是有性能問題。原因是 FLAnimatedImage 沒有繼承 UIImage,SDWebImage 的解碼器無法直接返回 FLAnimatedImage,只好在主線程中用圖片數據創建 FLAnimatedImage,這一步阻塞主線程導致頓卡。具體代碼分析和解決方案參見 SDWebImage 載入顯示 GIF 與性能問題。解決方案能用,但是從設計的角度看,SDWebImage 使用 FLAnimatedImage 並不合適。FLAnimatedImage 只適用於 GIF,無法通過自定義解碼器來支持其他格式的動圖。理想的情況是,圖片組件搭建好展示動圖的框架,有常用動圖的解碼,可以自定義解碼器來支持其他格式的動圖。

架構設計

主要結構

BBWebImage 的主要結構可以看下麵這幅圖。BBImageCache 管理圖片緩存,BBImageDownloader 管理圖片下載,BBImageCoderManager 管理圖片編解碼,BBWebImageEditor 提供圖片編輯方法。BBWebImageManager 調用前四者的方法實現相應功能,對外提供一個方法實現圖片載入 (緩存讀取與下載)、解碼、編輯和緩存。UIImageView 的擴展方法調用 BBWebImageManager 的方法獲取圖片用於展示。動圖封裝成 BBAnimatedImage,用 BBAnimatedImageView 展示。

BBImageCache

BBImageCache 是圖片緩存協議,定義向緩存存取圖片的行為。BBLRUImageCache 是預設使用的緩存,遵循 BBImageCache 協議。BBLRUImageCache 裡面有記憶體緩存與磁碟緩存,都採用 LRU 演算法 (Least recently used)。這部分設計基本參照 YYCache。記憶體緩存用字典和雙向鏈表實現 LRU 演算法。磁碟緩存用 SQLite 資料庫存儲數據相關信息 (key、大小、更新時間等),二進位數據本身根據文件大小來決定存儲至 SQLite 資料庫或者直接寫入沙盒目錄。

往記憶體緩存中保存的是 UIImage,取出的也是 UIImage。往磁碟緩存中存儲的是 Data 或者是 UIImage,後者會被編碼成 Data;取出的只是 Data,這裡不會進行解碼 (BBWebImageManager 拿到數據,才會用 BBImageCoderManager 進行解碼)。

如果預設緩存無法滿足需求,可以自定義緩存,遵循 BBImageCache 協議,替換預設緩存。

BBImageDownloader

BBImageDownloader 是圖片下載協議,定義圖片下載行為。BBMergeRequestImageDownloader 是預設使用的下載器,遵循 BBImageDownloader 協議。BBMergeRequestImageDownloader 會合併對同一 URL 的網路請求,防止對同一 URL 發出重覆請求。每一個下載任務封裝成 BBImageDownloadTask (是個協議,預設實現是 BBImageDefaultDownloadTask,可自定義實現) ,包含這次下載任務的完成回調等信息。每一個 URL 網路請求 (以下稱為 "下載操作") 封裝成 BBImageDownloadOperation (也是協議,預設實現是 BBMergeRequestImageDownloadOperation,可自定義實現),包含至少一個下載任務。

下載操作的執行順序是,一般操作 (下載圖片後要立即使用) 優先於預載入操作 (圖片不需要在下載後立即使用,只是下載存入緩存),同時先進先出,也就是老的操作優先執行。雖然 SDWebImage 提供了後進先出和設置優先順序的功能,但在做過的項目中並沒有用到。因此這裡沒有設計這些功能,以後需要的話可以加上。實現方法原來是用自帶的 Operation 和 OperationQueue 實現,但後來想把這一部分也自定義,於是用字典和雙向鏈表實現。一共有兩組字典和雙向鏈表的組合,一組代表一般操作隊列,另一組代表預載入操作隊列。最多同時執行操作數為 6 個。操作數少於 6,有新操作進來就執行;大於等於 6,把新操作插入相應隊列尾部。一個操作結束後,先從一般操作隊列頭部取一般操作來執行,沒有的話再從預載入操作隊列頭部取預載入操作來執行。預載入操作還可以升級為一般操作。如果前面有預載入任務,並且相應的預載入操作進入預載入操作隊列等待,後來有一般下載任務是相同的 URL,則之前的預載入操作會被移出預載入操作隊列,進入一般操作操作隊列而升級為一般操作,把後來的一般下載任務合併進來。

如果需要自定義圖片下載行為,例如 MD5 校驗等,可以考慮自定義下載任務 (遵循 BBImageDownlaodTask 協議) 或下載操作 (遵循 BBImageDownloadOperation 協議),甚至自定義整個下載器 (遵循 BBImageDownloader 協議)。

BBImageCoderManager

BBImageCoder 是圖片編解碼協議,定義圖片編解碼行為。BBImageCoderManager 遵循 BBImageCoder 協議,包含至少一個編解碼器 (也遵循 BBImageCoder 協議)。用 BBImageCoderManager 來編解碼時,BBImageCoderManager 會遍歷其中的編解碼器,嘗試找到一個能完成操作的編解碼器。這個圖片組件中的所有圖片編解碼操作 (包括靜圖、漸進式解碼、動圖) 都由遵循 BBImageCoder 協議的編解碼器完成,可以通過自定義編解碼器來自定義編解碼行為,支持不同格式的圖片。

BBWebImageEditor

BBWebImageEditor 是個結構體,包含一個字元串 key 和一個閉包 edit。閉包 edit 輸入一個 UIImage, 輸出一個 UIImage,用於編輯圖片。字元串 key 作為圖片編輯方法的唯一標識符,將與 edit 輸出的 UIImage 動態關聯 (通過擴展屬性 bb_imageEditKey 來訪問,以下稱為 “edit key”)。例如,定義一個添加濾鏡的圖片編輯器,edit 是添加濾鏡閉包,key 是 "filter",編輯後的圖片的 edit key 是 "filter";定義一個繪製圓角的圖片編輯器,edit 是繪製圓角閉包,key 是 "roundedCorner",編輯後的圖片的 edit key 是 "roundedCorner"。原圖的 edit key 為 nil。通過圖片的 edit key 就可以知道圖片是原圖還是某個編輯器編輯後的圖片。

BBWebImageManager

BBWebImageManager 對外提供載入圖片的方法 loadImage(with:)。與 SDWebImage 類似,先在記憶體緩存中找圖片,沒有的話找磁碟緩存,如果沒有就下載並緩存圖片。不同的是,圖片解壓縮在這一層才執行 (SDWebImage 在 cache 和 download operation 中都有執行),而且這裡還有圖片編輯步驟。loadImage(with:) 方法的 editor 參數是 BBwebImageEditor? 類型,傳 nil 表示需要原圖,傳某個編輯器表示需要用原圖進行編輯。如果從記憶體緩存中取到圖片,需要通過 edit key 判斷圖片的編輯狀態 (原圖、或者被編輯),決定後續步驟 (直接使用圖片,直接編輯圖片,需要從磁碟緩存或網路獲取圖片)。如果傳入了編輯器作為方法參數,則不進行圖片解壓縮,解壓縮由編輯器負責。原圖數據保存至磁碟緩存,原圖或編輯後的圖片保存至記憶體緩存,通過圖片的 edit key 來區分編輯狀態。

這個圖片組件內置的圖片編輯器中有一個比較常用,通過 bb_imageEditorCommon(with:) 方法創建,傳入的參數有 imageView 的大小和 contentMode、期望最大解析度、圓角位置和圓角半徑、邊框寬度和顏色、背景色。編輯器裁剪圖片,只保留 imageView 顯示的部分;根據 imageView 的大小與期望最大解析度計算降採樣解析度閾值,如果原圖解析度大於閾值就會進行降採樣;繪製圓角、邊框、背景色。可以用這個圖片編輯器自定義降採樣解析度閾值,防止記憶體占用過多;繪製好圓角和邊框,防止 CALayer 設置圓角和邊框造成頓卡。

UIImageView 擴展

BBWebCache 是圖片載入協議,定義圖片載入行為。預設實現了圖片載入方法 bb_setImage(with:) (以下稱為 "協議載入方法"),用 BBWebImageManager 的單例載入圖片,動態關聯 BBWebCacheOperation 對象用於訪問圖片載入任務 (方便以後取消任務)。UIImageView 遵循 BBWebCache 協議,載入 image 和 highlightedImage 的擴展方法,都直接調用協議載入方法,只是傳入的參數有所不同。與此類似,UIButton、CALayer、MKAnnotationView 都有相應的擴展方法用於載入圖片,也是直接調用協議載入方法。如果有自定義的 view 甚至 object 需要載入圖片,也可以遵循 BBWebCache 協議,調用協議載入方法來實現載入圖片的擴展方法。

動圖

動圖封裝成 BBAnimatedImage,繼承 UIImage。初始化方法除了圖片數據還有動圖解碼器,如果沒有指定解碼器,則從 BBWebImageManager 單例的 BBImageCoderManager 的解碼器中尋找,有合適的解碼器才能初始化動圖。動圖向解碼器獲取每一幀圖片以及動畫時間等信息,並管理圖片幀的緩存。根據總記憶體容量、可用記憶體容量來動態計算最大緩存容量,以此來清除暫時不用的圖片幀同時保存將要展示的圖片幀。也支持自定義最大緩存容量。動圖有 bb_editor 屬性,是 BBWebImageEditor? 類型。用某個圖片編輯器給這個屬性賦值,則會對圖片幀進行編輯。這個屬性預設為空,表示使用原始圖片幀。

BBAnimatedImageView 繼承 UIImageView,用來展示動圖。用 CADisplayLink 播放動畫。屏幕刷新時,向動圖獲取當前要展示的圖片幀。這裡只從緩存的圖片幀中獲取,避免解碼阻塞主線程。同時,告訴動圖下一幀要展示的圖片是第幾幀,由動圖進行後臺解碼。動圖會在 App 進入後臺時、從 imageView 上移除時、以及收到記憶體警告時,清除緩存的圖片幀。

BBAnimatedImageView 除了展示動圖,也可以展示靜圖。它本身就繼承 UIImageView,可以當作普通的 UIImageView 來用。BBAnimatedImage 本身繼承 UIImage,與編解碼協議 BBImageCoder 相符,可以在解碼器中解碼出來,這一點與普通的靜圖相同,不像 SDWebImage + FLAnimatedImage 那樣靜圖與 GIF 不相符 (導致要對 GIF 特殊處理)。在這個框架基礎上,通過自定義圖片編解碼器就可以支持其他格式的動圖 (當然也可以支持其他格式的靜圖,只是這部分在講動圖)。

總結

BBWebImage 的圖片緩存、下載、編解碼、編輯功能都可以自定義。把動圖封裝成繼承 UIImage 的類,用繼承 UIImageView 的類進行展示,支持編輯動圖的圖片幀。可以自定義編解碼器支持其他格式的圖片。現在 BBWebImage 搭建了框架,之後會逐步完善細節。如果有編輯靜圖或動圖的需求,或者其他相關需求,可以嘗試 BBWebImage。源碼及使用方法見 GitHub: https://github.com/Silence-GitHub/BBWebImage

轉載請註明出處:https://www.cnblogs.com/silence-cnblogs/p/10442984.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要: 下文將分享三種不同的數據去重方法數據去重:需根據某一欄位來界定,當此欄位出現大於一行記錄時,我們就界定為此行數據存在重覆。 數據去重方法1: 當表中最在最大流水號時候,我們可以通過關聯的方式為每條重覆的記錄獲取唯一值數據去重方法2:為表中記錄,按照指定欄位進行群組,並獲取最大流水號,然後再進 ...
  • MySql中處理字元串時間,會預設把第一個數字當成年份處理。 在C#伺服器中,使用Date.Now.ToString()生成的字元串時間,如果不指定字元串格式,C#會按照系統語言輸出不同的字元串格式,如: a. 美國: 06/01/2019 01:59:00 PM b.中國: 2019/06/01 ...
  • 大家都知道,評論和評分是決定app在appstore中排名的重要因素,但是大部分用戶下載安裝APP後卻不會去點評,所以添加提示用戶去點評的功能是很必要的。 目前,AppStore點贊評分有兩種方法,一種是跳出應用,跳轉到AppStore;進行評分.另一種是在應用內,內置AppStore進行評分. 序 ...
  • 【目錄】 (一)上傳圖片到伺服器一 Android代碼 (二)上傳圖片到伺服器二 Android 系統7.0以上調用相機相容問題 (三)上傳圖片到伺服器三 後臺伺服器代碼 一、相關知識 ①Android許可權申請 ②網路訪問框架OKHttp ③記憶體溢出問題:圖片壓縮 ④Android 系統7.0以上調 ...
  • 1). 在block內部使用外部指針且會造成迴圈引用情況下,需要用__week修飾外部指針: __weak typeof(self) weakSelf = self; 2). 在block內部如果調用了延時函數還使用弱指針會取不到該指針,因為已經被銷毀了,需要在block內部再將弱指針重新強引用一下 ...
  • 最近由於項目需要, 一直在研究藍牙4.0,在這兒分享給大家, 望共同進步. 一、關於藍牙開發的一些重要的理論概念: 1.當前ios中開發藍牙所運用的系統庫是<CoreBluetooth/CoreBluetooth.h>。 2.藍牙外設必須為4.0及以上(2.0需要MFI認證),否則無法開發,藍牙4. ...
  • 一、前言 LLDB是個開源的內置於XCode的具有REPL(read-eval-print-loop)特征的Debugger,其可以安裝C++或者Python插件。在日常的開發和調試過程中給開發人員帶來了非常多的幫助。瞭解並熟練掌握LLDB的使用是非常有必要的。這篇文章將會帶著大家一起瞭解在iOS開 ...
  • Unity 4.7 導出工程在XCode9/10上報錯 validateRenderPassDescriptor:644: failed assertion `Texture at colorAttachment[0] has usage (0x01) which doesn't specify M... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...