Uscita:
Android 11 (livello API 30) - API Thermal
Android 12 (livello API 31) - API NDK
(Anteprima) Android 15 (DP1) - getThermalHeadroomThresholds()
Il rendimento potenziale della tua app è limitato dallo stato termico del dispositivo, che può variare in base a caratteristiche come meteo, utilizzo recente e design termico del dispositivo. I dispositivi possono mantenere un livello elevato di prestazioni solo per un periodo di tempo limitato prima di essere sottoposti a throttling termico. Uno scopo fondamentale della tua implementazione dovrebbe essere raggiungere gli obiettivi di rendimento senza superare le limitazioni termiche. L'API Thermal lo rende possibile senza dover eseguire ottimizzazioni specifiche per il dispositivo. Inoltre, durante il debug dei problemi di rendimento, è importante sapere se lo stato termico del dispositivo sta limitando il rendimento.
I motori di gioco in genere hanno parametri di prestazioni di runtime che possono regolare il carico di lavoro imposto dal motore sul dispositivo. Ad esempio, questi parametri possono impostare il numero di thread worker, l'affinità dei thread worker per i core grandi e piccoli, le opzioni di fedeltà della GPU e le risoluzioni del framebuffer. In Unity Engine, gli sviluppatori di giochi possono regolare il carico di lavoro modificando le Impostazioni di qualità utilizzando il plug-in Adaptive Performance. Per Unreal Engine, utilizza le Impostazioni di scalabilità per regolare dinamicamente i livelli di qualità.
Quando un dispositivo si avvicina a uno stato termico non sicuro, il gioco può evitare di essere limitato diminuendo il carico di lavoro tramite questi parametri. Per evitare il throttling, devi monitorare lo stato termico del dispositivo e regolare in modo proattivo il carico di lavoro del motore di gioco. Quando il dispositivo si surriscalda, il carico di lavoro deve diminuire al di sotto del livello di prestazioni sostenibile per dissipare il calore. Dopo che il margine termico è diminuito a livelli più sicuri, il gioco può aumentare di nuovo le impostazioni di qualità, ma assicurati di trovare un livello di qualità sostenibile per un tempo di gioco ottimale.
Puoi monitorare lo stato termico del dispositivo eseguendo il polling del metodo getThermalHeadroom
. Questo metodo prevede per quanto tempo il dispositivo può mantenere il livello di prestazioni corrente senza surriscaldarsi. Se il tempo è inferiore al tempo necessario per eseguire il carico di lavoro, il gioco dovrebbe ridurlo a un livello sostenibile. Ad esempio, il gioco può passare a core più piccoli, ridurre la frequenza frame o la fedeltà.


Acquista Thermal Manager
Per utilizzare l'API Thermal, devi prima acquisire Thermal Manager
C++
AThermalManager* thermal_manager = AThermal_acquireManager();
Java
PowerManager powerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
Prevedi il margine termico con x secondi di anticipo per un maggiore controllo
Puoi chiedere al sistema di prevedere la temperatura con x secondi di anticipo con il carico di lavoro corrente. In questo modo hai un controllo più granulare e più tempo per reagire riducendo il carico di lavoro per evitare l'attivazione del throttling termico.
Il risultato va da 0,0 f (nessun throttling, THERMAL_STATUS_NONE
) a 1,0 f
(throttling elevato, THERMAL_STATUS_SEVERE
).
Se i tuoi giochi hanno diversi livelli di qualità grafica, puoi seguire le nostre
linee guida sul margine termico.
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);
In alternativa, puoi fare affidamento sullo stato termico per avere chiarimenti
Ogni modello di dispositivo potrebbe essere progettato in modo diverso. Alcuni dispositivi potrebbero essere in grado di distribuire meglio il calore e quindi di resistere a un margine termico più elevato prima di essere limitati. Se vuoi leggere un raggruppamento semplificato di intervalli di margine termico, puoi controllare lo stato termico per comprendere il valore del margine termico sul dispositivo attuale.
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);
Ricevere notifiche quando cambia lo stato termico
Puoi anche evitare di eseguire il polling di thermalHeadroom
finché thermalStatus
non raggiunge un determinato livello (ad esempio THERMAL_STATUS_LIGHT
). Per farlo, puoi registrare un callback per consentire al sistema di inviarti una notifica ogni volta che lo stato cambia.
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);
Ricordati di rimuovere l'ascoltatore al termine
C++
int result = AThermal_unregisterThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
// failed, check whether the callback has been registered previously
}
Java
powerManager.removeThermalStatusListener(listener);
Pulizia
Al termine, dovrai ripulire il file thermal_manager che hai acquisito. Se utilizzi Java, il riferimento a PowerManager può essere gestito automaticamente tramite il garbage collection. Tuttavia, se utilizzi l'API Java tramite JNI e hai conservato un riferimento, ricordati di ripulirlo.
C++
AThermal_releaseManager(thermal_manager);
Per una guida completa su come implementare l'API Thermal in un gioco nativo C++ utilizzando sia l'API C++ (API NDK) sia l'API Java (tramite JNI), consulta la sezione Integrare l'API Thermal nella sezione Codelab sull'adattabilità.
Linee guida per il margine termico
Puoi monitorare lo stato termico del dispositivo eseguendo il polling del metodo getThermalHeadroom
. Questo metodo prevede per quanto tempo il dispositivo può mantenere il livello di rendimento corrente prima di raggiungere THERMAL_STATUS_SEVERE
.
Ad esempio, se getThermalHeadroom(30)
restituisce 0,8, indica che in 30 secondi il margine di manovra dovrebbe raggiungere 0,8, dove manca 0,2 per il throttling severo o 1,0. Se il tempo è inferiore a quello necessario per eseguire il carico di lavoro, il gioco dovrebbe ridurlo a un livello sostenibile. Ad esempio, il gioco può ridurre la frequenza dei fotogrammi, la fedeltà o il lavoro di connettività di rete.
Stati termici e relativo significato
- Se il dispositivo non è soggetto a throttling termico:
- Un po' di throttling, ma nessun impatto significativo sul rendimento:
- Rallentamento significativo che influisce sulle prestazioni:
Limitazioni dei dispositivi dell'API Thermal
Esistono alcune limitazioni o requisiti aggiuntivi noti dell'API Thermal, a causa delle implementazioni dell'API Thermal sui dispositivi meno recenti. Di seguito sono riportate le limitazioni e le modalità per ovviarle:
- Non chiamare l'API
GetThermalHeadroom()
troppo spesso. In questo caso, l'API restituisceNaN
. Non dovresti chiamarlo più di una volta ogni 10 secondi. - Evita di effettuare chiamate da più thread, perché è più difficile garantire la frequenza di chiamata e l'API potrebbe restituire
NaN
. - Se il valore iniziale di
GetThermalHeadroom()
è NaN, l'API non è disponibile sul dispositivo - Se
GetThermalHeadroom()
restituisce un valore elevato (ad es. 0,85 o più) eGetCurrentThermalStatus()
restituisce ancoraTHERMAL_STATUS_NONE
, è probabile che lo stato non sia aggiornato. Utilizza le strategie di ricerca heuristica per stimare lo stato corretto della limitazione termica o utilizza semplicementegetThermalHeadroom()
senzagetCurrentThermalStatus()
.
Esempio di euristiche:
- Verifica che l'API Thermal sia supportata.
isAPISupported()
controlla il valore della prima chiamata agetThermalHeadroom
per assicurarsi che non sia 0 o NaN e salta l'utilizzo dell'API se il primo valore è 0 o NaN. - Se
getCurrentThermalStatus()
restituisce un valore diverso daTHERMAL_STATUS_NONE
, il dispositivo è sottoposto a throttling termico. - Se
getCurrentThermalStatus()
continua a restituireTHERMAL_STATUS_NONE
, non significa necessariamente che il dispositivo non è soggetto a throttling termico. Ciò potrebbe indicare chegetCurrentThermalStatus()
non è supportato sul dispositivo. Controlla il valore restituito digetThermalHeadroom()
per verificare le condizioni del dispositivo. - Se
getThermalHeadroom()
restituisce un valore maggiore di 1, 0, lo stato potrebbe essere effettivamenteTHERMAL_STATUS_SEVERE
o superiore. Riduci immediatamente il carico di lavoro e mantienilo basso finchégetThermalHeadroom()
non restituisce un valore inferiore. - Se
getThermalHeadroom()
restituisce un valore pari a 0,95, lo stato potrebbe essere effettivamenteTHERMAL_STATUS_MODERATE
o superiore, riduci immediatamente il carico di lavoro e mantieni l'attenzione per evitare una lettura più alta - Se
getThermalHeadroom()
restituisce un valore di 0,85, lo stato potrebbe essere effettivamenteTHERMAL_STATUS_LIGHT
, quindi fai attenzione e riduci il carico di lavoro se possibile
Pseudocodice:
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.
}
}
}
Diagramma: