Android-folyamatok felfedezése

Pár hete egy hibát javítottam az Android Studio-ban, és találkoztam az “android:process” címkével. (A Google-nél dolgozom az Android Studio csapatában.) Tudtam, hogy a tag lehetővé teszi, hogy megadjuk, hogy az android komponens milyen folyamaton fusson. Azonban sok finomabb részlet és az Android folyamatok mögötti érvelés ismeretlen volt számomra. Ezért utánaolvastam, és megírtam ezt a cikket, hogy megosszam, amit megtudtam.

Mint mindig, örömmel veszem, ha kijavítanak bármiben, amiről rosszul informált vagyok.

Az Android folyamatok áttekintése

Az Android a linuxon alapul, így “az Android rendszer egy új Linux folyamatot indít az alkalmazás számára egyetlen végrehajtószállal. Alapértelmezés szerint ugyanazon alkalmazás minden összetevője ugyanabban a folyamatban és szálban (az úgynevezett “fő” szálban) fut”. (from: Process and Threads Overview)

A “fő” szál általában a “UI” szál vagy az “esemény” szál is, mivel minden UI módosítás vagy eseménykezelés ezen a szálon történik. (kivétel: 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̵. ̵ Tévedtem, csak a rendszeralkalmazásoknál lehetséges, hogy különböző MainThread és UIThread legyen: “A build eszközök a @MainThread és @UiThread annotációkat felcserélhetőnek tekintik, így @MainThread metódusokból @MainThread metódusok hívhatók @UiThread metódusok és fordítva. Azonban lehetséges, hogy egy UI szál különbözik a főszáltól a több nézetet különböző szálakon tartalmazó rendszeralkalmazások esetében.”)

A főfolyamat (és az általa indított főszál) alapértelmezés szerint olyan folyamatnévvel fut, amely megegyezik az alkalmazás applicationId-jával (az alkalmazás build.gradle fájljában deklarálva). Tehát ha létrehoznál egy alapértelmezett projektet az Android Studio segítségével, akkor egy Android alkalmazást kapnál, amely a következő szálon fut: com.example.<felhasználónév>.myapplication és a kódodnak megfelelő alapértelmezett csomagot.

Mit csinál az “android:process” tag

Egy Android alkalmazásnak alapértelmezés szerint csak egyetlen folyamata van. Minden komponens ugyanabban a folyamatban fog futni. Amikor új komponenseket deklarál az alkalmazás manifesztjében, akkor az “android:process” tag segítségével új folyamatot deklarálhat az adott komponens számára. Például így:

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

A fenti részletben egy SettingActivity-t deklarálok, amely a helyi com.example.kelvinma.myapplication:setting folyamaton fog futni. (a lokális vs globálisról bővebben később ebben a cikkben)

Az “android:process” tag alkalmazható a 4 android komponensre:

  • Activity
  • Service
  • Receiver
  • Provider

és az Application tagre is. Ha az Application tag-en alkalmazzuk, az alkalmazás minden komponense is örökli az android:process-t, hacsak nem írja felül a saját android:process tag-jük.

Nem vagyok benne biztos, hogy mi a felhasználási módja annak, hogy ezt a tag-et használjuk az alkalmazás deklarációnkban, amikor ehelyett egyszerűen megváltoztathatjuk az applicationId-t. Ha tudsz egyet, kérlek, mondd el nekem.

Egy rövid vizuális példa

A többfolyamatos alkalmazás illusztrálására tegyük fel, hogy elindítom az alapértelmezett tevékenységet a Pomodoro alkalmazásomon, majd a fenti snippet felhasználva megváltoztatom a SettingsActivity-t, hogy egy másik folyamatban fusson. Ekkor az alkalmazásom elindítása és a SettingsActivity elindítása után az alkalmazásomnak 2 folyamata lesz:

$ ./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

Minden komponens azonban implicit módon az alapértelmezett folyamaton fut, hacsak a process tag másként nem határozza meg. Tehát ha az alapértelmezett aktivitáshoz hozzáadod a android:process=":setting"et, majd elindítasz egy másik aktivitást, akkor az alkalmazásodnak 2 folyamata lesz:

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

Mert az elindított aktivitás az alapértelmezett folyamaton fog elindulni, mivel nincs beállítva process tag.

Mikor tehetem az alkalmazásomat többfolyamatossá?

2 okot ismerek arra, hogy többfolyamatos alkalmazást hozzunk létre:

  1. Az alkalmazásodnak különálló műveletei vannak, amelyeket egymástól függetlenül kell futtatni, a leggyakoribb példa egy zenelejátszó alkalmazásnál az alapértelmezett folyamat kezeli a felhasználói felületet és egy másik folyamat a zenelejátszást.
  2. Az alkalmazásod memória korlátokba ütközik. Az Android folyamatonként korlátozza a RAM-használatot. Így több folyamat futtatásával növelheti az alkalmazás teljes RAM-korlátját. Mivel azonban a folyamatok nem osztják meg a memóriát, nem oszthatja meg a Singletonokat a folyamatok között.

Lokális vs. globális folyamat

A folyamat típusa a process tagben megadott értéktől függ. Ha kettősponttal (“:”) kezded, akkor a folyamat helyi, az alkalmazásod számára privát (a teljes folyamat neve applicationId:processName lenne). Ha kisbetűvel kezdődik, akkor globális folyamatról van szó.

A globális folyamat megosztható az alkalmazások között. Gyakran előfordul, hogy egy könyvtár egy globális folyamaton fut, így több alkalmazás megoszthatja a folyamatot a memóriahasználat csökkentése érdekében.

android:process quiz

Végezzük néhány forgatókönyvvel, hátha meg tudja jósolni a választ.

K: Mi történik, ha egy globális folyamat segítségével a folyamatot a csomag azonosítójára állítjuk? android:process="com.example.kelvinhanma.pomodoro"

A: A folyamatod változatlan marad, és ez megengedett. Bár ez egy értelmetlen és buta dolog.

K: Mi van, ha arra használod, hogy a folyamatodat egy másik alkalmazás csomagjára állítsd be? Tegyük fel, hogy egy com.example.kelvinhanma.pomodoro fut a készülékemen. Beállítom egy másik alkalmazás globális folyamatát ugyanarra a névre, és telepítem.

A végén 2 azonos nevű folyamatot kapnék!

./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

De ez rendben van. A folyamatneveknek nem kell egyedinek lenniük. Figyeld meg, hogy a felhasználók különbözőek (u0_a181, u0_a179) és a folyamatok azonosítói egyediek.

Remélem, tetszett a cikk és tanultál valamit. Nyugodtan szólj, ha valami hibás!