hogy

Androidban csak a fő alkalmazásszálnak van joga a képernyő frissítésére és a képernyőérintések kezelésére. Ez azt jelenti, hogy amikor az alkalmazás összetett munkát végez a fő szálban, akkor nincs lehetősége reagálni a kattintásokra. A felhasználó számára úgy tűnik, hogy az alkalmazás lefagy. Ebben az esetben ismét segít az összetett műveletek eltávolítása külön szálakba.

Van azonban egy sokkal finomabb és nem nyilvánvalóbb pont. Az Android 60 FPS-sel frissíti a képernyőt. Ez azt jelenti, hogy animációk megjelenítése vagy listák görgetése során csak 16,6 ms áll rendelkezésére az egyes képkockák megjelenítéséhez. A legtöbb esetben az Android megbirkózik ezzel a munkával, és nem veszíti el a kereteket. De egy rosszul megírt pályázat lelassíthatja.

akkor
Hoppá, kihagytunk egy keretet

Egy egyszerű példa: A RecyclerView egy interfész elem, amely lehetővé teszi rendkívül hosszú görgethető listák létrehozását, amelyek ugyanolyan mennyiségű memóriát foglalnak el, függetlenül a lista hosszától. Ez annak köszönhető, hogy ugyanazokat az interfészelem-készleteket (ViewHolder) használják a különböző listaelemek megjelenítéséhez. Ha egy listaelem el van rejtve a képernyőn, a ViewHolder a gyorsítótárba kerül, majd a következő listaelemek megjelenítésére szolgál.

Amikor a RecyclerView lekéri a ViewHolder elemet a gyorsítótárból, meghívja az adapter onBindViewHolder() metódusát, hogy feltöltse az adott listaelem adataival. És itt történik valami érdekes: ha az onBindViewHolder() metódus túl sok munkát végez, akkor a RecyclerView-nak nem lesz ideje időben elkészíteni a következő elemet a megjelenítéshez, és a lista lassulni kezd görgetés közben.

Egy másik példa. A RecyclerView-hoz csatlakoztathat egy egyéni RecyclerView.OnScrollListener()-et, amelynek az OnScrolled() metódusa a lista görgetésekor meghívódik. Általábana képernyő sarkában lévő kerek akciógomb dinamikus elrejtésére és megjelenítésére szolgál (FAB — Floating Action Button). De ha ennél a módszernél összetettebb kódot alkalmaz, a lista ismét lelassul.

És a harmadik példa. Tegyük fel, hogy a programunk felülete sok töredékből áll, amelyek között az oldalmenü (Fiók) segítségével válthatunk. Úgy tűnik, hogy a legkézenfekvőbb megoldás erre a problémára az, ha valami ehhez hasonló kódot helyezünk el a menüelem kattintáskezelőjében:

// Töredék váltásagetSupportFragmentManager .beginTransaction() .replace(R.id.container, fragment, "fragment") .elkövetni()

// Zárja be a fiókot fiók.closeFiók(GravityCompat.START)

Minden logikus, de csak ha elindítja az alkalmazást és teszteli, akkor látni fogja, hogy a menü bezárását jelző animáció lelassul. A probléma abban rejlik, hogy a commit() metódus aszinkron, vagyis a töredékek közötti váltás és a menü bezárása egyszerre történik, és az okostelefonnak egyszerűen nem lesz ideje az összes képernyőfrissítési műveletet időben feldolgozni.

Ennek elkerülése érdekében a menüzáró animáció befejezése után váltani kell a töredéket. Ezt úgy teheti meg, hogy egy egyéni DrawerListenert csatlakoztat a menühöz:

mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() { @Public void felülbírálása a DrawerSlide-on (fióknézet megtekintése, float slideOffset) {} @Public void felülbírálása a DrawerOpeneden (fióknézet megtekintése) {} @Override public void onDrawerStateChanged(int newState) {}

@Felülbírálás public void onDrawerClosed(View fióknézet) { if (mFragmentToSet != null) { getSupportFragmentManager() .beginTransaction() .replace(R. id.container, mFragmentToSet) .commit(); mFragmentToSet = null; });

Többegy egyáltalán nem nyilvánvaló pillanat. Az Android 3.0-tól kezdve az alkalmazás felületének renderelése a grafikus processzoron történik. Ez azt jelenti, hogy a program témájában megadott összes bitkép, rajzolható és erőforrás a GPU memóriájába töltődik, így a hozzáférés nagyon gyors.

A képernyőn megjelenő felület bármely eleme poligonok és GPU-utasítások halmazává alakul, így például gyors törlés esetén megjelenik. A láthatóság attribútum módosításával (button.setVisibility(View.GONE) és button.setVisibility(View.VISIBLE)) a nézet ugyanolyan gyorsan elrejtődik és megjelenik.

De a Nézet megváltoztatásakor, még a legkisebbnél is, a rendszernek újra el kell készítenie a Nézetet a semmiből, új poligonokat és utasításokat töltve be a GPU-ba. Sőt, a TextView megváltoztatásakor ez a művelet még drágább lesz, mivel az Androidnak először rasztereznie kell a betűtípust, azaz a szöveget téglalap alakú képekké kell alakítani, majd elvégezni az összes mérést és utasításokat generálni a GPU számára. És van egy művelet az aktuális elem és más elrendezési elemek helyzetének kiszámítására is. Mindezt figyelembe kell venni, és csak akkor kell megváltoztatni a Nézetet, amikor valóban szükséges.

A túlhúzás egy másik komoly probléma. Ahogy fentebb is mondtuk, a sok beágyazott elemet tartalmazó összetett elrendezések elemzése önmagában lassú lesz, de minden bizonnyal magával hozza a képernyő gyakori újrarajzolásának problémáját is.

Képzeld el, hogy több beágyazott LinearLayout-od van, és ezek egy része a background tulajdonsággal is rendelkezik, vagyis nem csak más interfész elemeket tartalmaz, hanem van egy kép formájú vagy színnel kitöltött háttér is. Ennek eredményeként az interfész megjelenítésének szakaszában a GPU a következőket fogja tenni: kitölti a gyökér LinearLayout által elfoglalt területet a kívánt színű képpontokkal, majd kitöltia képernyőnek a beágyazott elrendezés által elfoglalt része, egy másik szín (vagy ugyanaz) és így tovább. Ennek eredményeként egy képkocka renderelése során a képernyőn lévő sok pixel többször frissül. Ennek pedig semmi értelme.

Lehetetlen teljesen elkerülni a túllépést. Például, ha egy gombot piros háttéren kell megjelenítenie, akkor is először meg kell töltenie a képernyőt pirossal, majd újra kell rajzolnia a gombot jelképező képpontokat. Ráadásul az Android tudja, hogyan kell optimalizálni a renderelést, hogy ne forduljon elő túlhúzás (ha például két azonos méretű elem egymáson van, a második pedig átlátszatlan, akkor az első egyszerűen nem rajzolódik ki). Sok múlik azonban a programozón, akinek minden erejével meg kell próbálnia minimalizálni a túlhúzást.

Az overlay hibakereső eszköz segít ebben. Androidba van beépítve, és itt található: Beállítások ? Fejlesztői beállítások? GPU-túlhúzás hibakeresése? Túlrajzolt területek megjelenítése. Bekapcsolás után a képernyő különböző színekre festődik, ami a következőket jelenti:

  • normál szín – egyetlen fedőréteg;
  • kék - dupla fedőréteg;
  • zöld - hármas;
  • piros - a negyedik és több.

A szabály itt egyszerű: ha a program felületének nagy része zöldre vagy pirosra vált, akkor problémái vannak. Ha főleg kéket (vagy a program natív színét) látja kis zöld és piros foltokkal, ahol különböző kapcsolók vagy a felület egyéb dinamikus elemei jelennek meg, akkor minden rendben van.

Rajzolj túl egy egészséges programot és egy dohányost

És néhány tipp:

  • Lehetőleg ne használja a háttértulajdonságot az elrendezésekben.
  • Csökkentse a beágyazott elrendezések számát.
  • Szúrja be a következő sort a tevékenységkód elejére: getWindow().setBackgroundDrawable(null);.
  • Nemhasználjon átlátszóságot ott, ahol nélkülözheti.
  • A Hierarchy Viewer eszközzel elemezheti az elrendezések hierarchiáját, azok egymáshoz való viszonyát, megbecsülheti a megjelenítési sebességet és kiszámíthatja a méreteket.