ProfilingManager obsługuje przechwytywanie profili na podstawie wyzwalaczy systemowych. System zarządza procesem nagrywania i przekazuje wynikowy profil do Twojej aplikacji.
Aktywatory są powiązane ze zdarzeniami o kluczowym znaczeniu dla wydajności. Profile rejestrowane przez system zawierają szczegółowe informacje na potrzeby debugowania dotyczące głównych ścieżek użytkowników powiązanych z tymi regułami.
Przechwytywanie danych historycznych
Wiele wyzwalaczy wymaga analizy danych historycznych poprzedzających zdarzenie. Sygnał jest często konsekwencją problemu, a nie jego główną przyczyną. Jeśli rozpoczniesz profilowanie dopiero po wystąpieniu wyzwalacza, główna przyczyna może już być utracona.
Na przykład długo trwająca operacja w wątku UI powoduje błąd Aplikacja nie odpowiada (ANR). Zanim system wykryje błąd ANR i powiadomi o nim aplikację, operacja może się już zakończyć. Rozpoczęcie profilowania w tym momencie nie uwzględnia rzeczywistego blokowania.
Dokładne przewidywanie, kiedy wystąpią niektóre czynniki uruchamiające, jest niemożliwe, co uniemożliwia ręczne uruchomienie profilu z wyprzedzeniem.
Dlaczego warto korzystać z rejestrowania opartego na wyzwalaczach?
Głównym powodem używania wyzwalaczy profilowania jest rejestrowanie danych dotyczących nieprzewidywalnych zdarzeń, w przypadku których aplikacja nie może rozpocząć nagrywania ręcznie przed ich wystąpieniem. Aktywatory profilowania mogą służyć do:
- Debugowanie problemów z wydajnością: diagnozowanie błędów ANR, wycieków pamięci i innych problemów ze stabilnością.
- Optymalizuj główne ścieżki użytkownika: analizuj i ulepszaj ścieżki, np. uruchamianie aplikacji.
- Poznawanie zachowań użytkowników: uzyskuj statystyki dotyczące zdarzeń, np. zamykania aplikacji przez użytkowników.
Konfigurowanie aktywatora
Poniższy kod pokazuje, jak zarejestrować wywołanie TRIGGER_TYPE_APP_FULLY_DRAWN i zastosować do niego ograniczenie liczby wywołań.
Kotlin
fun recordWithTrigger() { val profilingManager = applicationContext.getSystemService(ProfilingManager::class.java) val triggers = ArrayList<ProfilingTrigger>() val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN) .setRateLimitingPeriodHours(1) triggers.add(triggerBuilder.build()) val mainExecutor: Executor = Executors.newSingleThreadExecutor() val resultCallback = Consumer<ProfilingResult> { profilingResult -> if (profilingResult.errorCode == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.resultFilePath ) setupProfileUploadWorker(profilingResult.resultFilePath) } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.errorCode + " errormsg=" + profilingResult.errorMessage ) } } profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback) profilingManager.addProfilingTriggers(triggers) }
Java
public void recordWithTrigger() { ProfilingManager profilingManager = getApplicationContext().getSystemService( ProfilingManager.class); List<ProfilingTrigger> triggers = new ArrayList<>(); ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder( ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); triggerBuilder.setRateLimitingPeriodHours(1); triggers.add(triggerBuilder.build()); Executor mainExecutor = Executors.newSingleThreadExecutor(); Consumer<ProfilingResult> resultCallback = new Consumer<ProfilingResult>() { @Override public void accept(ProfilingResult profilingResult) { if (profilingResult.getErrorCode() == ProfilingResult.ERROR_NONE) { Log.d( "ProfileTest", "Received profiling result file=" + profilingResult.getResultFilePath()); setupProfileUploadWorker(profilingResult.getResultFilePath()); } else { Log.e( "ProfileTest", "Profiling failed errorcode=" + profilingResult.getErrorCode() + " errormsg=" + profilingResult.getErrorMessage()); } } }; profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback); profilingManager.addProfilingTriggers(triggers); }
Kod wykonuje te czynności:
- Get the manager (Pobierz menedżera): pobiera usługę
ProfilingManager. - Zdefiniuj aktywator: tworzy
ProfilingTriggerdlaTRIGGER_TYPE_APP_FULLY_DRAWN. To zdarzenie występuje, gdy aplikacja zgłosi, że zakończyła uruchamianie i jest interaktywna. - Ustawianie limitów częstotliwości: stosuje 1-godzinny limit częstotliwości do tego konkretnego wyzwalacza (
setRateLimitingPeriodHours(1)). Zapobiega to rejestrowaniu przez aplikację więcej niż 1 profilu uruchamiania na godzinę. - Zarejestruj odbiorcę: wywołuje
registerForAllProfilingResults, aby zdefiniować wywołanie zwrotne, które obsługuje wynik. To wywołanie zwrotne otrzymuje ścieżkę zapisanego profilu za pomocą parametrugetResultFilePath(). - Add triggers: rejestruje listę reguł w
ProfilingManagerza pomocąaddProfilingTriggers. - Uruchom zdarzenie: wywołuje funkcję
reportFullyDrawn(), która wysyła do systemu zdarzenieTRIGGER_TYPE_APP_FULLY_DRAWN, co powoduje uruchomienie zbierania profilu pod warunkiem, że w tle działa śledzenie systemowe i dostępny jest limit ogranicznika częstotliwości. Ten opcjonalny krok pokazuje pełny proces, ponieważ aplikacja musi wywołaćreportFullyDrawn()dla tego aktywatora.
Pobieranie śladu
System zapisuje profile oparte na wyzwalaczach w tym samym katalogu co inne profile. Nazwa pliku ze śladami wywołanymi ma format:
profile_trigger_<profile_type_code>_<datetime>.<profile-type-name>
Możesz pobrać plik za pomocą ADB. Aby na przykład pobrać ślad systemowy zarejestrowany za pomocą przykładowego kodu za pomocą ADB, możesz użyć tego polecenia:
adb pull /data/user/0/com.example.sampleapp/files/profiling/profile_trigger_1_2025-05-06-14-12-40.perfetto-trace
Szczegółowe informacje o wizualizacji tych śladów znajdziesz w artykule Pobieranie i analizowanie danych profilowania.
Jak działa śledzenie w tle
Aby rejestrować dane sprzed wywołania, system operacyjny okresowo uruchamia śledzenie w tle. Jeśli podczas aktywnego śledzenia w tle wystąpi wyzwalacz, na który zarejestrowana jest Twoja aplikacja, system zapisze profil śledzenia w katalogu aplikacji. Profil będzie zawierać informacje o zdarzeniach, które doprowadziły do wywołania.
Po zapisaniu profilu system powiadamia aplikację za pomocą wywołania zwrotnego podanego w registerForAllProfilingResults. To wywołanie zwrotne podaje ścieżkę do przechwyconego profilu, do którego można uzyskać dostęp, wywołując ProfilingResult#getResultFilePath().
Aby zmniejszyć wpływ na wydajność urządzenia i czas pracy baterii, system nie śledzi w tle w sposób ciągły. Zamiast tego używa metody próbkowania. System losowo rozpoczyna śledzenie w tle w określonym przedziale czasu (o minimalnym i maksymalnym czasie trwania). Losowe rozmieszczenie tych śladów zwiększa pokrycie wyzwalaczy.
Profile wywoływane przez system mają zdefiniowany przez system maksymalny rozmiar, więc używają bufora pierścieniowego. Gdy bufor jest pełny, nowe dane śledzenia zastępują najstarsze dane. Jak widać na rysunku 1, zarejestrowany ślad może nie obejmować całego czasu trwania nagrywania w tle, jeśli bufor się zapełni. Zamiast tego przedstawia on najnowszą aktywność prowadzącą do wywołania.
Wdrażanie ograniczeń liczby żądań w przypadku poszczególnych wywołań
Triggery o wysokiej częstotliwości mogą szybko wyczerpać limit ogranicznika szybkości aplikacji. Aby lepiej zrozumieć ogranicznik szybkości, zapoznaj się z artykułem Jak działa ogranicznik szybkości. Aby zapobiec wyczerpaniu limitu przez jeden typ wyzwalacza, możesz wdrożyć ograniczenie liczby wywołań dla poszczególnych wyzwalaczy.
ProfilingManager obsługuje ograniczenie liczby wywołań dla poszczególnych wyzwalaczy zdefiniowanych przez aplikację. Dzięki temu możesz dodać kolejną warstwę ograniczania przepustowości na podstawie czasu oprócz istniejącego ogranicznika szybkości. Użyj interfejsu setRateLimitingPeriodHours API, aby ustawić konkretny czas odstępu dla reguły. Po upływie okresu oczekiwania możesz ponownie wywołać to działanie.
Debugowanie aktywatorów lokalnie
Ślady w tle są uruchamiane w losowych momentach, więc lokalne debugowanie wyzwalaczy jest trudne. Aby wymusić śledzenie w tle na potrzeby testowania, użyj tego polecenia ADB:
adb shell device_config put profiling_testing system_triggered_profiling.testing_package_name <com.example.myapp>
To polecenie wymusza rozpoczęcie ciągłego śledzenia w tle w przypadku określonego pakietu, co umożliwia każdemu aktywatorowi zbieranie profilu, jeśli zezwala na to ogranicznik szybkości.
Możesz też włączyć inne opcje debugowania, np. wyłączyć ogranicznik szybkości podczas debugowania lokalnego. Więcej informacji znajdziesz w artykule Polecenia debugowania do profilowania lokalnego.