piątek, sierpnia 13, 2010

Android - nawet platofrma potrafi przeciwko Tobie knuć ;-)

Wrzuciłem już Bloggeroida do Android Market i okazało się, że część użytkowników ma problem z obrazkami, dużymi obrazkami.

Problem mógł występować w 2 miejscach, w trakcie uploadowania dużych obrazków do Picasa'y i w momencie przeglądania obrazków w "menadżerze obrazków".

W obu przypadkach wylatywały błędy "OutOfMemoryError: bitmap size exceeds vm budget".

W przypadku uploadowania obrazków problem był w tym, że chcąc sobie ułatwić obrazek przerzucałem najpierw do strumienia w pamięci, niestety oznaczało to problemy z dużymi obrazkami. Na G1 problemy pojawiały się przy obrazkach mających 4-6 MB, wcześniej tego nie zauważyłem bo G1 ze swoim aparatem 3.2 megapiksela po prostu nie produkuje tak dużych zdjęć :-)
Tego strumienia pamięciowego (dokładniej ByteArrayOutputStream) używałem bo mogłem łatwo pobrać rozmiar obrazka co było mi potrzebne do ustawianie nagłówka Content-Lenght.
No to wyeliminowałem użycie ByteArrayOutputStream i szczęśliwy, że mi tak szybko poszło odpaliłem program na G1...... i znów się wywalił :-)
Okazuje się, że Android robi dokładnie taki sam numer jak ja i wykorzystuje ByteArrayOutputStream, przez co nadal do uploadowania każdy z obrazków był wrzucany do pamięci, tym razem jednak nie przez mój kod, a przez kod Androida i efektem był ten sam błąd :-)
Rozwiązaniem okazało się użycie trybu chunked w połączeniu HTTP.

Stąd lekcja, jeżeli na Androidzie masz zamiar wrzucać duże ilości danych przez HTTP to najlepiej rób to w trybie chunked, wystarczy na obiekcie typu HttpURLConnection wywołać metodę setChunkedStreamingMode(int) z wielkością "kawałka".

Drugi problem widoczny był w "menadżerze obrazków". Tutaj błąd leciał od razu z Androida, a dokładniej z BitmapFactory.decodeStream(InputStream), używanego do stworzenia bitmapy, z której powstać miała ikonka.
Poczytałem po sieci i okazało się, że tu dobrze jest użyć pewnej sztuczki, polegającej na wywołaniu metody BitmapFactory.decodeStream(InputStream,Rec,BitmapFactory.Options).
Kod wygląda teraz tak:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];
options.inSampleSize=4;
Bitmap bitmap = BitmapFactory.decodeStream(is, null,options);


Najważniejsze wydaje się być to inSampleSize=4, które powoduje, że Android dekoduje obrazek do wymiarów 1/4 oryginału [czyli ma on 16 razy mniej pikseli niż oryginał].

No i proszę, człowiek się uczy całe życie :-)


Podobne postybeta
Wredne Google Docs
Chrome2ChromeV2 na GitHub :-)
Niewyspanie - złe skutki niechciejśpizmu ;-)
Lenistwo w działaniu, "piklujemy" Androida ;-)
Piszemy sobie Bloggeroida dla Androida ;-)