Profilowanie oparte na aktywatorach

Na Androidzie 16 (poziom API 36) i nowszych ProfilingManager umożliwia automatyczne przechwytywanie profili w odpowiedzi na aktywatory systemowe, takie jak błędy Aplikacja nie odpowiada (ANR), awarie spowodowane brakiem pamięci (OutOfMemoryError) lub zdarzenia uruchomienia aplikacji. Ponieważ nie można przewidzieć, kiedy wiele z tych zdarzeń wystąpi w terenie, ręczne rozpoczęcie nagrywania z wyprzedzeniem jest niepraktyczne. W przypadku profilowania opartego na aktywatorach system zarządza przechwytywaniem i dostarcza wynikowy profil do aplikacji, dzięki czemu możesz:

  • Debugowanie problemów ze stabilnością: diagnozuj nieprzewidywalne błędy ANR i wycieki pamięci.
  • Optymalizowanie głównych ścieżek użytkownika (CUJ): analizuj procesy takie jak uruchamianie aplikacji (TRIGGER_TYPE_COLD_START i TRIGGER_TYPE_APP_FULLY_DRAWN).
  • Poznawanie zachowań użytkowników: możesz sprawdzać zdarzenia, takie jak zamknięcie aplikacji przez użytkownika.

Pełną listę dostępnych aktywatorów znajdziesz w ProfilingTrigger.

Rodzaje reguł

Reguły różnią się sposobem, w jaki system rejestruje profil po wystąpieniu zdarzenia.

Reguły, które używają śledzenia w tle

Wiele zdarzeń aktywujących, takich jak błąd ANR, jest konsekwencją wcześniejszego problemu. Rozpoczęcie śledzenia dopiero po wykryciu błędu ANR przez system nie pozwala na wykrycie blokującej operacji, która go spowodowała. Aby zarejestrować aktywność prowadzącą do zdarzenia, te aktywatory zapisują zrzut aktywnego śledzenia w tle systemu. Jeśli w momencie wystąpienia zdarzenia nie jest uruchomione żadne śledzenie w tle, profil nie jest zbierany:

Aktywatory, które rozpoczynają nowe przechwytywanie

Te aktywatory nie zależą od śledzenia w tle. Zamiast tego system rozpoczyna nowe przechwytywanie profilu, gdy wystąpi zdarzenie:

Aktywatory, w przypadku których zachowanie podczas przechwytywania różni się w zależności od anomalii

W przypadku tych aktywatorów anomalii typ przechwyconego profilu (zapisanie zrzutu aktywnego śladu w tle lub rozpoczęcie nowego przechwytywania) i zachowanie związane z ograniczaniem liczby żądań zależą od konkretnej anomalii wykrytej przez system:

Konfigurowanie aktywatora

Poniższy kod pokazuje, jak zarejestrować aktywator TRIGGER_TYPE_APP_FULLY_DRAWN i ustawić dla niego okres oczekiwania.

Kotlin

@RequiresApi(Build.VERSION_CODES.BAKLAVA)
fun recordWithTrigger() {
    val profilingManager =
        applicationContext.getSystemService(ProfilingManager::class.java) ?: return

    val triggers = ArrayList<ProfilingTrigger>()

    val triggerBuilder = ProfilingTrigger.Builder(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN)
        .setRateLimitingPeriodHours(1)

    triggers.add(triggerBuilder.build())

    val callbackExecutor: 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(callbackExecutor, resultCallback)
    profilingManager.addProfilingTriggers(triggers)

    Choreographer.getInstance().postFrameCallback { frameTimeNanos ->
        // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted.
        reportFullyDrawn()
    }
}

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 callbackExecutor = 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(callbackExecutor, resultCallback);
  profilingManager.addProfilingTriggers(triggers);

  Choreographer.getInstance().postFrameCallback((f) -> {
    // This will cause the TRIGGER_TYPE_APP_FULLY_DRAWN to be emitted.
    reportFullyDrawn();
  });
}

Kod wykonuje te czynności:

  1. Pobierz menedżera: pobiera usługę ProfilingManager.
  2. Zdefiniuj aktywator: tworzy ProfilingTrigger dla TRIGGER_TYPE_APP_FULLY_DRAWN. To zdarzenie występuje, gdy aplikacja wywołuje funkcję reportFullyDrawn() podczas uruchamiania na zimno zainicjowanego przez aktywność, aby zgłosić, że zakończyła uruchamianie i jest interaktywna.
  3. Ustaw okres oczekiwania reguły: stosuje 1-godzinny okres oczekiwania do tej konkretnej reguły za pomocą setRateLimitingPeriodHours oprócz systemowego ogranicznika liczby żądań. Ten okres oczekiwania uniemożliwia aplikacji rejestrowanie więcej niż 1 profilu uruchamiania na godzinę.
  4. Zarejestruj detektor: wywołuje registerForAllProfilingResults, aby zdefiniować wywołanie zwrotne, które obsługuje wynik. To wywołanie zwrotne otrzymuje ścieżkę zapisanego profilu przez getResultFilePath().
  5. Dodaj aktywatory: rejestruje listę aktywatorów w ProfilingManager za pomocą addProfilingTriggers.
  6. Uruchom zdarzenie: publikuje wywołanie zwrotne ramki Choreographer, które wywołuje reportFullyDrawn() po narysowaniu następnej ramki. Powoduje to wysłanie zdarzenia TRIGGER_TYPE_APP_FULLY_DRAWN do systemu i uruchomienie zbierania profili, zakładając, że ślad systemowy w tle był uruchomiony i dostępny jest limit ogranicznika liczby żądań. Aby ten aktywator działał, upewnij się, że aplikacja wywołuje funkcję reportFullyDrawn().

Cykl życia aktywatora

Zarejestrowane reguły są zachowywane po ponownym uruchomieniu aplikacji i systemu. Nie musisz ponownie rejestrować aktywatorów przy każdym uruchomieniu aplikacji ani po ponownym uruchomieniu systemu – gdy zarejestrujesz aktywatory za pomocą addProfilingTriggers, system będzie je automatycznie utrzymywać dla Twojej aplikacji, dopóki ich nie usuniesz lub nie odinstalujesz aplikacji.

Kiedy wyrejestrować reguły

Zarejestrowane reguły są zachowywane po ponownym uruchomieniu aplikacji i systemu, więc pominięcie metody addProfilingTriggers przy kolejnych uruchomieniach nie powoduje zatrzymania reguł zarejestrowanych w poprzedniej sesji. Aby zatrzymać rejestrowanie profili dla konkretnych zdarzeń lub wszystkich zdarzeń, wywołaj bezpośrednio removeProfilingTriggersByType lub clearProfilingTriggers.

Wyrejestrowanie aktywuje się w tych sytuacjach:

  • Kierowanie na kohorty lub wycofywanie flag zdalnych: możesz profilować segmenty w kohortach za pomocą zdalnej konfiguracji lub flag testów A/B. Za każdym razem, gdy urządzenie opuszcza kohortę próby lub flaga jest wyłączona, wyrejestruj aktywatory.
  • Zakończenie kampanii debugowania: gdy zbierzesz wystarczającą liczbę profili, aby zdiagnozować problem lub wprowadzić poprawkę, usuń aktywator. Wyrejestrowanie niepotrzebnych aktywatorów pozwala zachować pamięć urządzenia i limit liczby żądań aplikacji na potrzeby innych żądań profilowania.

Cykl życia wywołania zwrotnego

W przeciwieństwie do bezpośrednich żądań profilowania profile oparte na aktywatorach dostarczają wyniki tylko do globalnego detektora zarejestrowanego w registerForAllProfilingResults. W wywołaniu zwrotnym wywołaj ProfilingResult.getTriggerType, aby określić, który aktywator zainicjował przechwytywanie, i wywołaj ProfilingResult.getResultFilePath, aby zlokalizować zapisany plik profilu. Szczegółowe informacje o nazwach plików profili aktywacji, pobieraniu plików za pomocą adb i przesyłaniu wyników w tle znajdziesz w artykule Pobieranie i przesyłanie profili.

Chociaż aktywatory są zachowywane po ponownym uruchomieniu aplikacji i systemu, wywołanie zwrotne wyniku nie jest zachowywane. Zarejestruj wywołanie zwrotne w tle Executor po zakończeniu uruchamiania aplikacji (aby nie spowalniać uruchamiania na zimno), aby otrzymywać profile w obu tych sytuacjach:

  • Gdy aplikacja jest uruchomiona: w przypadku aktywatorów, które nie zatrzymują aplikacji ani nie powodują jej awarii, system wywołuje wywołanie zwrotne w podanym obiekcie Executor, gdy aktywator zostanie wywołany, a profil zostanie zapisany.
  • Po zamknięciu aplikacji: w przypadku aktywatorów, które często obejmują zamknięcie aplikacji lub je poprzedzają (np. TRIGGER_TYPE_ANR, TRIGGER_TYPE_OOM lub TRIGGER_TYPE_KILL_RECENTS), lub jeśli aplikacja zatrzyma się przed zakończeniem przechwytywania, system zapisuje profil w tle i kolejkuje wynik po ponownym uruchomieniu aplikacji i systemu. System przechowuje niedostarczone wyniki w kolejce przez maksymalnie 7 dni i dostarcza je przy następnym uruchomieniu aplikacji i wywołaniu registerForAllProfilingResults.

Jak działa śledzenie w tle

Aby zachować równowagę między obciążeniem związanym z profilowaniem a przechwytywaniem przydatnych artefaktów, system nie uruchamia śladów w tle w sposób ciągły. Zamiast tego okresowo pobiera próbki, uruchamiając ślady w tle w losowych odstępach czasu.

Ślady w tle wywoływane przez system używają bufora pierścieniowego o maksymalnym rozmiarze zdefiniowanym przez system. Gdy bufor się zapełni, nowe dane logu zastąpią najstarsze dane. Jak pokazano na rysunku 1, gdy zarejestrowany aktywator uruchamia się, gdy ślad w tle jest aktywny i dostępny jest limit ograniczenia liczby żądań, system zapisuje zrzut bufora pierścieniowego zawierający najnowszą aktywność prowadzącą do aktywatora.

Diagram pokazujący, jak działają zrzuty logów czasu w tle, z buforem pierścieniowym przechwytującym dane przed zdarzeniem wyzwalającym.
Rysunek 1. Jak działają zrzuty śladów w tle.

Ustawianie czasu oczekiwania dla konkretnego aktywatora

Reguły o wysokiej częstotliwości mogą szybko wyczerpać limit ograniczenia liczby żądań aplikacji. Aby zapobiec wyczerpaniu limitu przez pojedynczy typ aktywatora, użyj setRateLimitingPeriodHours, aby dodać minimalny okres oczekiwania na aktywator oprócz systemowego ogranicznika liczby żądań.

Debugowanie aktywatorów lokalnie

Ślady w tle są uruchamiane w środowisku produkcyjnym w losowych odstępach czasu, dlatego testowanie aktywatorów lokalnie wymaga wymuszenia ciągłego śledzenia w tle i ominięcia ograniczenia liczby żądań na poziomie systemu dla pakietu (nadal obowiązuje czas oczekiwania na aktywator ustawiony za pomocą setRateLimitingPeriodHours, a rate_limiter.disabled dotyczy tylko wywołań requestProfiling opartych na aplikacji).

Aby dowiedzieć się, jak za pomocą poleceń adb wymusić ślady w tle, symulować aktywatory z wiersza poleceń i sprawdzać stan usługi, zapoznaj się z artykułem Wymuszanie włączenia śladów w tle na potrzeby testowania aktywatorów w sekcji Polecenia debugowania na potrzeby lokalnego profilowania.