已发布:
Android 11 (API 级别 30) - 热管理 API
Android 12 (API 级别 31) - NDK API
(预览) Android 15 (DP1) - getThermalHeadroomThresholds()
应用的潜在性能受设备的热状态限制,热状态会根据天气、近期使用情况和设备的散热设计等特性而有所不同。设备只能在热限制之前保持高性能一段时间。您的实现的关键目标应该是实现性能目标,而不会超过热限制。热管理 API 使其成为可能,无需进行特定于设备的优化。此外,在调试性能问题时,了解设备的热状态是否限制了性能非常重要。
游戏引擎通常具有运行时性能参数,可以调整引擎对设备造成的负载。例如,这些参数可以设置工作线程数、大内核和小内核的工作线程关联性、GPU 保真度选项和帧缓冲区分辨率。在 Unity 引擎中,游戏开发者可以通过使用 自适应性能插件 更改 质量设置 来调整工作负载。对于 Unreal 引擎,请使用 可扩展性设置 动态调整质量级别。
当设备接近不安全的热状态时,您的游戏可以通过降低这些参数的工作负载来避免被限制。为避免限制,您应监控设备的热状态并主动调整游戏引擎的工作负载。一旦设备过热,工作负载必须降至可持续性能级别以下才能散热。在热余量下降到更安全的水平后,游戏可以再次提高质量设置,但请确保找到最佳游戏时间的可持续质量级别。
您可以通过轮询 getThermalHeadroom
方法来监控设备的热状态。此方法预测设备在过热之前可以维持当前性能级别的时间长度。如果时间小于运行工作负载所需的时间,则您的游戏应将工作负载降低到可持续级别。例如,游戏可以切换到较小的内核、降低帧率或降低保真度。
获取热管理程序
要使用热管理 API,首先需要获取热管理程序。
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);
热状态更改时收到通知
您还可以避免轮询 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 的完整指南,请查看 Adaptability codelab 部分中的集成 Thermal API 部分。
散热余量指南
您可以通过轮询 getThermalHeadroom
方法来监控设备的散热状态。此方法预测设备在达到 THERMAL_STATUS_SEVERE
之前可以维持当前性能级别的时长。例如,如果 getThermalHeadroom(30)
返回 0.8,则表示在 30 秒内,预计余量将达到 0.8,距离严重降频还有 0.2 的距离,或者说是 1.0。如果时间小于运行工作负载所需的时间,则游戏应将工作负载降低到可持续的级别。例如,游戏可以降低帧率、降低保真度或减少网络连接工作。
散热状态及其含义
- 如果设备未发生散热降频
- 存在一些降频,但对性能没有显著影响
- 严重的降频会影响性能
Thermal API 的设备限制
由于旧设备上 Thermal API 的实现,Thermal API 存在一些已知的限制或其他要求。限制和解决方法如下:
- 不要过于频繁地调用
GetThermalHeadroom()
API。这样做会导致 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.
}
}
}
图表