跳至主要内容

搞懂 Android 的三種 Build Variant:user、userdebug 與 eng 差在哪

在 Android(AOSP)開發流程裡,lunch 選 target 時你一定看過類似 aosp_arm64-userdebug 這種寫法。中間那個 userdebug 就是 build variant(建置變體),它決定了刷出來的 image「多開放、多好除錯、多接近正式出貨版」。理解這三種 variant 的差異,是判斷「這顆 bug 該用哪種 image 重現」「效能數據能不能信」的基礎。

本文完整說明 useruserdebugeng 三種 variant 的定位、底層屬性差異,以及實務上該怎麼選。


一、三種 variant 的定位

Build variant 本質上是同一份原始碼、用不同的編譯設定產生的三種 image。它們的差異不在功能,而在「安全鎖定程度」與「除錯便利性」的取捨。

user——出貨版

這是真正交到消費者手上的版本,鎖得最死。系統以最高安全性運作,不能 root,幾乎所有除錯管道都關閉。手機店裡買到的量產機跑的就是 user build。

userdebug——可除錯的出貨版

行為幾乎等同 user,做了同樣的最佳化,但額外保留了除錯能力(可提權為 root、開啟部分 log)。它是 QA、測試、driver 驗證的主力,因為它「跟出貨版夠像,又抓得到問題」。

eng——工程版

給開發者在實驗室快速迭代用的,開放程度最高。預設就是 root、debug 工具全開、不做效能最佳化,重視的是「改 code、重刷、驗證」的速度,而不是跑起來像不像正式機。


二、底層屬性差異

三種 variant 的差別,最終體現在幾個系統屬性(system property)與模組安裝規則上。以下逐項拆解。

1. ro.secure 與 root 權限

ro.secure 決定 adb daemon 是否以安全模式啟動:

  • eng:ro.secure=0,adb 開機後直接就是 root,不需要任何提權動作。
  • userdebug:ro.secure=1,但因為 ro.debuggable=1,可以用 adb root 重啟 adbd 取得 root。
  • user:ro.secure=1ro.debuggable=0,adb root 直接被拒絕,無法提權。

這是三者最常被感受到的差異:eng 隨手就 root,userdebug 要多打一個指令,user 根本不給。

2. ro.debuggable

這個屬性控制整個系統是否「可被除錯」,影響 adb rootjdwp 附加除錯器、部分 SELinux 與 log 行為:

  • enguserdebug:ro.debuggable=1
  • user:ro.debuggable=0

換句話說,只要你需要接除錯器或看比較深的 log,就不能用 user

3. 模組安裝規則(module tags)

AOSP 的 build 系統會依 variant 決定哪些模組要被包進 image。傳統上模組可以標 engdebuguser 等 tag(現代寫法用 LOCAL_MODULE_TAGS 或對應的 Soong 屬性):

  • eng:安裝標了 engdebug 的模組,加上所有沒指定 variant 的產品模組——也就是「裝好裝滿」,連工程專用工具都在。
  • userdebug:安裝標了 debug 的模組,以及 product 設定要求的模組,但不含 eng 專屬模組。
  • user:只安裝 product 明確要求的模組,最精簡。

所以有些只在 eng 才存在的測試工具或 binary,一旦切到 userdebug 就會消失——這常常是「在我機器上有、在測試機沒有」的原因。

4. dexpreopt 與效能最佳化

  • eng:預設關閉 dexpreopt(APP 的 dex 不在編譯期預先最佳化)。好處是 build 快、改 framework 後重刷快;代價是開機慢、跑起來慢
  • userdebug / user:啟用 dexpreopt 等最佳化,開機與執行效能接近真實出貨狀態。

這一點對效能測試至關重要——eng build 上量到的開機時間、流暢度、耗電都不能拿來當出貨依據,因為它根本沒最佳化。

5. Log、assert 與其他行為

eng build 通常開著更多 verbose log、保留 LOG_NDEBUG 相關輸出,某些 assert/檢查也更嚴格(方便早期抓錯)。user build 則會關閉大量除錯 log 以節省效能與避免資訊外洩。userdebug 介於兩者之間,偏向 user 但保留關鍵除錯 log。


三、對照表

項目enguserdebuguser
定位工程開發版可除錯的出貨版正式出貨版
ro.secure011
ro.debuggable110
預設 root是(開機即 root)否(可 adb root 提權)否(無法提權)
安裝模組eng + debug + 全部debug + product 要求僅 product 要求
dexpreopt / 最佳化關閉啟用啟用
開機/執行效能慢(未最佳化)接近出貨出貨等級
除錯 log最多中等最少
主要用途快速改 code、重刷QA/測試/driver 驗證消費者

四、實務上該怎麼選

用 eng 的時機

你正在頻繁改 framework 或 native 層、需要立刻 root、需要工程專用工具、而且不在乎效能表現——例如 bring-up 新板子、debug 開機流程、快速試錯。重點是迭代速度。

用 userdebug 的時機

你要做 QA、驗收、跑效能測試,或是重現「只有在接近正式環境才會出現」的 bug。因為它做了與 user 相同的最佳化,量到的數據可信;又保留 root 與 log,問題抓得到。大多數測試部門的日常 image 都是 userdebug

用 user 的時機

出貨前的最終驗證,以及實際交付給消費者。任何要送 CTS/GMS 認證、要模擬真實使用者環境、或要確認除錯管道確實關閉的場景,都必須用 user


五、一句話總結

eng 是「全開、方便改,但離出貨版最遠」;user 是「完全鎖死的出貨版」;userdebug 則是「跟出貨版幾乎一樣,只是留了一道除錯後門」。

因此有一個實務原則值得記住:要重現使用者問題、要量效能、要做 QA,請用 userdebug 而不是 engeng 沒最佳化又裝了一堆工程模組,它的行為不能代表使用者手上的機器;而 user 又鎖到你抓不了問題。userdebug 正是為了這個「像出貨、又可除錯」的甜蜜點而存在。