熱能 API

發布日期

Android 11 (API 級別 30) - Thermal API

Android 12 (API 級別 31) - NDK API

(預先發布版) Android 15 (DP1) - getThermalHeadroomThresholds()

應用程式的潛在效能會受限於裝置的熱力狀態,這可能會因天氣、最近使用情況和裝置的熱力設計等特性而異。裝置只能暫時維持一定時段的高效能,之後就會為了防止過熱而進行節流。實作作業的一大目標,是要在不超出熱溫限制的情況下達成效能目標。無須針對裝置進行特定最佳化,即可透過 Thermal API 達成此目標。此外,在針對效能問題進行偵錯時,知道裝置熱力狀態是否限制效能十分重要。

遊戲引擎通常會提供執行階段效能參數,可調整引擎加諸於裝置的工作負載。舉例來說,這些參數可以設定工作站執行緒數量、大型與小型核心的工作站執行緒相依性、GPU 擬真度選項,以及 framebuffer 解析度。在 Unity 引擎中,遊戲開發人員可以使用 Adaptive Performance 外掛程式變更品質設定,調整工作負載。如果是 Unreal Engine,請使用「可擴充性設定」動態調整畫質等級。

當裝置接近不安全的熱力狀態時,遊戲可以透過這些參數來減少工作負載,藉此避免發生過熱保護情形。如要避免發生過熱保護情形,您應監控裝置的熱力狀態,並主動調整遊戲引擎工作負載。

裝置一旦過熱,工作負載就必須降低至永續效能等級以下才能散熱。熱空間降至安全等級後,遊戲可以再次提高畫質設定,但請務必找出可持續的畫質等級,以獲得最佳遊戲時間。

您可以藉由輪詢 getThermalHeadroom 方法來監控裝置的熱力狀態。這個方法可預測裝置在不過熱的情況下,可維持目前效能等級的時間長度。如果這段時間比執行工作負載所需時間還短,則遊戲應將工作負載降低至永續等級。舉例來說,遊戲可以改用小型核心、降低影格速率或擬真度。

ADPF Thermal API 預先整合
圖 1. 熱力上升空間,未主動監控 getThermalHeadroom
ADPF Thermal API 整合後
圖 2:熱力上升空間,並主動監控 `getThermalHeadroom`

取得 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(0);
ALOGI("ThermalHeadroom: %f", thermal_headroom);

Java

float thermalHeadroom = powerManager.getThermalHeadroom(0);
Log.d("ADPF", "ThermalHeadroom: " + 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);

在熱力狀態改變時收到通知

您也可以避免輪詢 thermalHeadroom,直到 thermalStatus 達到特定層級 (例如:THERMAL_STATUS_LIGHT)。如要這麼做,您可以註冊回呼,讓系統在狀態變更時通知您。

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++ API (NDK API) 和 Java API (透過 JNI) 在原生 C++ 遊戲中實作 Thermal API,請參閱「適應性 Codelab」一節中的「整合 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()

經驗法則示例:

  1. 確認裝置支援 Thermal API。isAPISupported() 會檢查對 getThermalHeadroom 的第一次呼叫值,確保該值不是 0 或 NaN,如果第一個值是 0 或 NaN,則會略過使用 API。
  2. 如果 getCurrentThermalStatus() 傳回的值不是 THERMAL_STATUS_NONE,表示裝置正在進行熱節流。
  3. 如果 getCurrentThermalStatus() 持續傳回 THERMAL_STATUS_NONE,不一定代表裝置未處於過熱保護狀態。這可能表示裝置不支援「getCurrentThermalStatus()」。檢查 getThermalHeadroom() 的傳回值,確保裝置狀態正常。
  4. 如果 getThermalHeadroom() 傳回的值大於 1.0,狀態實際上可能是 THERMAL_STATUS_SEVERE 或更高,請立即減少工作負載,並維持較低的工作負載,直到 getThermalHeadroom() 傳回較低的值為止。
  5. 如果 getThermalHeadroom() 傳回 0.95 的值,實際狀態可能為 THERMAL_STATUS_MODERATE 以上,請立即減少工作負載,並持續觀察,避免讀取到更高的值。
  6. 如果 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.
      }
    }
  }

圖表:

ADPF 經驗法則
範例
圖 3. 判斷舊裝置是否支援 Thermal API 的啟發式方法範例