如果您在處理圖片時不夠謹慎,可能很快就會發生效能問題。即使是 JPG 或 PNG 等壓縮格式的小型圖片,在解碼顯示時也可能變成大型點陣圖。如果圖形使用效率不佳,可能會導致記憶體問題,進而影響應用程式和裝置上其他應用程式的效能。請按照下列最佳做法,確保應用程式能發揮最佳效能。
使用圖片載入程式庫
使用 Coil (適用於以 Kotlin 為主的專案) 或 Glide (適用於 Java 專案) 等圖片載入程式庫,可提升應用程式效率。這些程式庫會執行快取圖片、視需要減少圖形取樣,以及回收圖形物件等作業,藉此減少應用程式的記憶體用量。
降低圖片取樣率
請務必使用符合需求的圖片大小。請避免將大型高解析度圖片載入小型容器 (例如縮圖)。請改用下採樣,在將圖片解碼至記憶體前縮放圖片。
用戶端降採樣
Coil 和 Glide 等圖片載入程式庫會自動處理縮減取樣。您可以使用 ImageLoader (適用於 Coil) 或 DownsampleStrategy (適用於 Glide) 設定縮減取樣策略。如果手動管理點陣圖,可以使用 inSampleSize 解碼較小的版本。如要安全地執行這項操作,請先將 inJustDecodeBounds 設為 true,讀取圖片尺寸而不分配記憶體,計算樣本大小,將 inSampleSize 設為該值,將 inJustDecodeBounds 設為 false,然後解碼圖片。
建議使用伺服器端調整大小功能
請盡可能直接從後端伺服器要求所需圖片的確切尺寸。這樣做可減少網路用量和磁碟快取空間,同時避免在裝置上調整圖片大小時產生記憶體負擔,進而降低記憶體用量。
您可以設定程式庫,將目標檢視區塊大小動態附加至圖片網址。舉例來說,Coil 允許使用自訂攔截器執行這項操作,而 Glide 則支援使用自訂模型載入器 (例如 BaseGlideUrlLoader) 執行這項操作。
避免使用不受限制的版面配置大小
圖片載入器必須先知道目標大小,才能有效縮減圖片大小 (用戶端或伺服器端),然後執行要求。
請避免在載入遠端圖片的可組合項上使用 wrapContentSize,或讓維度不受限制。如果這些程式庫無法推斷目標界線,就會改為載入原始全尺寸圖片。這可能會導致載入的圖片遠大於必要大小,進而增加記憶體用量和延遲時間。
請改為在圖片可組合函式中設定明確的尺寸 (例如使用 Modifier.size),或定義顯示比例。這樣一來,版面配置引擎就能預先計算確切的像素目標,圖片載入器隨後就能使用這項資訊,要求並解碼大小正確的資產。
為不同螢幕大小提供額外資源
如果應用程式提供內建圖片,建議您為不同裝置解析度提供不同尺寸的素材資源。這有助於縮減應用程式在裝置上的下載大小以及提升效能,因為系統會在較低解析度的裝置上載入解析度較低的圖片。如要進一步瞭解如何為不同裝置大小提供替代點陣圖,請參閱替代點陣圖說明文件。
請勿直接套用邊框間距
有時您可能需要在圖片中加入邊框間距。舉例來說,您可能希望圖片周圍有透明邊框,以形成上下黑邊。在這種情況下,請「不要」直接在圖片中加入邊框間距,以免變更圖片尺寸。請維持圖片尺寸,並使用 InsetDrawable 調整圖片在畫面上的位置。或者,您也可以在保存圖片的 Composable 或 View 中加入邊框間距。
選擇正確的像素格式
選擇合適的像素格式,在記憶體用量和品質之間取得平衡。如果不需要透明度,請使用 RGB_565,這種格式使用的記憶體是預設 ARGB_8888 格式的一半。
在 Glide 中,您可以使用 DecodeFormat 設定這項屬性。在 Coil 中,您可以使用 bitmapConfig 屬性。
盡可能使用向量
如果是幾何圖形組成的圖片,向量圖形比點陣圖小得多,而且可順暢縮放,適用於任何螢幕密度。在適當情況下,使用 ShapeDrawable 等元素來表示圖像。
盡可能釋放及重複使用點陣圖
大型圖像檔案會佔用大量記憶體,為減少影響,請盡可能釋放或重複使用圖像物件。
如果您使用圖片載入程式庫,請務必在不再需要點陣圖時,將點陣圖釋出至程式庫的管理集區。程式庫可在需要時重複使用物件,並保留記憶體緩衝區,以備不時之需。
如果您是手動管理圖像,請在完成後呼叫 Bitmap.recycle 並立即捨棄 Bitmap 參照,藉此釋放點陣圖,而不是依賴垃圾回收。
其他提示與訣竅
本節列出幾種其他方法,可協助您在處理圖像時提升應用程式效能。
不要使用 AAB/APK 檔案封裝大型圖片
造成應用程式下載大小偏大的主要原因之一,就是封裝在 AAB 或 APK 檔案中的圖像。請使用 APK 分析工具,確保封裝的圖片檔案並未超過所需尺寸。縮減圖片大小,或考慮將圖片放在伺服器上,並且僅在必要時下載圖片。
找出多餘的點陣圖
如果有多個相同圖片的副本,就會浪費記憶體。您可以使用 Android Studio 分析器找出多餘的圖像。使用記憶體快照資料分析器擷取記憶體快照資料,然後選擇「重複點陣圖」設定來篩選結果。
使用 ImageBitmap 時,請先呼叫 prepareToDraw,然後再繪圖
使用 ImageBitmap 時,如要啟動將紋理上傳至 GPU 的程序,請先呼叫 ImageBitmap#prepareToDraw(),然後再實際進行繪圖。這有助於 GPU 準備紋理,並提高在螢幕上顯示影像內容的效能。多數圖片載入程式庫都已執行這項最佳化作業,但如果您是自行使用 ImageBitmap 類別,就需要留意這一點。
最好將 Int DrawableRes 或網址做為參數傳遞至可組合項,而非 Painter
由於處理圖片的程序非常複雜 (例如為 Bitmaps 編寫 equals 函式會耗用昂貴的計算資源),Painter API 並未明確標示為穩定版,且附有 @Stable 註解。不穩定的類別可能會產生不必要的重組作業,原因在於編譯器無法輕易推斷資料是否已變更。
因此,建議您將網址或可繪製資源 ID 做為參數傳遞至可組合項,而非做為參數傳遞至 Painter。
// Prefer this:
@Composable
fun MyImage(url: String) {
}
// Over this:
@Composable
fun MyImage(painter: Painter) {
}
為您推薦
- 注意:系統會在 JavaScript 關閉時顯示連結文字
- ImageBitmap 與 ImageVector {:#bitmap-vs-vector}
- 在 Compose 中儲存 UI 狀態
- Jetpack Compose 階段