已發布:
Android 11 (API 級別 30) - Thermal API
Android 12 (API 級別 31) - NDK API
(預先發布版) Android 15 (DP1) - getThermalHeadroomThresholds()
應用程式的潛在效能會受限於裝置的熱力狀態,這可能會因天氣、最近使用情況和裝置的熱力設計等特性而異。裝置只能暫時維持一定時段的高效能,之後就會為了防止過熱而進行節流。實作作業的一大目標,是要在不超出熱溫限制的情況下達成效能目標。溫度 API 可讓您不必進行裝置專屬最佳化。此外,在針對效能問題進行偵錯時,知道裝置熱力狀態是否限制效能十分重要。
遊戲引擎通常會提供執行階段效能參數,可調整引擎加諸於裝置的工作負載。舉例來說,這些參數可以設定工作站執行緒數量、大型與小型核心的工作站執行緒相依性、GPU 擬真度選項,以及 framebuffer 解析度。在 Unity Engine 中,遊戲開發人員可以使用Adaptive Performance 外掛程式變更品質設定,藉此調整工作負載。針對 Unreal Engine,請使用可調整性設定動態調整品質等級。
當裝置接近不安全的熱力狀態時,遊戲可以透過這些參數來減少工作負載,藉此避免發生過熱保護情形。如要避免發生過熱保護情形,您應監控裝置的熱力狀態,並主動調整遊戲引擎工作負載。裝置一旦過熱,工作負載就必須降低至永續效能等級以下才能散熱。熱能裕度降至較安全的程度後,遊戲可以再次提高品質設定,但請務必找到可持續的品質等級,以便獲得最佳遊戲時間。
您可以藉由輪詢 getThermalHeadroom
方法來監控裝置的熱力狀態。這個方法可預測裝置在不過熱的情況下,可維持目前效能等級的時間長度。如果這段時間比執行工作負載所需時間還短,則遊戲應將工作負載降低至永續等級。舉例來說,遊戲可以改用小型核心、降低影格速率或降低擬真度。
![ADPF Thermal API 預先整合](https://developer.android.google.cn/static/games/optimize/adpf/images/adpf_thermal_pre-integration.png?authuser=3&hl=zh-tw)
![ADPF Thermal API 整合後](https://developer.android.google.cn/static/games/optimize/adpf/images/adpf_thermal_post-integration.png?authuser=3&hl=zh-tw)
取得 Thermal Manager
如要使用 Thermal API,您必須先取得 Thermal Manager
C++
AThermalManager* thermal_manager = AThermal_acquireManager();
Java
PowerManager powerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
預測熱力上升空間 x 秒,以便進一步控管
您可以要求系統根據目前的工作量,預測 x 秒後的溫度。這樣一來,您就能更精細地控管,並有更多時間做出反應,藉由減少工作負載來防止熱力節流功能啟用。
結果範圍為 0.0f (不限速,THERMAL_STATUS_NONE
) 到 1.0f (嚴重限速,THERMAL_STATUS_SEVERE
)。如果遊戲有不同的圖像品質等級,您可以遵循我們的熱力裕度指南。
C++
float thermal_headroom = AThermal_getThermalHeadroom(10);
ALOGI("ThermalHeadroom in 10 sec: %f", thermal_headroom);
Java
float thermalHeadroom = powerManager.getThermalHeadroom(10);
Log.d("ADPF", "ThermalHeadroom in 10 sec: " + thermalHeadroom);
或者,請依據熱力狀態進行確認
每個裝置型號的設計可能不同。部分裝置可能可更有效地散熱,因此在節流前可承受更高的熱量餘裕空間。如要讀取簡易的熱力裕度範圍群組,您可以查看熱力狀態,瞭解目前裝置的熱力裕度值。
C++
AThermalStatus thermal_status = AThermal_getCurrentThermalStatus(thermal_manager);
ALOGI("ThermalStatus is: %d", thermal_status);
Java
int thermalStatus = powerManager.getCurrentThermalStatus();
Log.d("ADPF", "ThermalStatus is: " + thermalStatus);
在熱力狀態變更時收到通知
您也可以在 thermalStatus
達到特定層級 (例如:THERMAL_STATUS_LIGHT
) 之前,避免輪詢 thermalHeadroom
。如要這麼做,您可以註冊回呼,讓系統在狀態變更時通知您。
C++
int result = AThermal_registerThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
// failed, check whether you have previously registered callback that
// hasn’t been unregistered
}
Java
// PowerManager.OnThermalStatusChangedListener is an interface, thus you can
// also define a class that implements the methods
PowerManager.OnThermalStatusChangedListener listener = new
PowerManager.OnThermalStatusChangedListener() {
@Override
public void onThermalStatusChanged(int status) {
Log.d("ADPF", "ThermalStatus changed: " + status);
// check the status and flip the flag to start/stop pooling when
// applicable
}
};
powerManager.addThermalStatusListener(listener);
請記得在完成後移除事件監聽器
C++
int result = AThermal_unregisterThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
// failed, check whether the callback has been registered previously
}
Java
powerManager.removeThermalStatusListener(listener);
清除
完成後,您必須清理取得的 thermal_manager。如果您使用的是 Java,系統可以自動為您收集 PowerManager 參照的垃圾。不過,如果您是透過 JNI 使用 Java API 並保留參照,請務必清除參照!
C++
AThermal_releaseManager(thermal_manager);
如需完整指南,瞭解如何在原生 C++ 遊戲中使用 C++ API (NDK API) 和 Java API (透過 JNI) 實作 Thermal API,請參閱「適應性程式碼研究室」一節中的「整合 Thermal API」一節。
熱力上升空間規範
您可以藉由輪詢 getThermalHeadroom
方法來監控裝置的熱力狀態。這個方法可預測裝置在達到 THERMAL_STATUS_SEVERE
之前,可維持目前效能等級的時間長度。舉例來說,如果 getThermalHeadroom(30)
傳回 0.8,表示在 30 秒內,預定可用空間會達到 0.8,距離嚴重限速 (1.0) 的距離為 0.2。如果這段時間比執行工作負載所需時間還短,則遊戲應將工作負載降低至永續等級。舉例來說,遊戲可以降低影格速率、降低擬真度或減少網路連線工作。
溫度狀態和含義
- 如果裝置未處於過熱保護狀態:
- 已進行部分過熱保護,但對效能沒有太大影響:
- 發生會影響效能的大幅度過熱保護情形:
Thermal API 的裝置限制
由於在舊版裝置上實作 Thermal API,因此 Thermal API 有一些已知的限制或額外需求。限制和解決方法如下:
- 請勿過度頻繁呼叫
GetThermalHeadroom()
API。如此一來,API 就會傳回NaN
。每 10 秒不得呼叫多次。 - 請避免從多個執行緒呼叫,因為這會使確保呼叫頻率變得更加困難,並可能導致 API 傳回
NaN
。 - 如果
GetThermalHeadroom()
的初始值為 NaN,則裝置無法使用 API - 如果
GetThermalHeadroom()
傳回高值 (例如 0.85 以上),而GetCurrentThermalStatus()
仍傳回THERMAL_STATUS_NONE
,則狀態可能未更新。使用推論法估算正確的溫度調節狀態,或只使用getThermalHeadroom()
而不使用getCurrentThermalStatus()
。
經驗法則示例:
- 確認是否支援 Thermal API。
isAPISupported()
會檢查對getThermalHeadroom
的第一次呼叫的值,確保該值不是 0 或 NaN,如果第一次的值是 0 或 NaN,就會略過使用 API。 - 如果
getCurrentThermalStatus()
傳回的值不是THERMAL_STATUS_NONE
,表示裝置正在受到熱限速限制。 - 如果
getCurrentThermalStatus()
持續傳回THERMAL_STATUS_NONE
,並不一定代表裝置未受到過熱保護。這可能表示裝置不支援getCurrentThermalStatus()
。檢查getThermalHeadroom()
的傳回值,確保裝置狀態。 - 如果
getThermalHeadroom()
傳回的值大於 1.0,實際狀態可能為THERMAL_STATUS_SEVERE
或更高,請立即降低工作負載並維持較低的工作負載,直到getThermalHeadroom()
傳回較低的值為止 - 如果
getThermalHeadroom()
傳回的值為 0.95,實際狀態可能為THERMAL_STATUS_MODERATE
或更高,請立即減少工作負載,並持續監控,以免讀數升高 - 如果
getThermalHeadroom()
傳回的值為 0.85,實際狀態可能為THERMAL_STATUS_LIGHT
,請繼續留意,並盡可能減少工作負載
虛擬程式碼:
bool isAPISupported() {
float first_value_of_thermal_headroom = getThermalHeadroom();
if ( first_value_of_thermal_headroom == 0 ||
first_value_of_thermal_headroom == NaN ) {
// Checked the thermal Headroom API's initial return value
// it is NaN or 0,so, return false (not supported)
return false;
}
return true;
}
if (!isAPISupported()) {
// Checked the thermal Headroom API's initial return value, it is NaN or 0
// Don’t use the API
} else {
// Use thermalStatus API to check if it returns valid values.
if (getCurrentThermalStatus() > THERMAL_STATUS_NONE) {
// The device IS being thermally throttled
} else {
// The device is not being thermally throttled currently. However, it
// could also be an indicator that the ThermalStatus API may not be
// supported in the device.
// Currently this API uses predefined threshold values for thermal status
// mapping. In the future you may be able to query this directly.
float thermal_headroom = getThermalHeadroom();
if ( thermal_headroom > 1.0) {
// The device COULD be severely throttled.
} else if ( thermal_headroom > 0.95) {
// The device COULD be moderately throttled.
} else if ( thermal_headroom > 0.85) {
// The device COULD be experiencing light throttling.
}
}
}
圖表: