Android studija slučaja: placeholder sličica i memorija

Autor ovog članka je profesionalni softverski inženjer i nudi konsultantske usluge android razvoja i obuke. Više informacija ovde.

Ove dane provodim tražeći načina da optimizujem i smanjim memorijsku potrošnju jedne android aplikacije i time, nadam se, poboljšam performanse. Ako želite da utvrdite šta zauzima memoriju u vašoj aplikaciji može vam biti od koristi Eclipse MAT alat – Memory Analizer Tool. Može se instalirati kao plugin u Eclipsu ili kao standalone aplikacija i to prilično jednostavno, a i koršćenje može biti vrlo jednostavno ali tu je i naravno mnogo naprednih opcija. O ovom alatu možda nekom drugom prilikom, a danas o slučaju koji ćemo pratiti preko ovog alata.

Analizirajući memoriju aplikacije ustanovio sam da klasa ImageDownloader, iz internog mini frejmovorka, drži referencu ka Bitmap objektu koji na heap-u zauzima 2 840 000 bajtova – 2.7MB. Instanca klase ImageDownloader inače skida slike za koje nije čudno da budu ove veličine, ali sama ne drži referencu ka njima. Međutim ova klasa ima nekoliko statičkih varijabli – Bitmap-a koje služe kao placeholder-i za ImageView dok se sama slika ne preuzme. Jasno je da placeholder ne bi smeo biti ove veličine, pa je valjalo otkriti zašto se to dešava…

Recimo da u /res/drawable folderu vaše aplikacije imate placeholder.png sličicu rezolucije 500×350 px. Kada učitate ovu sličicu u Bitmap objekat u memoriji telefona može se desiti da zaključite da zauzima čak 2 800 000 bajtova, tj. 2.67MB. Kako je to moguće za sličicu koja na fajl sistemu zauzima 24 509 bajtova tj. oko 25KB? Otkud ovolika razlika?

placeholder.png
placeholder.png

Prva stvar ovde je da sličica u memoriji nikako ne može zauzimati toliko malo memorije koliko zauzima na disku, zato što je na disku fajl koji je kompresovan na neki od standardnih načina (npr. JPG ili PNG) i time mu je smanjena veličina. BItmapa u memoriji ne može koristiti taj način kompresovanja već mora imati podatke o boji i transparentnosti za svaki pixel slike. Ako to znamo onda možemo izračunati koliko bi ova sličica stvarno trebalo da zauzima memorije: Ukupan broj piksela je 500 x 350, i za svaki piksel nam treba 4 bajta (po jedan za transparentnost i tri osnovne boje) što nas dovodi do brojke od 500 x 350 x 4 = 700 000 bajtova, tj 683KB.

Znači sličica u memoriji telefona je 2 800 000 bajtova a trebalo bi biti 700 000 bajtova. Odmah upada u oči da je razlika tačno 4x između ova dva broja. Ako bi probali da izmerite na nekom drugom telefonu koliko ova slika zauzima negde biste videli 1 400 000 bajtova a negde i očekivanih 700 000 bajtova. Ovo zavisi od gustine piksela ekrana na telefonu, tj da li je telefon ldpi, mdpi, hdpi ili xhdpi konfiguracije. Ako znamo da je prvi telefon, na kome je problem i uočen, xhdpi konfiguracije postaje jasno u čemu je stvar.

Problem je u tome što je sličica u /res/drawable folderu. Kada xhdpi telefon zatraži ovu sličicu sistem će je prvo potražiti u /res/drawable-xhdpi folderu resursa, a ako je tu ne nađe, na kraju će je učitati iz /res/drawable direktorijuma ali će je skalirati da bude duplo veća! (odnos ldpi : mdpi : hdpi : xhdpi je 0.75 : 1 : 1.5 : 2 ). Da li ovo objašnjava to što vidimo 4x više memorije nego što bi trebalo? Da, i to savršeno. Slika je sada u stvari 1000 x 700 px, što daje računicu 1000 x 700 x 4 = 2 800 000 bajtova.

Jednostavnim pomeranjem sličice u /res/drawable-xhdpi sličica u memoriji će zauzimati očekivanih 700 000 bajtova.

Ovaj smo problem rešili, ali nema potrebe da tu stanemo jer je očigledno da se zauzeće memorije za jedan običan placeholder može dodatno smanjiti. Jednostavnim trikom možete duplo smanjiti zauzeće memorije po bitmapi, tako što ćete prilikom učitavanja slike zatražiti da se piksel ne opisuje preko 4 bajta nego preko 2 bajta. Ovo naravno nije besplatno u smislu kvaliteta ali za ovakav tip sličice (manje-više u dve boje) će sigurno biti dovoljno dobro. Kako ovo uraditi: BitmapFactory decode… metode primaju i Options objekat preko koga možete dosta uticati na sam proces dekodiranja. Jedna od stvari koju možete podesiti je inPrefferedConfig koji može imati vrednost Bitmap.Config.ARGB_8888 (po jedan bajt za alfa kanal – transparentnost, crvenu, zelenu i plavu boju) ali može imati i vrednost Bitmap.Config.RGB_565 (5 bitova, 6 bitova i 5 bitova respektivno za crvenu, zelenu i plavu boju)

Sada će svaki piksel zauzimati 2 bajta pa će slika zauzimati 500 x 350 x 2 = 350 000 bajtova, čime smo prepolovili zauzeće memorije.

Ne moramo ni ovde stati: Ova rezolucija slike je verovatno prevelika za placeholder, tako da verovatno mozemo i smanjti rezoluciju što će dosta uticati na veličinu same bitmape. Još jedan način bi bio da sličicu pretvorimo u 9patch. Ovim dodatnim modifikacijama ćemo smanjiti Bitmap objekat na oko 100KB što je poboljšanje od 28 puta :)

Podeli ovaj post preko...

One thought on “Android studija slučaja: placeholder sličica i memorija”

Leave a Reply

Your email address will not be published. Required fields are marked *