Mierz czas ładowania i renderowanie ekranu za pomocą narzędzia Firebase Performance Monitoring

1. Wstęp

Ostatnia aktualizacja: 2021-03-11

Dlaczego musimy mierzyć wydajność wyświetleń?

Widoki są kluczową częścią aplikacji na Androida, która bezpośrednio wpływa na wygodę użytkownika. Na przykład Twoje działanie lub fragment zawiera interfejs użytkownika, w którym znajdują się komponenty widoku, z którymi użytkownicy wchodzą w interakcję. Użytkownicy nie są w stanie zobaczyć całej zawartości interfejsu użytkownika, dopóki nie zostanie ona w całości narysowana na ekranie. Powolne i zawieszone ekrany bezpośrednio utrudniają interakcję użytkownika z aplikacją i powodują złe doświadczenia użytkownika.

Czy usługa Firebase Performance Monitoring nie zapewnia tych wskaźników wydajności od razu po wyjęciu z pudełka?

Usługa Firebase Performance Monitoring automatycznie rejestruje niektóre dane dotyczące wydajności, takie jak czas rozpoczęcia aplikacji (tj. czas ładowania tylko pierwszego działania) i wydajność renderowania ekranu (tj. wolne i zamrożone klatki dla działań, ale nie dla Paprochy). Jednak aplikacje branżowe zwykle nie mają wielu działań, ale raczej jedno działanie i wiele fragmentów. Ponadto wiele aplikacji zwykle implementuje własne widoki niestandardowe dla bardziej złożonych przypadków użycia. Dlatego często przydatne jest zrozumienie, jak mierzyć czas ładowania i wydajność renderowania ekranu zarówno działań, jak i fragmentów, poprzez instrumentację niestandardowych śladów kodu w aplikacji. Możesz łatwo rozszerzyć to ćwiczenie z programowania, aby zmierzyć wydajność komponentów widoku niestandardowego.

Czego się dowiesz

  • Jak dodać monitorowanie wydajności Firebase do aplikacji na Androida
  • Zrozumienie ładowania działania lub fragmentu
  • Jak oprzyrządować ślady niestandardowego kodu, aby zmierzyć czas ładowania działania lub fragmentu
  • Zrozumienie renderowania ekranu i czym jest wolna/zamrożona klatka
  • Jak instrumentować ślady niestandardowego kodu za pomocą metryk w celu rejestrowania wolnych/zamrożonych ekranów
  • Jak wyświetlić zebrane metryki w konsoli Firebase

Co będziesz potrzebował

  • Android Studio 4.0 lub nowszy
  • Urządzenie/emulator Androida
  • Wersja Java 8 lub wyższa

2. Przygotowanie do pracy

Zdobądź kod

Uruchom następujące polecenia, aby sklonować przykładowy kod dla tego ćwiczenia z programowania. Spowoduje to utworzenie na twoim komputerze folderu o nazwie codelab-measure-android-view-performance :

$ git clone https://github.com/FirebaseExtended/codelab-measure-android-view-performance.git
$ cd codelab-measure-android-view-performance

Jeśli nie masz gita na swoim komputerze, możesz także pobrać kod bezpośrednio z GitHuba.

Zaimportuj projekt measure-view-performance-start do Android Studio. Prawdopodobnie zobaczysz błędy kompilacji lub ostrzeżenie o brakującym pliku google-services.json . Poprawimy to w następnej sekcji tego kroku.

Podczas tych zajęć z programowania użyjemy wtyczki Firebase Assistant , aby zarejestrować naszą aplikację na Androida w projekcie Firebase i dodać niezbędne pliki konfiguracyjne Firebase, wtyczki i zależności do naszego projektu na Androida — wszystko z poziomu Android Studio !

Połącz swoją aplikację z Firebase

  1. Przejdź do Android Studio / Pomoc > Sprawdź aktualizacje , aby upewnić się, że korzystasz z najnowszych wersji Android Studio i Asystenta Firebase.
  2. Wybierz Narzędzia > Firebase , aby otworzyć panel Asystenta .

e791bed0999db1e0.png

  1. Wybierz opcję Monitorowanie wydajności , którą chcesz dodać do swojej aplikacji, a następnie kliknij opcję Rozpocznij pracę z monitorowaniem wydajności .
  2. Kliknij Połącz z Firebase , aby połączyć swój projekt na Androida z Firebase (spowoduje to otwarcie konsoli Firebase w przeglądarce) .
  3. W konsoli Firebase kliknij Dodaj projekt , a następnie wprowadź nazwę projektu Firebase (jeśli masz już projekt Firebase, możesz zamiast tego wybrać ten istniejący projekt) . Kliknij Kontynuuj i zaakceptuj warunki, aby utworzyć projekt Firebase i nową aplikację Firebase.
  4. Następnie powinieneś zobaczyć okno dialogowe umożliwiające połączenie nowej aplikacji Firebase z projektem Android Studio.

42c498d28ead2b77.png

  1. Wracając do Android Studio, w panelu Asystenta powinno pojawić się potwierdzenie, że Twoja aplikacja jest połączona z Firebase.

dda8bdd9488167a0.png

Dodaj monitorowanie wydajności do swojej aplikacji

W panelu Asystenta w Android Studio kliknij opcję Dodaj monitorowanie wydajności do swojej aplikacji .

Powinieneś zobaczyć okno dialogowe Akceptuj zmiany, po którym Android Studio powinno zsynchronizować aplikację, aby upewnić się, że dodano wszystkie niezbędne zależności.

9b58145acc4be030.png

Na koniec w panelu Asystenta w Android Studio powinien pojawić się komunikat o powodzeniu, że wszystkie zależności są poprawnie skonfigurowane.

aa0d46fc944e0c0b.png

Jako dodatkowy krok włącz rejestrowanie debugowania , postępując zgodnie z instrukcjami w kroku „(Opcjonalnie) Włącz rejestrowanie debugowania”. Te same instrukcje są również dostępne w dokumentacji publicznej .

3. Uruchom aplikację

Jeśli pomyślnie zintegrowałeś aplikację z zestawem SDK do monitorowania wydajności, projekt powinien się teraz skompilować. W Android Studio kliknij Uruchom > Uruchom „aplikację” , aby skompilować i uruchomić aplikację na podłączonym urządzeniu/emulatorze z Androidem.

Aplikacja ma dwa przyciski, które przenoszą Cię do odpowiedniego działania i fragmentu, na przykład:

410d8686b4f45c33.png

W kolejnych krokach tego ćwiczenia z programowania dowiesz się, jak mierzyć czas ładowania i wydajność renderowania ekranu działania lub fragmentu.

4. Zrozumienie ładowania Aktywności lub Fragmentu

W tym kroku dowiemy się, co robi system podczas ładowania Aktywności lub Fragmentu.

Zrozumienie ładowania działania

W przypadku działania czas ładowania definiuje się jako czas od momentu utworzenia obiektu działania aż do całkowitego narysowania pierwszej klatki na ekranie ( w tym czasie użytkownik po raz pierwszy zobaczy pełny interfejs użytkownika działania czas ). Aby zmierzyć, czy aplikacja jest w pełni narysowana, możesz użyć metody reportFullyDrawn() w celu zmierzenia czasu, jaki upłynął między uruchomieniem aplikacji a pełnym wyświetleniem wszystkich zasobów i hierarchii widoków.

Na wysokim poziomie, gdy aplikacja wywołuje startActivity(Intent) , system automatycznie wykonuje następujące procesy. Zakończenie każdego procesu wymaga czasu, co wydłuża czas między utworzeniem działania a momentem, w którym użytkownik widzi interfejs działania działania na swoim ekranie.

c20d14b151549937.png

Zrozumienie ładowania fragmentu

Podobnie jak w przypadku Aktywności, czas ładowania Fragmentu definiuje się jako czas rozpoczynający się od momentu dołączenia Fragmentu do działania hosta aż do całkowitego narysowania na ekranie pierwszej klatki widoku fragmentu.

5. Zmierz czas ładowania działania

Opóźnienia w pierwszej klatce mogą powodować złe doświadczenia użytkownika, dlatego ważne jest, aby zrozumieć, jak duże początkowe opóźnienie ładowania doświadczają użytkownicy. Możesz przygotować niestandardowy kod śledzenia , aby zmierzyć ten czas ładowania:

  1. Rozpocznij śledzenie kodu niestandardowego (o nazwie TestActivity-LoadTime ) w klasie Activity zaraz po utworzeniu obiektu Activity.

TestActivity.java

public class TestActivity extends AppCompatActivity {   
    // TODO (1): Start trace recording as soon as the Activity object is created.
    private final Trace viewLoadTrace = FirebasePerformance.startTrace("TestActivity-LoadTime");

    // ...

}
  1. Zastąp wywołanie zwrotne onCreate() i pobierz widok dodany za pomocą metody setContentView() .
@Override     
public void onCreate(Bundle savedInstanceState) {    
    super.onCreate(savedInstanceState);          

    // Current Activity's main View (as defined in the layout xml file) is inflated after this            
    setContentView(R.layout.activity_test);          

    // ...

    // TODO (2): Get the View added by Activity's setContentView() method.         
    View mainView = findViewById(android.R.id.content);     

    // ...
}
  1. Dołączyliśmy implementację FistDrawListener , która ma dwa wywołania zwrotne: onDrawingStart() i onDrawingFinish() (więcej szczegółów na temat FirstDrawListener i tego, co może wpłynąć na jego wydajność, można znaleźć w następnej sekcji poniżej) . Zarejestruj element FirstDrawListener na końcu wywołania zwrotnego onCreate() działania. Powinieneś zatrzymać viewLoadTrace w wywołaniu zwrotnym onDrawingFinish() .

TestActivity.java

    // TODO (3): Register the callback to listen for first frame rendering (see
    //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when View drawing is
    //  finished.
    FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {              
        @Override             
        public void onDrawingStart() {       
          // In practice you can also record this event separately
        }

        @Override             
        public void onDrawingFinish() {
            // This is when the Activity UI is completely drawn on the screen
            viewLoadTrace.stop();             
        }         
    });
  1. Uruchom aplikację ponownie. Następnie przefiltruj logcat za pomocą „ Metryki śledzenia rejestrowania ”. Naciśnij przycisk LOAD ACTIVITY i poszukaj dzienników jak poniżej:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

🎉 Gratulacje! Pomyślnie zmierzyłeś czas ładowania działania i zgłosiłeś te dane do monitorowania wydajności Firebase. Zarejestrowane dane zobaczymy w konsoli Firebase w dalszej części tego ćwiczenia z programowania.

Cel FirstDrawListener

W sekcji powyżej zarejestrowaliśmy FirstDrawListener . Celem FirstDrawListener jest pomiar, kiedy pierwsza klatka rozpoczęła się i zakończyła rysowanie.

Implementuje metodę ViewTreeObserver.OnDrawListener i zastępuje wywołanie zwrotne onDraw() , które jest wywoływane, gdy drzewo widoków ma zostać narysowane. Następnie zawija wynik, udostępniając dwa wywołania zwrotne narzędzi onDrawingStart() i onDrawingFinish() .

Kompletny kod FirstDrawListener można znaleźć w kodzie źródłowym tego laboratorium programistycznego .

6. Zmierz czas ładowania fragmentu

Pomiar czasu ładowania fragmentu jest podobny do sposobu, w jaki mierzymy go dla działania, ale z pewnymi drobnymi różnicami. Ponownie użyjemy niestandardowego śledzenia kodu :

  1. Zastąp wywołanie zwrotne onAttach() i rozpocznij nagrywanie fragmentLoadTrace . Nazwiemy ten ślad Test-Fragment-LoadTime .

Jak wyjaśniono we wcześniejszym kroku, obiekt Fragment można utworzyć w dowolnym momencie, ale staje się on aktywny dopiero po dołączeniu do działania hosta.

TestFragment.java

public class TestFragment extends Fragment {

   // TODO (1): Declare the Trace variable.
   private Trace fragmentLoadTrace;

   @Override
   public void onAttach(@NonNull Context context) {
       super.onAttach(context);

       // TODO (2): Start trace recording as soon as the Fragment is attached to its host Activity.
       fragmentLoadTrace = FirebasePerformance.startTrace("TestFragment-LoadTime");
   }
  1. Zarejestruj FirstDrawListener w wywołaniu zwrotnym onViewCreated() . Następnie, podobnie jak w przykładzie działania, zatrzymaj śledzenie w onDrawingFinish() .

TestFragment.java

@Override
public void onViewCreated(@NonNull View mainView, Bundle savedInstanceState) {
   super.onViewCreated(mainView, savedInstanceState);

   // ...

   // TODO (3): Register the callback to listen for first frame rendering (see
   //  "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when view drawing is
   //  finished.
   FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {

       @Override
       public void onDrawingStart() {
           // In practice you can also record this event separately
       }

       @Override
       public void onDrawingFinish() {
           // This is when the Fragment UI is completely drawn on the screen
           fragmentLoadTrace.stop();
       }
   });
  1. Uruchom aplikację ponownie. Następnie przefiltruj logcat za pomocą „ Metryki śledzenia rejestrowania ”. Naciśnij przycisk LOAD FRAGMENT i poszukaj dzienników jak poniżej:
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)

🎉 Gratulacje! Pomyślnie zmierzyłeś czas ładowania fragmentu i zgłosiłeś te dane do monitorowania wydajności Firebase. Zarejestrowane dane zobaczymy w konsoli Firebase w dalszej części tego ćwiczenia z programowania.

7. Zrozumienie renderowania ekranu i czym jest wolna/zamrożona klatka

Renderowanie interfejsu użytkownika polega na generowaniu ramki z aplikacji i wyświetlaniu jej na ekranie. Aby mieć pewność, że interakcja użytkownika z aplikacją będzie płynna, aplikacja powinna renderować klatki w czasie krótszym niż 16 ms, aby uzyskać 60 klatek na sekundę ( dlaczego 60 klatek na sekundę? ). Jeśli w Twojej aplikacji występuje powolne renderowanie interfejsu użytkownika, system jest zmuszony pomijać klatki, a użytkownik odczuje zacinanie się w aplikacji. Nazywamy tego Jankiem .

Podobnie zamrożone klatki to klatki interfejsu użytkownika, których renderowanie trwa dłużej niż 700 ms. To opóźnienie stanowi problem, ponieważ aplikacja wygląda na zablokowaną i nie reaguje na działania użytkownika przez prawie pełną sekundę podczas renderowania klatki.

8. Zmierz wolne/zamrożone klatki fragmentu

Monitorowanie wydajności Firebase automatycznie przechwytuje wolne/zamrożone klatki dla działania ( ale tylko wtedy, gdy jest przyspieszane sprzętowo ). Jednak ta funkcja nie jest obecnie dostępna dla fragmentów. Powolne/zamrożone ramki Fragmentu definiuje się jako wolne/zablokowane klatki dla całego Działania pomiędzy wywołaniami zwrotnymi onFragmentAttached() i onFragmentDetached() w cyklu życia Fragmentu.

Czerpiąc motywację z klasy AppStateMonitor ( która jest częścią zestawu SDK Performance Monitoring odpowiedzialnego za rejestrowanie śladów ekranu dla Activity ), zaimplementowaliśmy klasę ScreenTrace ( która jest częścią tego repozytorium kodu źródłowego codelab ). Klasę ScreenTrace można podłączyć do wywołania zwrotnego cyklu życia FragmentManager działania w celu przechwytywania wolnych/zamrożonych klatek. Ta klasa udostępnia dwa publiczne interfejsy API:

  • recordScreenTrace() : Rozpoczyna nagrywanie śladu ekranu
  • sendScreenTrace() : Zatrzymuje rejestrowanie śladu ekranu i dołącza niestandardowe metryki do rejestrowania liczby klatek ogółem, wolnych i zamrożonych

Dołączając te niestandardowe metryki, ślady ekranu dla Fragmentów można obsługiwać w taki sam sposób, jak ślady ekranu dla Działania i można je wyświetlać wraz z innymi śladami renderowania ekranu w panelu wydajności konsoli Firebase.

Oto jak rejestrować ślady ekranu dla swojego Fragmentu:

  1. Zainicjuj klasę ScreenTrace w swoim działaniu, która hostuje fragment.

MainActivity.java

// Declare the Fragment tag
private static final String FRAGMENT_TAG = TestFragment.class.getSimpleName();

// TODO (1): Declare the ScreenTrace variable.
private ScreenTrace screenTrace;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // TODO (2): Initialize the ScreenTrace variable.
    screenTrace = new ScreenTrace(this, FRAGMENT_TAG);

    // ...
}
  1. Po załadowaniu fragmentu zarejestruj się w FragmentLifecycleCallbacks i zastąp wywołania zwrotne onFragmentAttached() i onFragmentDetached() . Zrobiliśmy to dla Ciebie. Musisz rozpocząć nagrywanie śladów ekranu w wywołaniu zwrotnym onFragmentAttached() i zatrzymać nagrywanie w wywołaniu zwrotnym onFragmentDetached() .

MainActivity.java

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
       new FragmentManager.FragmentLifecycleCallbacks() {

           @Override
           public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
               super.onFragmentAttached(fm, f, context);

               // TODO (3): Start recording the screen traces as soon as the Fragment is
               //  attached to its host Activity.
               if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
                   screenTrace.recordScreenTrace();
               }
           }

           @Override
           public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {
               super.onFragmentDetached(fm, f);

               // TODO (4): Stop recording the screen traces as soon as the Fragment is
               //  detached from its host Activity.
               if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
                   screenTrace.sendScreenTrace();
               }

               // Unregister Fragment lifecycle callbacks after the Fragment is detached
               fm.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks);
           }
       };
  1. Uruchom aplikację ponownie. Następnie naciśnij przycisk LOAD FRAGMENT . Poczekaj kilka sekund, a następnie kliknij back button na dolnym pasku nawigacyjnym.

Przefiltruj logcat za pomocą „ Metryki śledzenia rejestrowania ”, a następnie poszukaj dzienników jak poniżej:

I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms)

Przefiltruj logcat za pomocą „ FireperfViews ”, a następnie poszukaj dzienników jak poniżej:

D/FireperfViews: sendScreenTrace MainActivity-TestFragment, name: _st_MainActivity-TestFragment, total_frames: XX, slow_frames: XX, frozen_frames: XX

🎉 Gratulacje! Pomyślnie zmierzyłeś wolne/zamrożone ramki dla fragmentu i zgłosiłeś te dane do monitorowania wydajności Firebase. Zarejestrowane metryki zobaczymy w dalszej części tego ćwiczenia z programowania w konsoli Firebase.

9. Sprawdź metryki w konsoli Firebase

  1. W logcat kliknij adres URL konsoli Firebase, aby odwiedzić stronę szczegółów śledzenia. ceb9d5ba51bb6e89.jpeg

Alternatywnie w konsoli Firebase wybierz projekt zawierający Twoją aplikację. W lewym panelu znajdź sekcję Wydanie i monitorowanie , a następnie kliknij Wydajność .

  • Na głównej karcie Pulpit nawigacyjny przewiń w dół do tabeli śladów, a następnie kliknij kartę Niestandardowe ślady . W tej tabeli zobaczysz niestandardowe ślady kodu, które dodaliśmy wcześniej, a także niektóre gotowe ślady , takie jak ślad _app_start .
  • Znajdź dwa niestandardowe ślady kodu, TestActivity-LoadTime i TestFragment-LoadTime . Kliknij Czas trwania dowolnego z nich, aby wyświetlić więcej szczegółów na temat zebranych danych.

a0d8455c5269a590.png

  1. Strona szczegółów śledzenia kodu niestandardowego zawiera informacje o czasie trwania śledzenia (tj. zmierzonym czasie ładowania).

5e92a307b7410d8b.png

  1. Możesz także wyświetlić dane dotyczące wydajności niestandardowego śledzenia ekranu.
  • Wróć do głównej karty Panelu sterowania , przewiń w dół do tabeli śladów, a następnie kliknij kartę Renderowanie ekranu . W tej tabeli zobaczysz niestandardowe ślady ekranu, które dodaliśmy wcześniej, a także wszelkie gotowe ślady ekranu , takie jak ślad MainActivity .
  • Znajdź niestandardowy ślad ekranu, MainActivity-TestFragment . Kliknij nazwę śledzenia, aby wyświetlić zagregowane dane dotyczące powolnego renderowania i zablokowanych klatek.

ee7890c7e2c28740.png

10. Gratulacje

Gratulacje! Pomyślnie zmierzyłeś czas ładowania i wydajność renderowania ekranu działania i fragmentu, korzystając z monitorowania wydajności Firebase!

Co osiągnąłeś

Co dalej

Wydajność Firebase zapewnia więcej sposobów pomiaru wydajności aplikacji niż śledzenie niestandardowe. Automatycznie mierzy czas uruchamiania aplikacji, dane dotyczące wydajności aplikacji na pierwszym planie i aplikacji w tle . Czas sprawdzić te dane w konsoli Firebase .

Ponadto Firebase Performance oferuje automatyczne monitorowanie żądań sieciowych HTTP/S . Dzięki temu możesz łatwo instrumentować żądania sieciowe bez pisania ani jednej linii kodu. Czy możesz spróbować wysłać kilka żądań sieciowych ze swojej aplikacji i znaleźć dane w konsoli Firebase ?

Premia

Teraz, gdy wiesz, jak mierzyć czas ładowania i wydajność renderowania ekranu działania/fragmentu za pomocą niestandardowych śladów kodu, możesz zapoznać się z naszą bazą kodu open source , aby sprawdzić, czy możesz od razu przechwycić te metryki dla dowolnego działania/fragmentu to część aplikacji? Jeśli chcesz, wyślij PW :-)

11. Nauka bonusowa

Zrozumienie tego, co dzieje się podczas ładowania działania, pomoże Ci lepiej zrozumieć charakterystykę wydajności Twojej aplikacji. We wcześniejszym kroku szczegółowo opisaliśmy, co dzieje się podczas ładowania działania, ale poniższy diagram opisuje każdą fazę znacznie bardziej szczegółowo.

cd61c1495fad7961.png