Data premiery:
Android 11 (poziom API 30) – interfejs Thermal API
Android 12 (poziom 31 interfejsu API) – interfejs NDK API
(Wersja zapoznawcza) Android 15 (DP1) – getThermalHeadroomThresholds()
Potencjalna wydajność aplikacji jest ograniczona przez stan termiczny urządzenia, który może się różnić w zależności od takich czynników jak pogoda, ostatnie użycie i konstrukcja termiczna urządzenia. Urządzenia mogą utrzymywać wysoki poziom wydajności tylko przez ograniczony czas, zanim nastąpi ograniczenie wydajności z powodu temperatury. Głównym celem wdrożenia powinno być osiągnięcie celów związanych ze skutecznością bez przekraczania ograniczeń termicznych. Interfejs Thermal API umożliwia to bez konieczności optymalizacji pod kątem konkretnego urządzenia. Ponadto podczas debugowania problemów z wydajnością ważne jest, aby wiedzieć, czy stan termiczny urządzenia ogranicza wydajność.
Silniki gier mają zwykle parametry wydajności środowiska wykonawczego, które mogą dostosowywać obciążenie urządzenia. Za pomocą tych parametrów można na przykład ustawić liczbę wątków instancji roboczych, powiązanie wątków instancji roboczych z dużymi i małymi rdzeniami, opcje wierności GPU oraz rozdzielczość bufora ramki. W silniku Unity deweloperzy gier mogą dostosowywać obciążenie, zmieniając ustawienia jakości za pomocą wtyczki Adaptive Performance. W przypadku Unreal Engine użyj ustawień skalowalności, aby dynamicznie dostosowywać poziomy jakości.
Gdy urządzenie zbliża się do niebezpiecznego stanu termicznego, gra może uniknąć ograniczenia wydajności, zmniejszając obciążenie za pomocą tych parametrów. Aby uniknąć ograniczenia przepustowości, monitoruj stan termiczny urządzenia i proaktywnie dostosowuj obciążenie silnika gry.
Gdy urządzenie się przegrzeje, obciążenie musi spaść poniżej poziomu wydajności, który można utrzymać, aby rozproszyć ciepło. Gdy zapas mocy cieplnej zmniejszy się do bezpiecznego poziomu, gra może ponownie zwiększyć ustawienia jakości, ale musi znaleźć zrównoważony poziom jakości, aby zapewnić optymalny czas gry.
Stan termiczny urządzenia możesz monitorować, wywołując metodę
getThermalHeadroom. Ta metoda przewiduje, jak długo urządzenie może utrzymać obecny poziom wydajności bez przegrzewania się. Jeśli czas jest krótszy niż czas potrzebny do uruchomienia zadania, gra powinna zmniejszyć obciążenie do poziomu, który można utrzymać. Na przykład gra może przejść na mniejsze rdzenie, zmniejszyć liczbę klatek na sekundę lub obniżyć jakość.
Pobieranie Menedżera termicznego
Aby korzystać z interfejsu Thermal API, musisz najpierw uzyskać dostęp do Menedżera termicznego.
C++
AThermalManager* thermal_manager = AThermal_acquireManager();
Java
PowerManager powerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE);
Prognozowanie rezerwy termicznej z wyprzedzeniem o x sekund w celu zwiększenia kontroli
Możesz poprosić system o prognozowanie temperatury za x sekund przy bieżącym obciążeniu. Dzięki temu zyskujesz większą kontrolę i więcej czasu na reakcję, ponieważ zmniejsza to obciążenie, aby zapobiec włączeniu się ograniczenia termicznego.
Wynik mieści się w zakresie od 0,0f (brak ograniczania przepustowości,THERMAL_STATUS_NONE)
do 1,0f (duże ograniczenie,THERMAL_STATUS_SEVERE). Jeśli w swoich grach masz różne poziomy jakości grafiki, możesz skorzystać z naszych wskazówek dotyczących zapasu mocy cieplnej.
C++
float thermal_headroom = AThermal_getThermalHeadroom(0);
ALOGI("ThermalHeadroom: %f", thermal_headroom);
Java
float thermalHeadroom = powerManager.getThermalHeadroom(0);
Log.d("ADPF", "ThermalHeadroom: " + thermalHeadroom);
Możesz też sprawdzić stan termiczny, aby uzyskać więcej informacji.
Każdy model urządzenia może być zaprojektowany inaczej. Niektóre urządzenia mogą lepiej rozprowadzać ciepło, dzięki czemu mogą wytrzymać wyższy margines termiczny, zanim dojdzie do ograniczenia wydajności. Jeśli chcesz zapoznać się z uproszczonym podziałem zakresów rezerwy termicznej, możesz sprawdzić stan termiczny, aby zrozumieć wartość rezerwy termicznej na bieżącym urządzeniu.
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);
Otrzymywanie powiadomień o zmianach stanu termicznego
Możesz też uniknąć sondowania thermalHeadroom, dopóki thermalStatus nie osiągnie określonego poziomu (np. THERMAL_STATUS_LIGHT). W tym celu możesz zarejestrować wywołanie zwrotne, aby system powiadamiał Cię o każdej zmianie stanu.
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);
Pamiętaj, aby po zakończeniu pracy usunąć detektor.
C++
int result = AThermal_unregisterThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
// failed, check whether the callback has been registered previously
}
Java
powerManager.removeThermalStatusListener(listener);
Uporządkuj
Gdy skończysz, musisz wyczyścić uzyskany obiekt thermal_manager. Jeśli używasz języka Java, odwołanie do klasy PowerManager może zostać automatycznie usunięte przez mechanizm odśmiecania pamięci. Jeśli jednak używasz interfejsu Java API za pomocą JNI i zachowujesz odwołanie, pamiętaj, aby je usunąć.
C++
AThermal_releaseManager(thermal_manager);
Pełny przewodnik po implementacji interfejsu Thermal API w natywnej grze w C++ z użyciem interfejsu C++ API (NDK API) i interfejsu Java API (przez JNI) znajdziesz w sekcji Integracja interfejsu Thermal API w sekcji Adaptability codelab.
Wytyczne dotyczące zapasu mocy cieplnej
Stan termiczny urządzenia możesz monitorować, odpytując metodę
getThermalHeadroom. Ta metoda przewiduje, jak długo urządzenie może utrzymać obecny poziom wydajności, zanim osiągnie THERMAL_STATUS_SEVERE.
Jeśli na przykład funkcja getThermalHeadroom(30) zwraca wartość 0,8, oznacza to, że w ciągu 30 sekund wartość headroom osiągnie 0,8, czyli będzie o 0,2 jednostki oddalona od wartości 1,0, która oznacza poważne ograniczanie przepustowości. Jeśli czas jest krótszy niż czas potrzebny do uruchomienia zadania, gra powinna zmniejszyć obciążenie do poziomu, który można utrzymać. Może na przykład zmniejszyć liczbę klatek na sekundę, obniżyć jakość lub ograniczyć pracę związaną z łącznością sieciową.
Stany termiczne i ich znaczenie
- Jeśli urządzenie nie jest poddawane ograniczeniu termicznemu:
- Ograniczenie przepustowości, ale bez istotnego wpływu na wydajność:
- Znaczne ograniczanie przepustowości, które wpływa na wydajność:
Ograniczenia interfejsu Thermal API dotyczące urządzeń
Istnieją pewne znane ograniczenia lub dodatkowe wymagania dotyczące interfejsu Thermal API, które wynikają z jego implementacji na starszych urządzeniach. Ograniczenia i sposoby ich obejścia są następujące:
- Nie wywołuj interfejsu
GetThermalHeadroom()API zbyt często. W takim przypadku interfejs API zwracaNaN. Nie należy wywoływać tej funkcji częściej niż raz na 10 sekund. - Unikaj wywoływania z wielu wątków, ponieważ trudniej jest zapewnić częstotliwość wywoływania i może to spowodować, że interfejs API zwróci wartość
NaN. - Jeśli początkowa wartość
GetThermalHeadroom()to NaN, interfejs API nie jest dostępny na urządzeniu. - Jeśli funkcja
GetThermalHeadroom()zwraca wysoką wartość (np. 0,85 lub więcej), a funkcjaGetCurrentThermalStatus()nadal zwraca wartośćTHERMAL_STATUS_NONE, stan prawdopodobnie nie jest aktualizowany. Użyj heurystyki, aby oszacować prawidłowy stan ograniczenia termicznego, lub po prostu użyjgetThermalHeadroom()bezgetCurrentThermalStatus().
Przykład heurystyki:
- Sprawdź, czy interfejs Thermal API jest obsługiwany.
isAPISupported()sprawdza wartość pierwszego wywołania funkcjigetThermalHeadroom, aby upewnić się, że nie jest to 0 ani NaN, i pomija użycie interfejsu API, jeśli pierwsza wartość to 0 lub NaN. - Jeśli
getCurrentThermalStatus()zwraca wartość inną niżTHERMAL_STATUS_NONE, oznacza to, że urządzenie jest poddawane ograniczeniu termicznemu. - Jeśli
getCurrentThermalStatus()stale zwraca wartośćTHERMAL_STATUS_NONE, nie musi to oznaczać, że urządzenie nie jest poddawane ograniczeniu termicznemu. Może to oznaczać, że usługagetCurrentThermalStatus()nie jest obsługiwana na tym urządzeniu. Sprawdź wartość zwracaną przezgetThermalHeadroom(), aby upewnić się, że urządzenie jest w odpowiednim stanie. - Jeśli
getThermalHeadroom()zwraca wartość > 1,0, stan może być w rzeczywistości równyTHERMAL_STATUS_SEVERElub wyższy. Należy natychmiast zmniejszyć obciążenie i utrzymywać je na niższym poziomie, dopókigetThermalHeadroom()nie zwróci niższej wartości. - Jeśli funkcja
getThermalHeadroom()zwróci wartość 0,95, stan może być w rzeczywistości równyTHERMAL_STATUS_MODERATElub wyższy. W takim przypadku należy natychmiast zmniejszyć obciążenie i zachować czujność, aby zapobiec wyższym odczytom. - Jeśli funkcja
getThermalHeadroom()zwróci wartość 0, 85, stan może być w rzeczywistościTHERMAL_STATUS_LIGHT. Zachowaj ostrożność i w miarę możliwości zmniejsz obciążenie.
Pseudokod:
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.
}
}
}
Diagram: