Android 2D 算繪管道支援硬體加速功能,也就是說,對畫布執行的所有繪圖作業都使用 GPU。由於啟用硬體加速需要較多資源,應用程式會耗用更多 RAM。
硬體加速功能預設為啟用。如果您的應用程式僅使用標準可組合函式,全域開啟此功能應不會造成不良繪圖效果。不過,由於並非所有 2D 繪圖作業都支援硬體加速,開啟這項設定可能會影響部分自訂繪圖呼叫。通常會出現的問題包括隱形元素、例外狀況或錯誤轉譯像素。為解決上述現象,Android 會讓您在多個層級選擇啟用或停用硬體加速。請參閱「控制硬體加速」。
如果您的應用程式會自訂繪圖,請在開啟硬體加速的實際硬體裝置上測試應用程式,以找出硬體問題。「繪圖作業支援」一節說明硬體加速的已知問題及解決方法。
另請參閱「OpenGL with the Framework APIs」。
控制硬體加速
您可以在下列層級中控制硬體加速:
- 應用程式
- 活動
- 視窗
- 可組合項目
應用程式層級
在 Android 資訊清單檔案中,將下列屬性新增至 <application> 標記,以便為整個應用程式啟用硬體加速:
<application android:hardwareAccelerated="true" ...>
活動層級
如果全域開啟硬體加速時,您的應用程式無法正常運作,也可以個別控制各項活動。如要在活動層級啟用或停用硬體加速,您可以針對 <activity> 元素使用 android:hardwareAccelerated 屬性。以下範例為整個應用程式啟用硬體加速,但針對一項活動停用此功能:
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
視窗層級
如果您需要更精細的控制,可以使用下列程式碼啟用特定視窗的硬體加速功能:
window.setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
)
可組合函式層級
在 Compose 中,沒有可停用硬體加速功能的個別可組合函式切換鈕。
如要將可組合函式轉譯至專屬圖層,請使用 Modifier.graphicsLayer。這樣一來,轉換屬性 (例如 alpha、scaleX、scaleY、translationX、translationY、rotationX、rotationY、rotationZ 和 transformOrigin) 就能變更,而不必重新執行可組合項的繪圖程式碼。為獲得最佳效能,請一律使用修飾符的 lambda 形式設定這些屬性。
如要明確強制使用螢幕外緩衝區進行進階繪圖作業 (例如圖層內的自訂混合),請使用 CompositingStrategy.Offscreen。詳情請參閱「圖形修飾符」。
如果您有自訂繪圖作業,且嚴格要求軟體算繪,可以使用 AndroidView 代管舊版 View,並在該檢視區塊上呼叫 setLayerType(View.LAYER_TYPE_SOFTWARE, null)。
支援繪圖作業
硬體加速時,2D 算繪管道可支援最常用的 Canvas 繪圖作業,以及許多不常用的作業。系統支援所有用於算繪 Android 應用程式的繪圖作業、標準可組合項,以及反射和圖塊紋理等常見的進階視覺效果。
下表說明各種 API 級別的各種作業支援等級:
| 第一支援的 API 級別 | ||||
| 畫布 | ||||
| drawBitmapMesh() (色彩陣列) | 18 | |||
| drawPicture() | 23 | |||
| drawPosText() | 16 | |||
| drawTextOnPath() | 16 | |||
| drawVertices() | 29 | |||
| setDrawFilter() | 16 | |||
| clipPath() | 18 | |||
| clipRegion() | 18 | |||
| clipRect(Region.Op.XOR) | 18 | |||
| clipRect(Region.Op.Difference) | 18 | |||
| clipRect(Region.Op.ReverseDifference) | 18 | |||
| clipRect() 旋轉/視角 | 18 | |||
| 繪製 | ||||
| setAntiAlias() (適用於文字) | 18 | |||
| setAntiAlias() (行) | 16 | |||
| setFilterBitmap() | 17 | |||
| setLinearText() | ✗ | |||
| setMaskFilter() | ✗ | |||
| setPathEffect() (行) | 28 | |||
| setShadowLayer() (文字除外) | 28 | |||
| setStrokeCap() (行) | 18 | |||
| setStrokeCap() (點) | 19 | |||
| setSubpixelText() | 28 | |||
| Xfermode | ||||
| PorterDuff.Mode.DARKEN (framebuffer) | 28 | |||
| PorterDuff.Mode.LIGHTEN (framebuffer) | 28 | |||
| PorterDuff.Mode.OVERLAY (framebuffer) | 28 | |||
| 著色器 | ||||
| ComposeShader 中的 ComposeShader | 28 | |||
| ComposeShader 中的相同類型著色器 | 28 | |||
| ComposeShader 上的本機矩陣 | 18 | |||
畫布縮放
最初的硬體加速 2D 算繪管道最初是支援未縮放的繪圖,有些繪圖作業在採用較大的縮放值時會大幅降低品質。這些作業會實作為縮放比例為 1.0 的紋理,並由 GPU 轉換。從 API 級別 28 開始,所有繪圖作業都可以順利縮放。
下表顯示了變更實作以正確處理大比值縮放:
| 要縮放的繪圖作業 | 第一支援的 API 級別 |
| drawText() | 18 |
| drawPosText() | 28 |
| drawTextOnPath() | 28 |
| 簡單形狀 | 17 |
| 複雜形狀 | 28 |
| drawPath() | 28 |
| 陰影圖層 | 28 |
Paint如果依賴的繪圖作業未經硬體加速,請將受影響的繪圖作業算繪到螢幕外軟體 Bitmap (或 ImageBitmap) 中,然後繪製結果。其餘 UI 則會保留硬體加速路徑。
提示與祕訣
切換為硬體加速 2D 圖像可以立即提升效能,但如果要在設計應用程式時有效使用 GPU,您仍應採用下列建議:
- 盡量減少版面配置複雜度和重新組合
- 保持版面配置樹狀結構淺層,並限制重組次數。將狀態讀取作業延後至最窄的範圍,這樣變更時,系統會重新繪製最小的區域。舉例來說,請在
Modifier.graphicsLayer { }內讀取動畫狀態,而不是在可組合函式的主體中。詳情請參閱「Jetpack Compose 效能」。 - 避免過度繪製
- 請勿互相繪製過多圖層。移除檢視畫面上被其他不透明元素完全遮擋的 UI 元素。如果您需要繪製多個彼此重疊的圖層,請考慮將這些圖層合併為單一圖層。使用目前的硬體繪製時,原則上每個畫面每個像素的像素數都不會超過 2.5 倍 (點陣圖數量中的透明像素!)
- 不要在繪圖方法中建立算繪物件
- 常見的錯誤是每次叫用算繪方法時,建立新的
Paint或新的Path。這會強制更頻繁地執行垃圾收集器,並在硬體管道中略過快取和最佳化。為避免這種情況,請重複使用及變動物件:- 使用標準方法:標準
DrawScope方法 (例如drawRect和drawCircle) 會在內部重複使用Paint物件,不需要開發人員分配。 - 變動而非重新分配:撰寫自訂邏輯時,請使用
path.rewind清除現有的Path,而非例項化新的Path。 - 有效率地保留狀態:在可組合函式內,使用
remember { Path() }分配物件一次。如果您要建構可重複使用的自訂修飾符擴充功能,請使用DrawModifierNode實作自訂Modifier.Node,以便分配及重複使用物件,而不會導致新的堆積分配。
- 使用標準方法:標準
- 不要經常修改形狀
- 舉例來說,複雜的形狀、路徑和圓形都是使用材質遮罩算繪的。每次建立或修改路徑時,硬體管道都會建立新的遮罩,費用高昂。
- 不要經常修改點陣圖
- 每次變更點陣圖的內容時,系統都會在下次繪圖時,重新上傳為 GPU 紋理。
- 請謹慎使用 Alpha 版
- 使用
Modifier.alpha或 Compose Animation API 建立半透明的可組合項時,該項目通常會呈現在螢幕外緩衝區中,所需供應率會加倍。如要避免非重疊內容的螢幕外緩衝區負擔,請設定CompositingStrategy.ModulateAlpha。如果是個別繪圖呼叫,請直接將 Alpha 套用至繪圖指令 (例如color = Color.Red.copy(alpha = 0.5f)),不必建立圖層。