11 mars 2025 (updated: 11 mars 2025)

Øk hastigheten på appytelsen din med Android Profiler

Chapters

      Har du noen gang lagt merke til at en app du utviklet plutselig fryser? Uansett, forestill deg at det nettopp skjedde. Er neste steg å profilere app-prosessen med Android Profiler? Hvis ikke, har jeg skrevet denne artikkelen spesielt for deg!

      Android Profiler er et sett med verktøy tilgjengelig fra Android Studio 3.0 som erstatter tidligere Android Monitor-verktøy. Den nye pakken er langt mer avansert når det gjelder å diagnostisere ytelsesproblemer i apper. Den kommer med en delt tidslinjevisning og detaljerte CPU-, minne- og nettverksprofiler. Ved å bruke det dyktig kan vi spare mye tid som ellers ville blitt kastet bort på feilsøking eller scrolling i logger i et Logcat-vindu.

      For å få tilgang til profileringsverktøyene klikker du på Vis > Verktøykvinner > Android Profiler eller finner et tilsvarende verktøykvinner i verktøylinjen. For å se sanntidsdata må du koble til en enhet med aktivert USB-feilsøking eller bruke Android-emulatoren og ha app-prosessen valgt. Jeg oppfordrer deg til å lese den offisielle Android-brukerhåndboken for å lære hvordan du inspiserer all data som vises i dette vinduet.

      Liker du å lære gjennom eksempler? Jeg har forberedt to eksempler å øve med CPU Profiler. Dette er små apper som har hatt noen ytelsesproblemer. La oss prøve å løse dem!

      Optimalisering av Android-appytelse

      Eksempel 1

      Vi vil starte med å utvikle en enkel Android-applikasjon som viser en liste over sekvensielle datoer (som ikke har skjedd ennå). Under hver dato kan vi vise gjenværende tid i dager, timer, minutter og sekunder.

      Koden fra begge eksemplene er tilgjengelig på GitHub, så du kan enkelt klone depotet og åpne prosjektet i Android Studio. For nå, sjekk ut revisjonen merket sample-1-before.

      Start med å definere et oppsett bestående av RecyclerView plassert inne i SwipeRefreshLayout. Det vil tillate dataene å oppdatere seg ved vertikal sveip.

      Deretter, opprett en Activity som inflater vårt oppsett, håndterer brukerinteraksjon og utfører operasjoner på hovedtråden for å vise oppdaterte data:

      I linje 9 bruker vi RecyclerView adapter. Vi bruker recycler-biblioteket fra android-commons (brukt i de fleste EL Passion Android-prosjekter). En generisk funksjon tar en liste over elementer, referanse til elementlayoutressurs og en binder. Målet er å holde koden kortfattet og sette opp RecyclerView adapter uten noe boilerplate-kode.

      På slutten av onCreate funksjonen setter vi lytteren for å bli varslet om oppdateringshandlinger utløst av SwipeRefreshLayout. Den refererte refreshData funksjonen erstatter listen med friske nye elementer og varsler adapteren om eventuelle datendringer.

      I linje 25 genererer vi en liste med 1000 elementer. Hvert element setter sine egenskaper i forhold til den nåværende datoen og tidsforskyvningen i dager. Forskyvningen tar verdier fra 0 til 999 og påvirker datoen som vises av elementet (se linje 29). Vi bruker ThreeTenABP som vår dato- og varighets-API. Det er en uvurderlig tilbakeport av java.time.* pakken optimalisert av Jake Wharton for Android.

      I linje 36 utfører vi noen operasjoner for å motta gjenværende tid som en mer menneskelig lesbar varighet.

      I linje 50 binder vi et element med visningsholderen for å oppdatere itemView på en spesifisert posisjon. Vi får tilgang til ressurser for å hente strengen formatert med remainingTime verdien.

      Elementet selv holder formattedDate og remainingTime verdier klare til å vises i de tilsvarende TextView komponentene. La oss bruke følgende elementoppsett:

      Start appen og sveip for å oppdatere data. Har du lagt merke til en frysing? Sannsynligvis ikke. Det avhenger sterkt av enhetens CPU og andre prosesser som bruker CPU-tid. Nå, åpne Android Profiler Tool Window og velg riktig tidslinje for å åpne CPU Profiler. Koble til enheten din og sveip for å oppdatere igjen. Merk at profilertråder legges til app-prosessen og bruker ekstra CPU-tid. Jeg antar at du nå allerede har opplevd rammeskipping. Se på Logcat siden koreografen burde allerede ha advart deg om tung behandling:

      I/Choreographer: Skipped 147 frames! Applikasjonen kan gjøre for mye arbeid på sin hovedtråd.

      Kult! Vi kan starte vår inspeksjon. Se på CPU Profiler tidslinjen:

      CPU Profiler tidslinje

      Over diagrammet er det en visning som representerer brukerinteraksjon med appen. Alle brukerinput-hendelser vises her som lilla sirkler. Du kan se en sirkel som representerer sveipen vi utførte for å oppdatere data. Litt lenger ned kan du finne den nåværende Sample1Activity. Dette området kalles hendelsestidslinje.

      Under hendelsene er det CPU-tidslinjen, som grafisk viser CPU-bruken til appen og andre prosesser i forhold til den totale CPU-tiden som er tilgjengelig. Hva mer, du kan se antall tråder appen din bruker.

      På bunnen kan du se tidslinjen for trådaktivitet som tilhører app-prosessen. Hver tråd er i en av tre tilstander indikert med farger: aktiv (grønn), ventende (gul) eller sovende (grå). På toppen av listen kan du finne appens hovedtråd. På enheten min (Nexus 5X) bruker den ~35% av CPU-tiden i omtrent 5 sekunder. Det er mye! Vi kan ta opp en metode-spor for å se hva som skjer der.

      Klikk på Record-knappen 🔴 rett før du sveiper for å oppdatere handlingen og stopp opptaket kort tid etter at dataoppdateringen er fullført. Når du er ferdig, merk at metode-sporpanelet nettopp har dukket opp:

      android profiler opptak

      Vi vil starte vår analyse fra Call Chart vist i den første fanen. Den horisontale aksen representerer tidens gang. Kallere og deres kalte (fra topp til bunn) vises på den vertikale aksen. Metodekall skilles også etter farge avhengig av om det er et kall til system-API, tredjeparts-API eller vår metode. Merk at total tid for hvert metodekall er en sum av metode selv-tid og dens kalte tid. Fra dette diagrammet kan du slutte at ytelsesproblemet er et sted inne i generateItems metoden. Flytt musen over baren for å sjekke mer detaljer om forløpt tid. Du kan også dobbeltklikke på baren for å se metodeerklæringen i koden. Det er ganske vanskelig å slutte mer fra denne fanen fordi det krever mye zooming og rulling, så vi vil bytte til den neste fanen.

      android profiler

      Flame Chart er mye bedre for å avsløre hvilke metoder som tok enhetens dyrebare CPU-tid. Den aggregerer de samme kallstablene, inverterer diagrammet fra den forrige fanen. I stedet for mange korte horisontale barer, vises en enkelt lengre bar. Bare se på det nå:

      android profiler CPU-tid

      To mistenkelige metoder funnet. Ville du tro at getRemainingTime den totale metodekjøringstiden vil ta over 2 sekunder og LocalDateTime.format over 1 sekund med CPU-tid?

      5

      Merk at denne tiden også inkluderer enhver periode hvor tråden ikke var aktiv. I øverste høyre hjørne av metode-sporpanelet kan du bytte tidsinformasjonen til å bli vist i Thread Time. Hvis vi analyserer en enkelt tråd, kan det være å foretrekke alternativet siden det viser CPU-tidforbruk ikke påvirket av andre tråder.

      Ok, la oss gå videre. Nå åpne den siste fanen for å se Bottom Up-diagrammet. Det viser en liste over metodekall sortert synkende etter CPU-tidforbruk. Dette diagrammet vil gi oss detaljerte tidsinformasjoner (i mikrosekunder). Ved å utvide metodene kan du finne deres kallere.

      6

      Få ut tidsinformasjonen fra diagrammet om metodene vi anklaget for å bruke for mye CPU-tid. Plasser dem i forhold til to metoder fra deres kallstabel:

      Du kan se at getRemainingTime og LocalDateTime.format bruker over 80% av det registrerte metode-sporet! For å fikse den fryse, må vi jobbe med å generere elementer. Det er åpenbart.

      Så, hva skal vi gjøre? Du har sannsynligvis kommet opp med flere løsninger allerede. Vi utfører en tung beregning for å lage 1000 elementer (ikke et lite antall). Du kan tenke på å implementere en paginering for gradvis å lage og vise dataene. Det er en flott idé siden det vil skalere. Men denne gangen vil jeg gjerne gå en annen vei. Hva om vi utfører all formatering rett før vi viser dataene i RecyclerView på spesifisert posisjon — når vi binder Item med RecyclerView.ViewHolder? Takket være det, vil vi påkalle getRemainingTime og LocalDateTime.format metodene bare for noen få for tiden viste og klare til å vise elementer — ikke tusen ganger som før. For å oppnå dette må vi oppdatere Item egenskapene for å holde bare nødvendige data for å utføre formatering senere:

      Det krever å bruke følgende endringer i generateItems og bindItem funksjonene:

      La oss se at vi har inlinet createItem funksjonen siden all formatering nå skjer inne i bindItem metoden. Sjekk ut revisjonen merket sample-1-after for å motta disse endringene.

      Det er på tide å starte CPU Profiler på nytt og ta opp metode-sporet etter at endringene i koden vår ble introdusert. Se på Call Chart for å sjekke om optimaliseringen vår gikk bra:

      7 Android Profiler Call Chart

      Hvis du flytter musen over generateItems funksjonen, vil du finne ut at den nå bruker ~0.3 sekunder av veggklokketiden. Det er over 13 ganger mindre CPU-tid enn før optimaliseringen! Før vi begynner å feire, la oss bytte til Flame Chart for å sikre at endringene våre ikke har negativ innvirkning på total varighet av bindItem metoden. Heldigvis bruker den opptil 0.1 sekunder.

      8I tillegg kan du rulle det for å sikre at optimaliseringen vår ikke påvirker den generelle appytelsen. Prøv å ta opp metode-sporet under en slik rulling. Se at koreografen ikke lenger klager over rammeskipping. Succes! Koden er optimalisert og vi er ferdige med det første eksempelet!

      Eksempel 2

      I det neste eksemplet vil vi for det meste gjenbruke koden fra eksempel 1 etter optimalisering. Den eneste endringen vi vil gjøre i aktivitetslayouten. Vi vil legge til en ImageView over RecyclerView. For å gjøre hele innholdet rullbart, plasser begge visningene inne i NestedScrollView:

      For å unngå konflikter i rulleadferden til RecyclerView må vi sette nestedScrollingEnabled-attributtet til false. Sjekk revisjonen merket sample-2-before for å hente dette eksemplet raskt. Start appen og sveip for å oppdatere dataene. Du bør merke en frysing selv uten Android Profiler tilkoblet.

      Denne gangen har jeg bestemt meg for å la deg utføre diagnose på egen hånd for ikke å ødelegge moroa. Etter vellykket app ytelsesoptimalisering, bør du ikke oppleve noen frysing som i eksempel 1. Det er bare én regel — skjermen som vises for brukeren kan ikke endre sitt utseende. Lykke til!

      Oppsummering

      Jeg tror jeg har oppmuntret deg til å se på Android Profiler oftere. Jeg mener dette er en god praksis hvis vi bryr oss om en jevn brukeropplevelse. I denne artikkelen har jeg hovedsakelig fokusert på CPU Profiler. Imidlertid er både Memory Profiler og Network Profiler, som ikke er dekket i teksten, også verdt å se nærmere på. Å registrere minneallokering hjelper mye med å finne lekkasjer, f.eks. å skylde på deg for ikke å ha resirkulert Bitmaps. Uansett kan profilering av nettverksaktivitet føre til flere optimaliseringer med mål om å redusere batteriforbruk.

       

      Paweł Gajda

      Senior Android Developer

      Kanskje dette er starten på en vakker venskap?

      Vi er tilgjengelig for nye prosjekter.

      Contact us