Utforskning av Android-processer

För några veckor sedan fixade jag ett fel i Android Studio och stötte på taggen ”android:process”. (Jag arbetar på Google i Android Studio teamet.) Jag visste att taggen gör det möjligt att ange vilken process androidkomponenten ska köras på. Men många av de finare detaljerna och resonemangen bakom Android-processer var okända för mig. Så jag läste på och skrev den här artikeln för att dela med mig av vad jag lärt mig.

Som alltid korrigeras jag gärna om något jag är felinformerad om.

En översikt över Android-processer

Android är baserat på Linux så ”Android-systemet startar en ny Linux-process för applikationen med en enda exekveringstråd. Som standard körs alla komponenter i samma program i samma process och tråd (kallad huvudtråden).” (från: Process and Threads Overview)

Generellt sett är ”huvudtråden” också ”UI-tråden” eller ”händelsetråden”, eftersom alla ändringar av UI-gränssnittet eller händelsehantering sker i denna tråd. (Undantag: y̵o̵u̵ ̵c̵a̵n̵ ̵c̵h̵a̵n̵g̵e̵ ̵t̵h̵i̵s̵ ̵b̵y̵ ̵u̵s̵i̵n̵g̵ ̵T̵h̵r̵e̵a̵d̵ ̵A̵n̵n̵o̵t̵a̵t̵i̵o̵n̵s̵. ̵ Jag hade fel, det är bara möjligt för systemprogram att ha olika MainThread och UIThread: ”Byggverktygen behandlar @MainThread– och @UiThread-annotationerna som utbytbara, så du kan anropa @UiThread-metoder från @MainThread-metoder och vice versa. Det är dock möjligt att en UI-tråd kan skilja sig från huvudtråden när det gäller systemappar med flera vyer på olika trådar.”)

Som standard körs huvudprocessen (och huvudtråden som den startar) med ett processnamn som är detsamma som applicationId för applikationen (deklarerad i appens build.gradle). Så om du skulle skapa ett standardprojekt med Android Studio skulle du få en Android-app som körs med tråden: com.example.<användarnamn>.myapplication och det matchande standardpaketet för din kod.

Vad taggen ”android:process” gör

Som standard har en Android-app bara en enda process. Alla komponenter kommer att köras på samma process. När du deklarerar nya komponenter i applikationens manifest kan du använda taggen ”android:process” för att deklarera en ny process för den här komponenten. Så här:

<activity
android:name=".SettingsActivity"
android:process=":setting"
android:label="@string/title_activity_settings">
</activity>

I utdraget ovan deklarerar jag en SettingActivity som kommer att köras på den lokala com.example.kelvinma.myapplication:setting-processen. (Mer om lokal vs global senare i artikeln)

Taggen ”android:process” kan tillämpas på de 4 androidkomponenterna:

  • Activity
  • Service
  • Receiver
  • Provider

och även på taggen Application. När den tillämpas på Application-taggen ärver alla komponenter i applikationen också android:process om de inte åsidosätts av en egen android:process-tagg.

Jag är inte säker på vad användningsfallet är för att använda den här taggen på din applikationsdeklaration när du bara kan ändra din applicationId i stället. Om du vet något, berätta det för mig.

Ett kort visuellt exempel

För att illustrera en app med flera processer antar jag att jag startar standardaktiviteten i min Pomodoro-applikation och sedan, med hjälp av utklippet ovan, ändrar min SettingsActivity så att den körs i en annan process. Efter att ha startat min app och SettingsActivity kommer min applikation att ha två processer:

$ ./adb shell ps | grep kelvin
u0_a61 32175 1669 1292180 47600 SyS_epoll_ b738f2b5 S com.example.kelvinhanma.pomodoro
u0_a61 32223 1669 1284796 43736 SyS_epoll_ b738f2b5 S com.example.kelvinhanma.pomodoro:setting

Hur som helst körs alla komponenter implicit på standardprocessen om inget annat anges i processtaggen. Så om du lägger till android:process=":setting"till din standardaktivitet och sedan startar en annan aktivitet kommer din app att ha två processer:

com.example.kelvinhanma.pomodoro:setting
com.example.kelvinhanma.pomodoro

Då den startade aktiviteten kommer att starta på standardprocessen eftersom den inte har en process-tagg som är inställd.

När gör jag min app till en multiprocess?

Det finns två skäl som jag känner till för att skapa en app med flera processer:

  1. Din app har separata åtgärder som måste köras oberoende av varandra, det vanligaste exemplet är att en app för musikuppspelning skulle ha standardprocessen som hanterar gränssnittet och en annan process som hanterar musikuppspelningen.
  2. Din app har minnesbegränsningar. Android begränsar RAM-användningen per process. Du kan alltså öka appens totala RAM-gräns genom att köra flera processer. Men eftersom processer inte delar minne kan du inte dela Singletons mellan processer.

Lokal vs global process

Processens typ beror på det värde du anger i processtaggen. Om du börjar med ett kolon (”:”) är processen en lokal process som är privat för ditt program (fullständigt processnamn skulle vara applicationId:processName). Om du börjar med en liten bokstav är det en global process.

En global process kan delas mellan program. Ofta körs ett bibliotek på en global process så att flera program kan dela processen för att minska minnesanvändningen.

android:process quiz

Vi avslutar med några scenarier, se om du kan förutsäga svaret.

Q: Vad händer om du använder en global process för att ställa in din process till ditt paket-ID? android:process="com.example.kelvinhanma.pomodoro"

A: Din process förblir oförändrad och detta är tillåtet. Även om det är en meningslös och dum sak att göra.

Q: Vad händer om du använder den för att ställa in din process på ett annat programs paket? Anta att jag har en com.example.kelvinhanma.pomodoro som körs på min enhet. Jag ställer in den globala processen för en annan app till samma namn och distribuerar den.

Jag skulle få två processer med samma namn!

./adb shell ps | grep kelvin
u0_a181 28008 630 4324212 66060 SyS_epoll_wait 0 S com.example.kelvinhanma.pomodoro
u0_a179 28287 630 4327384 66200 SyS_epoll_wait 0 S com.example.kelvinhanma.pomodoro

Men det är okej. Processnamn behöver inte vara unika. Lägg märke till att användarna är olika (u0_a181, u0_a179) och att process-id:erna är unika.

Hoppas att du gillade artikeln och lärde dig något. Låt mig gärna veta om något är felaktigt!