Mierz czas wczytywania i renderowanie ekranu za pomocą Monitorowania wydajności Firebase

1. Wprowadzenie

Ostatnia aktualizacja: 11.03.2021

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

Widoki to kluczowy element aplikacji na Androida, który bezpośrednio wpływa na wrażenia użytkownika. Na przykład aktywność lub fragment zawiera interfejs użytkownika zawierający komponenty widoku danych, z którymi użytkownicy wchodzą w interakcje. Użytkownicy nie mogą zobaczyć całej zawartości interfejsu, dopóki nie zostanie on w całości narysowany na ekranie. Powolne i zablokowane ekrany bezpośrednio utrudniają użytkownikom korzystanie z aplikacji i pogarszają ich wrażenia.

Czy Monitorowanie wydajności Firebase nie udostępnia tych danych od razu?

Monitorowanie wydajności Firebase automatycznie rejestruje od razu niektóre dane o wydajności, takie jak czas uruchamiania aplikacji (tj. czas wczytywania tylko w przypadku pierwszej aktywności) oraz wydajność renderowania ekranu (tzn. spowolnione i zablokowane klatki w przypadku aktywności, ale nie fragmenty). Jednak aplikacje branżowe zwykle nie zawierają wielu aktywności, tylko 1 aktywność i wiele fragmentów. Poza tym wiele aplikacji zwykle implementuje własne widoki niestandardowe do bardziej złożonych zastosowań. Dlatego często warto się dowiedzieć, jak zmierzyć czas wczytywania i wydajność renderowania ekranu aktywności i fragmentów kodu, stosując w aplikacji niestandardowe logi czasu. Możesz łatwo rozszerzyć to ćwiczenia z programowania, aby mierzyć wydajność komponentów widoku niestandardowego.

Czego się nauczysz

  • Jak dodać Monitorowanie wydajności Firebase do aplikacji na Androida
  • Informacje o wczytywaniu aktywności lub fragmentu
  • Jak instrumentować niestandardowe logi czasu kodu do pomiaru czasu wczytywania aktywności lub fragmentu
  • Informacje o renderowaniu ekranu i tym, co to jest powolna lub zablokowana klatka
  • Jak instrumentować niestandardowe ślady kodu za pomocą wskaźników, aby rejestrować Powolne lub zablokowane ekrany
  • Wyświetlanie zebranych danych w konsoli Firebase

Czego potrzebujesz

  • Android Studio 4.0 lub nowszy
  • urządzenie z Androidem lub emulator,
  • Java w wersji 8 lub nowszej,

2. Przygotowanie

Pobierz kod

Aby skopiować przykładowy kod na potrzeby tego ćwiczenia z programowania, uruchom następujące polecenia. 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 git na swoim komputerze, możesz też 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. W następnej sekcji tego kroku poprawimy ten błąd.

W ramach tych ćwiczeń w programie wykorzystamy wtyczkę Asystent Firebase, aby zarejestrować naszą aplikację na Androida w projekcie Firebase i dodać niezbędne pliki konfiguracyjne Firebase, wtyczki oraz zależności do naszego projektu na Androida – wszystko to z poziomu Android Studio.

Łączenie aplikacji z Firebase

  1. Otwórz Android Studio lub Pomoc > Sprawdź dostępność aktualizacji, by mieć pewność, że używasz najnowszych wersji Android Studio i Asystenta Firebase.
  2. Wybierz Narzędzia > Firebase, aby otworzyć panel Asystenta.

E791bed0999db1e0.png

  1. Wybierz Monitorowanie wydajności, aby dodać je do aplikacji, a następnie kliknij Zacznij korzystać z monitorowania 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 potem wpisz nazwę projektu Firebase (jeśli masz już projekt Firebase, możesz zamiast niego wybrać ten istniejący projekt). Kliknij Dalej i zaakceptuj warunki, aby utworzyć projekt Firebase i nową aplikację Firebase.
  4. Zobaczysz okno dialogowe, w którym możesz połączyć nową aplikację Firebase z projektem Android Studio.

42c498d28ead2b77.png

  1. W Android Studio w panelu Asystent powinno pojawić się potwierdzenie połączenia aplikacji z Firebase.

dda8bdd9488167a0.png

Dodaj Monitorowanie wydajności do aplikacji

W panelu Asystent w Android Studio kliknij Dodaj do aplikacji monitorowanie wydajności.

Powinno się wyświetlić okno Zaakceptuj zmiany, po którym Android Studio powinien zsynchronizować aplikację, aby upewnić się, że zostały dodane wszystkie niezbędne zależności.

9b58145acc4be030.png

W panelu Asystent w Android Studio powinien wyświetlić się komunikat o powodzeniu informujący o prawidłowym skonfigurowaniu wszystkich zależności.

aa0d46fc944e0c0b.png

Dodatkowym krokiem jest włączenie rejestrowania debugowania, wykonując instrukcje podane w kroku „(Opcjonalnie) Włączanie rejestrowania debugowania”. Te same instrukcje są również dostępne w dokumentacji publicznej.

3. Uruchom aplikację

Jeśli udało Ci się zintegrować aplikację z pakietem SDK Performance Monitoring, projekt powinien się skompilować. W Android Studio kliknij Uruchom > Uruchom aplikację, aby skompilować i uruchomić aplikację na połączonym urządzeniu z Androidem lub emulatorze.

Aplikacja ma 2 przyciski, które prowadzą do odpowiedniej aktywności i fragmentu, np.:

410d8686b4f45c33.png

W kolejnych krokach tego ćwiczenia z programowania dowiesz się, jak mierzyć czas wczytywania i wydajność renderowania ekranu w przypadku aktywności lub fragmentu.

4. Informacje o wczytywaniu aktywności lub fragmentu

W tym kroku sprawdzimy, co robi system podczas wczytywania aktywności lub fragmentu.

Informacje o wczytywaniu aktywności

Czas wczytywania aktywności oznacza czas od momentu utworzenia obiektu Aktywność do momentu, gdy pierwsza klatka zostanie w pełni wyświetlona na ekranie (wtedy użytkownik po raz pierwszy zobaczy pełny interfejs aktywności). Aby sprawdzić, czy aplikacja jest w pełni rysowana, możesz użyć metody reportFullyDrawn() do pomiaru czasu, który upływa od uruchomienia aplikacji do pełnego wyświetlenia wszystkich zasobów i hierarchii widoków.

Ogólnie, gdy aplikacja wywołuje startActivity(Intent), system automatycznie wykonuje te procesy: Realizacja każdego z tych działań jest czasochłonna, co jest wydłużane o czas upływający między utworzeniem aktywności a momentem wyświetlenia przez użytkownika interfejsu działania na ekranie.

c20d14b151549937.png

Informacje o wczytywaniu fragmentu kodu

Podobnie jak w przypadku aktywności, czas wczytywania fragmentu definiowany jest jako czas od momentu dołączenia fragmentu do aktywności hosta aż do momentu, gdy pierwsza klatka dla widoku fragmentów zostanie zaznaczona na ekranie.

5. Mierz czas wczytywania aktywności

Opóźnienia w pierwszej ramce mogą negatywnie wpływać na wygodę użytkowników, dlatego warto wiedzieć, jak duże opóźnienie wczytywania początkowego mają użytkownicy. Możesz wykorzystać niestandardowy zrzut kodu, aby zmierzyć czas wczytywania:

  1. Rozpocznij niestandardowy ślad kodu (o nazwie TestActivity-LoadTime) w klasie Activity, gdy tylko zostanie utworzony obiekt 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 przez metodę 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. Uwzględniamy implementację funkcji FistDrawListener, która zawiera 2 wywołania zwrotne: onDrawingStart() i onDrawingFinish() (w następnej sekcji poniżej znajdziesz więcej informacji o parametrze FirstDrawListener oraz o tym, co może wpływać na jego skuteczność. Zarejestruj FirstDrawListener na końcu wywołania zwrotnego działania onCreate(). Należy zatrzymać działanie viewLoadTrace w onDrawingFinish()wywołaniu zwrotnym.

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ą filtra „Logging logcat” (Wskaźnik logu czasu). Kliknij przycisk LOAD ACTIVITY i poszukaj dzienników takich jak poniżej:
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)

🎉 Gratulacje! Udało Ci się zmierzyć czas wczytywania aktywności i przesłać dane do Monitorowania wydajności Firebase. Zarejestrowane dane zobaczymy w konsoli Firebase w dalszej części tego ćwiczenia z programowania.

Przeznaczenie FirstDrawListener

W sekcji powyżej zarejestrowaliśmy: FirstDrawListener. FirstDrawListener służy do określania, kiedy rozpoczęło się i zakończyło rysowanie pierwszej klatki.

Implementuje metodę ViewTreeObserver.OnDrawListener i zastępuje wywołanie zwrotne onDraw(), które jest wywoływane podczas rysowania drzewa widoków. Następnie opakowuje wynik, dostarczając 2 wywołania zwrotne narzędzia onDrawingStart() i onDrawingFinish().

Pełny kod FirstDrawListener znajdziesz w kodzie źródłowym tego ćwiczenia.

6. Zmierz czas wczytywania fragmentu

Pomiar czasu wczytywania fragmentu kodu jest podobny do pomiaru aktywności, ale występują pewne pewne różnice. Ponownie użyjemy śledzenia niestandardowego kodu:

  1. Zastąp wywołanie zwrotne onAttach() i zacznij rejestrować fragmentLoadTrace. Nadamy temu śladowi nazwę Test-Fragment-LoadTime.

Jak wyjaśniliśmy na poprzednim kroku, obiekt Fragment można utworzyć w dowolnym momencie, ale staje się on aktywny tylko wtedy, gdy jest połączony z aktywnością 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 onViewCreated()wywołaniu zwrotnym. Następnie, podobnie jak w przypadku aktywności, 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ą filtra „Logging logcat” (Wskaźnik logu czasu). Kliknij przycisk LOAD FRAGMENT i poszukaj dzienników takich jak poniżej:
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)

🎉 Gratulacje! Udało Ci się zmierzyć czas wczytywania fragmentu i przesłać dane do Monitorowania wydajności Firebase. Zarejestrowane dane zobaczymy w konsoli Firebase w dalszej części tego ćwiczenia z programowania.

7. Informacje o renderowaniu ekranu i tym, co to jest powolna lub zablokowana klatka

Renderowanie UI to generowanie ramki z aplikacji i wyświetlenie jej na ekranie. Aby zapewnić płynną interakcję użytkownika z aplikacją, aplikacja powinna renderować klatki w czasie poniżej 16 ms, aby osiągnąć 60 klatek na sekundę ( dlaczego 60 kl./s?). Jeśli interfejs Twojej aplikacji jest wolno renderowany, system jest zmuszony do pomijania klatek, przez co użytkownik zauważy zacinanie się. Nazywamy to jan.

Analogicznie zablokowane klatki to klatki interfejsu, których renderowanie trwa dłużej niż 700 ms. Opóźnienie jest problemem, ponieważ wygląda na to, że aplikacja się zawiesiła i przez prawie pełną sekundę podczas renderowania klatki aplikacja nie reaguje na dane wejściowe użytkownika.

8. Zmierz spowolnione/zamrożone klatki fragmentu

Monitorowanie wydajności Firebase automatycznie rejestruje spowolnione/zablokowane klatki w przypadku danej aktywności (ale tylko wtedy, gdy jest to przyspieszone sprzętowo). Jednak ta funkcja nie jest obecnie dostępna w przypadku fragmentów. Powolne/zablokowane klatki fragmentu kodu to spowolnione/zablokowane klatki w całej aktywności z wywołań zwrotnych onFragmentAttached() i onFragmentDetached() w cyklu życia fragmentu.

Aby wykorzystać motywację w klasie AppStateMonitor (która jest częścią pakietu SDK Performance Monitoring odpowiedzialnego za rejestrowanie zrzutów ekranu na potrzeby aktywności), wdrożyliśmy klasę ScreenTrace (która jest częścią tego repozytorium kodu źródłowego ćwiczeń z programowania). Klasę ScreenTrace można połączyć z wywołaniem zwrotnym cyklu życia FragmentManager aktywności, aby przechwytywać spowolnione lub zablokowane klatki. Ta klasa udostępnia 2 publiczne interfejsy API:

  • recordScreenTrace(): rozpoczyna rejestrowanie zrzutu ekranu
  • sendScreenTrace(): zatrzymuje rejestrowanie zrzutu ekranu i dołącza dane niestandardowe do rejestrowania liczby wszystkich, spowolnionych i zablokowanych klatek.

Dzięki dołączeniu tych niestandardowych danych zrzuty ekranu fragmentów kodu mogą być obsługiwane tak samo jak zrzuty ekranu aktywności i można je wyświetlać razem z innymi śladami renderowania ekranu w panelu Wydajność w konsoli Firebase.

Oto jak zarejestrować ślady ekranu dla fragmentu:

  1. Zainicjuj klasę ScreenTrace w aktywności, 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 wczytaniu fragmentu kodu zarejestruj się w usłudze FragmentLifecycleCallbacks i zastąp wywołania onFragmentAttached() oraz onFragmentDetached(). Zrobiliśmy to za Ciebie. Musisz rozpocząć rejestrowanie zrzutó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 kliknij przycisk LOAD FRAGMENT. Poczekaj kilka sekund i kliknij back button na dolnym pasku nawigacyjnym.

Przefiltruj dzienniki według parametru „Logging logcat” (Wskaźnik logu czasu), a potem wyszukaj logi podobne do tego:

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

Ustaw w logcat filtr „FireperfViews”, a potem wyszukaj logi podobne do tego:

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

🎉 Gratulacje! Udało Ci się zmierzyć spowolnione/zablokowane klatki dla fragmentu i przesłać te dane do Monitorowania wydajności Firebase. Zarejestrowane dane wyświetlimy w konsoli Firebase w dalszej części tego ćwiczenia z programowania.

9. Sprawdzanie danych w konsoli Firebase

  1. W dzienniku kliknij adres URL konsoli Firebase, aby otworzyć stronę ze szczegółami śledzenia. ceb9d5ba51bb6e89.jpeg

Możesz też w konsoli Firebase wybrać projekt zawierający Twoją aplikację. W panelu po lewej stronie znajdź sekcję Zwolnij i Monitorowanie, a potem kliknij Skuteczność.

  • Na głównej karcie Dashboard (Panel) przewiń w dół do tabeli logów czasu, a następnie kliknij kartę Niestandardowe logi czasu. W tej tabeli zobaczysz niestandardowe logi czasu, które dodaliśmy wcześniej, oraz kilka gotowych logów czasu, takich jak _app_start.
  • Znajdź 2 niestandardowe ślady kodu: TestActivity-LoadTime i TestFragment-LoadTime. Kliknij Czas trwania przy każdej z nich, aby wyświetlić więcej informacji o zbieranych danych.

A0d8455c5269a590.png

  1. Strona ze szczegółami niestandardowego logu czasu zawiera informacje o czasie trwania logu czasu (tj. zmierzonym czasie wczytywania).

5e92a307b7410d8b.png

  1. Możesz też wyświetlić dane o wydajności niestandardowego logu czasu ekranu.
  • Wróć do głównej karty Panel, przewiń w dół do tabeli logów czasu i kliknij kartę Renderowanie ekranu. W tej tabeli zobaczysz niestandardowe zrzuty ekranu, które dodaliśmy wcześniej, oraz wszystkie gotowe zrzuty ekranu, takie jak MainActivity.
  • Znajdź swój własny zrzut ekranu: MainActivity-TestFragment. Kliknij nazwę logu czasu, aby wyświetlić zbiorcze dane powolnego renderowania i zablokowanych klatek.

ee7890c7e2c28740.png

10. Gratulacje

Gratulacje! Udało Ci się zmierzyć czas wczytywania i wydajność renderowania ekranu aktywności i fragmentu za pomocą Monitorowania wydajności Firebase.

Co udało Ci się osiągnąć

Co dalej?

Wydajność Firebase zapewnia więcej sposobów pomiaru wydajności aplikacji niż niestandardowy log czasu. Automatycznie mierzy dane o czasie uruchamiania aplikacji oraz o wydajności aplikacji na pierwszym planie i w tle. Pora sprawdzić te dane w konsoli Firebase.

Firebase Performance zapewnia też automatyczne monitorowanie żądań sieciowych HTTP/S. Dzięki temu możesz łatwo instrumentować żądania sieciowe bez pisania ani pisania kodu. Czy możesz wysłać część żądań sieci z aplikacji i znaleźć dane w konsoli Firebase?

Premia

Wiesz już, jak mierzyć czas wczytywania i wydajność renderowania ekranu aktywności/fragmentu ekranu przy użyciu niestandardowych zrzutów kodu. Spróbuj teraz zapoznać się z naszą bazą kodu open source, aby sprawdzić, czy potrafisz od razu przechwycić te dane dla dowolnej aktywności lub fragmentu, który wchodzi w skład aplikacji. Jeśli chcesz, możesz wysłać nam swoją wiadomość :-)

11. Dodatkowa edukacja

Zrozumienie, co dzieje się podczas wczytywania aktywności, pomoże Ci lepiej poznać charakterystykę wydajności aplikacji. Wcześniej ogólnie opisaliśmy, co dzieje się podczas wczytywania działania, ale na poniższym diagramie szczegółowo opisujemy każdy etap procesu.

cd61c1495fad7961.png