Kiedy programista Javy przerzuca się na Androida
Po ponad sześciu latach pracy programisty Javy, zdecydowałem się spróbować swoich sił w programowaniu w systemie Android. Czytałem, że Java może stanowić bardzo dobrą podstawą do nauki programowania na platformie Androida. Osobiście pracowałem już w technologiach takich jak Spring, Struts, Hibernate i in., oraz zacząłem swoją pracę w Javie, wersja J2SE 5.0. Dlatego właśnie pomyślałem, że to dobry pomysł.
A więc, jak zostać programistą Androida?
Oto moja historia. Znajdziecie tu moje wnioski, przeczytacie o moich próbach, dowiecie się o problemach jakie napotykałem, oraz o tym, jak sobie z nimi poradzić.
Od czego zacząć?
Zacznijcie od projektu. Mój pierwszy projekt w Androidzie był aplikacją umożliwiającą planowanie podróży po amerykańskich autostradach. Dzięki tej aplikacji użytkownicy są w stanie zaplanować przejazd w taki sposób, aby móc zjeść obiad w ulubionej restauracji, albo odwiedzić miejsca, które uznają za interesujące. Stworzenie tej aplikacji zajęło nam ponad miesiąc.
Najlepszym krokiem początkowym w przygotowaniu projektu naszej pierwszej aplikacji jest przeczytanie instrukcji dla programistów, a konkretnie rozdziału pt. “Jak zacząć”. W mojej ocenie jest on napisany bardzo prosto, co znacznie ułatwia uczenie się. Instrukcja wprowadza początkującego programistę w świat Androida krok po kroku, od instalacji środowiska budowy, do bardziej skomplikowanych rzeczy, jak ściąganie zdjęć z telefonu.
Android Studio – czy warto korzystać z tego IDE?
W mojej pracy używałem już IntelliJ przez dwa lata, a przedtem pracowałem z Eclipse. Programując w Androidzie, musiałem się zmierzyć z Android Studio, które jest oficjalnym zintegrowanym środowiskiem programistycznym (IDE) dla programowania aplikacji w Androidzie, opartym na IntelluJ IDEA. Android Studio wygląda praktycznie jak IntelliJ, co nie jest dziwne, ponieważ oba środowiska są oparte na tym samym kodzie źródłowym. Jednakże podczas, gdy IntelliJ oferuje całkiem szerokie spektrum funkcjonalności (wspomaga tworzenie w JEE, Scala, Groovy, itp.), Android Studio jest uproszczonym środowiskiem, zorientowanym na Androida. Umożliwia tworzenie nowych projektów i ich łatwiejszą konfigurację. Uważam jednak, że funkcje Android Studio będą wcześniej czy później dodane do IntelliJ.
Osobiście nie jestem fanem korzystania z IDE, w którym ma się mnóstwo już zainstalowanych komponentów służących do operowania wieloma narzędziami, bibliotekami, językami itp., których i tak się nie używa. Programowanie w środowisku zorientowanym na Androida, gdzie mamy dostęp do zbiorów bibliotek i plug-inów tylko dla Androida to, moim zdaniem, znacznie lepsze rozwiązanie.
Tryb podglądu w Android Studio - funkcjonalność warta polecenia
Funkcjonalność, która warta jest polecenia to tryb podglądu. Odkryłem ją podczas pracy z Android Studio. Pozwala przeglądać zaprojektowaną aplikację na wielu różnych ekranach telefonów i tabletów.
Ilustracja 1: tryb podglądu na kilku różnych ekranach.
Cykl życia aplikacji
Cykl życia aplikacji w systemie Android został zaprojektowany w zupełnie inny sposób niż w aplikacjach kontrolowanych przez JVM. W Androidzie nie ma klasycznej metody statycznej public static main(String[] args) {…}. Zamiast tego znajdujemy serię nowych metod w klasach aktywności, takie jak onCreate, on Resume, onPause, onDestroy, itp. Stosując wyżej wspomniane metody definiuje się konkretne “zachowania” danych klas aktywności.
Każda z wyżej wspomnianych metod uczestniczy w cyklu życia danej klasy aktywności, co widzimy w poniższym diagramie.
Ilustracja 2: Cykl życia klasy aktywności przy zastosowaniu adekwatnych metod.
JVM vs. Dalvik
Różnica między cyklem życia procesów Java Virtual Machine (JVM) i procesów działających na wirtualnej maszynie Dalvik, jest znacząca. Procesy aplikacji Javy działające na JVM mogą być zamknięte w każdej chwili, więc ma się całkowitą kontrolę nad cyklem życiowym danego procesu aplikacji na JVM.
W Androidzie jest trochę inaczej, bo nawet po zamknięciu aplikacji przy pomocy interfejsu użytkownika, jest ona nadal aktywna w tle. Jest to taki system operacyjny, który decyduje kiedy „zabić” działanie aplikacji. Wyobraźcie sobie, że zamknęliście swoją ulubioną grę albo aplikację na telefonie. W rzeczywistości jednak może ona nadal działać w tle, uwolniwszy jedynie trochę miejsca w pamięci, zamknąwszy połączenie internetowe i in. Czemu tak się dzieje? Twórcy Androida doszli do wniosku, że w takich słabych urządzeniach jak telefony komórkowe łatwiej i szybciej jest przełączyć aplikację na tryb stand-by niż wyłączyć ją i ponownie uploadować całą pamięć. Użycie zapasu i CPU jest znacznie wyższe, kiedy się ponownie otwiera aplikację od początku niż kiedy się ją restartuje ze stanu uśpienia.
Na stackoverflow.com można znaleźć niestandardowe rozwiązania, jak całkowicie zamknąć proces aplikacji, ale wielu się one nie podobają. Według mnie, wszystko zależy od aplikacji i potrzeb. Jeśli nie ma się przymusu, aby zrobić inaczej, powinno się oddać kontrolę Androidowi.
Obiekty vs. typy prymitywne
W Androidzie duży nacisk kładzie się na optymalizację CPU i użycie pamięci. Dlatego to wielu programistów poleca używanie prymitywów zamiast obiektów. Aby wyjaśnić dlaczego, zazwyczaj przytaczają następujący argument: „Używanie Integer do obliczeń angażuje więcej cykli CPU i zajmuje mniej pamięci. Typy prymitywne nie są obiektami, więc nie powodują odśmiecania pamięci.” Jeśli tworzysz aplikację dla starszych modeli telefonów musisz pomyśleć o optymalizacji od samego początku. Można by także powiedzieć, że: „Przedwczesna optymalizacja jest to źródło całego zła”. No cóż, trzeba znaleźć złoty środek.
MVC
W Androidzie nie znajdziecie klasycznych wzorców MVC (Model-View-Controller). Widok jest wbudowany w wiele różnych plików XML i w samych klasach Activity. Jest cienka granica między logiką i widokiem zdefiniowanym w klasach Activity albo fragmentach. Można tworzyć wiele klas, których funkcją jest operowanie logiką dla jednego lub wielu widoków (fragmentów). Klasy Activity są często odpowiedzialne zarówno za widok jak i działanie logiki biznesowej. Niektórzy uważają, że w Androidzie mamy do czynienia z „kontrolerem widoku”. Jak dla mnie, wciąż ciężko jest budować klasy Activity i nie łamać zasady jednej odpowiedzialności.
Enums
Było to zaskakujące dla mnie, że nie rekomenduje się używania enums (wyliczeniowych typów danych) w Androidzie, co jest wyraźnie zaznaczone w instrukcji: “Enums często wymagają ponad dwukrotnie większej pamięci niż stałe statyczne. Powinno się szczególnie unikać używania enums w Androidzie.”
Zamiast tego używa się wielu różnych typów, polecają stosowanie stałych statycznych i zmiennych statycznych. Przyszłość może być inna, jeśli telefony będą miały mocniejsze procesory, a ograniczenia pamięci nie będą tak problematyczne.
Java 8
Niestety, Android nie wspiera Javy 8. Kiedy buduje się aplikację w Androidzie, wspierane są jedynie apki minimalnie na Android 4.0 i API level 14. Kiedy się tworzy aplikację, która musi być kompatybilna ze starszą wersją systemu Android i chcesz użyć lambdy takiej jak oferowana w Javie 8, musisz zastosować zewnętrzne biblioteki, takie jak RetroLambda. Jednakże RetroLambda nie ma innych funkcjonalności dostępnych w Javie 8, co podkreśla instrukcja Androida: „Niestety, właściwości Javy 8, poza wyrażeniem lambda, nie są wspierane przez RetroLambda na dzień dzisiejszy. Tylko Android N wprowadza wsparcie dla Javy 8”. O ile wiem, Android N nie ma zamiaru w pełni jej wspierać.
camelCase vs. _underscore
Kiedy się przegląda różne przykłady aplikacji na Androida, ich autorzy stosują różne konwencje notacji ID w schematy XML. Nie znalazłem żadnej informacji w przewodniku na temat konwencji atrybucji ID, a co gorsza, sam dokument nie jest spójny, kiedy chodzi o tę kwestię. W niektórych przykładach widzimy notację obiektów ID są jako main_layout, a w innym – mainLayout. Na szczęście samo środowisko programowania definiuje zasady notacji dość klarownie: kiedy nadajemy ID w plikach XML, trzeba używać podkreślnika. Aby nazwać pliki XML, też należy użyć podkreślnika. Argumentem za stosowaniem takiej nomenklatury jest fakt, że pliki tworzone w folderze /res nie mogą zawierać znaków w górnym indeksie.
.class vs .dex
W klasycznej Javie każda klasa jest skompilowana w oddzielnym pliku .class . Jeśli masz jedną klasę publiczną, jedną zagnieżdżoną i jedną statyczną w danej klasie, otrzymujesz trzy pliki .class. W Androidzie wszystkie te klasy są skompilowane w jednym pliku .dex. Pliki .dex są tworzone przy pomocy narzędzia zwanego dexer. W przeciwieństwie do plików .class , pliki .dex są mocno zoptymalizowane pod względem zużywania pamięci.
Ilustracja 3: różnica pomiędzy plikami .class i .dex .
.jar vs .apk
Plik .apk jest plikiem wykonywalny służącym do otwierania lub instalowania aplikacji w Androidzie. Inaczej niż w przypadku plików .jar, które składają się z wielu skompilowanych plików .class, pliki .apk zawierają pliki .dex, gdzie wszystkie aplikacje zostały spakowane.
Ilustracja 4:przykład agregacji klasowej w pliku .jar
Jak widać na powyższej ilustracji, plik .jar składa się z wielu klas skompilowanych w plik .class.
Ilustracja 5: przykład agregacji pliku .dex w .apk
Co się dzieje z plikami biblioteki .jar dodanymi do aplikacji Android? Wszystkie pliki .class umiejscowione w plikach .jar są skompilowane w kodzie bajtowym maszyny Dalvik, a następnie spięte z plikami .dex z pozostałymi klasami.
Kod bajtowy Java vs. kod bajtowy Dalvik
Aplikacje Androida są skompilowane na kodzie bajtowym działającym na DVM (Dalvik Virtual Machine), który jest trochę inny niż kod bajtowy Java. JVM działa na zasadzie stosu, podczas gdy DVM na zasadzie rejestru. JVM ma około 200 poleceń kodów operacji, podczas gdy DVM ma 218 i są one dłuższe, ponieważ zawierają adresy rejestrów źródłowych i docelowych.
W JVM wiele poleceń jest odpowiedzialnych za transfer danych pomiędzy stosem, a listą lokalnych zmiennych. W DVM nie ma takich poleceń, ponieważ nie ma stosu.
Podsumowując…
Oto jak przebiegło pierwszych kilka tygodni mojej pracy nad tworzeniem aplikacji w Androidzie. Twórcy systemu Android kładą wielki nacisk na optymalizację kodu i pamięci oraz zużycia CPU. DVM jako taki różni się znacząco od JVM. To co mnie zadziwiło, to fakt że dla DVM został stworzony specjalny kod bajtowy, oraz, że struktura plików .dex jest tak odmienna od plików .jar. Osobiście nie spodziewałem się jak dotąd tak poważnych zmian.
Dajcie znać, jakie były wasze początki!
Grzegorz Kukla
Programista Java z ponad sześcioletnim doświadczeniem;