Udforskning af Android-processer
For et par uger siden var jeg ved at rette en fejl i Android Studio og stødte på “android:process”-tagget. (Jeg arbejder hos Google på Android Studio-holdet.) Jeg vidste, at tagget giver dig mulighed for at angive den proces, som android-komponenten skal køre på. Men mange af de finere detaljer og ræsonnementer bag Android-processer var ukendte for mig. Så jeg læste op på det og skrev denne artikel for at dele det, jeg lærte.
Som altid er jeg glad for at blive korrigeret om noget, jeg er misinformeret om.
En oversigt over Android-processer
Android er baseret på linux, så “Android-systemet starter en ny Linux-proces for programmet med en enkelt udførelsestråd. Som standard kører alle komponenter i det samme program i den samme proces og tråd (kaldet “hovedtråden”).” (fra: Oversigt over processer og tråde)
Generelt er “hoved”-tråden også “UI”-tråden eller “event”-tråden, da enhver ændring af UI’en eller eventhåndtering sker i denne tråd. (undtagelse: 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̵. ̵ Jeg tog fejl, det er kun muligt for systemapplikationer at have forskellige MainThread og UIThread: “Byggeværktøjerne behandler @MainThread
– og @UiThread
-annotationerne som indbyrdes udskiftelige, så du kan kalde @UiThread
-metoder fra @MainThread
-metoder og omvendt. Det er dog muligt for en UI-tråd at være forskellig fra hovedtråden i tilfælde af systemapps med flere visninger på forskellige tråde.”)
Som standard kører hovedprocessen (og den hovedtråd, den starter) med et procesnavn, der er det samme som applicationId for programmet (angivet i din app’s build.gradle). Så hvis du skulle oprette et standardprojekt med Android Studio, ville du få en Android-app, der kører med tråden: com.example.<brugernavn>.myapplication og den matchende standardpakke til din kode.
Hvad “android:process”-tagget gør
Som standard vil en Android-app kun have en enkelt proces. Alle komponenter vil køre på den samme proces. Når du erklærer nye komponenter i dit programs manifest, kan du bruge “android:process”-tagget til at erklære en ny proces for denne komponent. Sådan:
<activity
android:name=".SettingsActivity"
android:process=":setting"
android:label="@string/title_activity_settings">
</activity>
I ovenstående uddrag erklærer jeg en SettingActivity, der vil køre på den lokale com.example.kelvinma.myapplication:setting
-proces. (mere om lokal vs. global senere i denne artikel)
Det “android:process”-tag kan anvendes på de 4 android-komponenter:
- Activity
- Service
- Receiver
- Provider
og også på Application-tagget. Når det anvendes på Application-tagget, arver alle komponenter i applikationen også android:process, medmindre de overskrives af deres eget android:process-tag.
Jeg er ikke sikker på, hvad der er brugssituationen for at bruge dette tag på din applikationsdeklaration, når du bare kan ændre din applicationId i stedet. Hvis du kender en, så fortæl mig det.
Et kort visuelt eksempel
For at illustrere en app med flere processer skal du antage, at jeg starter standardaktiviteten på min Pomodoro-applikation, hvorefter jeg ved hjælp af ovenstående uddrag ændrer min SettingsActivity til at køre på en anden proces. Når jeg starter min app og SettingsActivity, vil min applikation have to 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
Alle komponenter kører dog implicit på standardprocessen, medmindre andet er angivet i process-tagget. Så hvis du tilføjer android:process=":setting"
til din standardaktivitet og derefter starter en anden aktivitet, vil din app have 2 processer:
com.example.kelvinhanma.pomodoro:setting
com.example.kelvinhanma.pomodoro
Den startede aktivitet vil nemlig starte på standardprocessen, da den ikke har et process-tag angivet.
Hvornår gør jeg min app multiprocesser?
Der er 2 grunde, jeg kender til at oprette en app med flere processer:
- Din app har separate handlinger, der skal køres uafhængigt af hinanden, det almindelige eksempel er en musikafspilningsapp, hvor standardprocessen håndterer brugergrænsefladen og en anden proces håndterer musikafspilning.
- Din app støder på hukommelsesbegrænsninger. Android begrænser RAM-forbruget for hver enkelt proces. Så du kan øge din app’s samlede RAM-grænse ved at køre flere processer. Men da processer ikke deler hukommelse, kan du ikke dele Singleton’er mellem processer.
Lokal vs. global proces
Processens type afhænger af den værdi, du indtaster i process-tagget. Hvis du starter med et kolon (“:”), er processen en lokal proces, som er privat for dit program (det fulde procesnavn ville være applicationId:processName). Hvis du starter med et lille bogstav, er det en global proces.
En global proces kan deles mellem programmer. Ofte vil et bibliotek køre på en global proces, så flere programmer kan dele processen for at reducere hukommelsesforbruget.
android:process quiz
Lad os slutte af med et par scenarier, se om du kan forudsige svaret.
Q: Hvad sker der, hvis du bruger en global proces til at indstille din proces til dit pakke-id? android:process="com.example.kelvinhanma.pomodoro"
A: Din proces forbliver uændret, og det er tilladt. Selv om det er en meningsløs og dum ting at gøre.
Q: Hvad sker der, hvis du bruger den til at indstille din proces til en anden apps pakke? Lad os antage, at jeg har en com.example.kelvinhanma.pomodoro
kørende på min enhed. Jeg indstiller den globale proces i en anden app til det samme navn og distribuerer den.
Jeg ville ende op med 2 processer med samme navn!
./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 er fint nok. Procesnavne behøver ikke at være unikke. Bemærk, at brugerne er forskellige (u0_a181, u0_a179), og at proces-id’erne er unikke.
Håber du nød artiklen og lærte noget. Du er velkommen til at lade mig vide, hvis noget er forkert!