sobota, 7 maja 2011

Inne podejście do przechowywania danych w systemie Android, z wykorzystaniem db4o i NetBeans 6.9

W systemie Android domyślnie mamy dostęp do relacyjnej bazy danych SQLite w której możemy zapisywać informacje z naszej aplikacji. Jest to typowy sposób dostępu do danych w tym systemie. Nie jest dziwne, że do tego systemu została wybrana ta baza. Nie posiada ona wielu magicznych dodatków (jak obecnie popularne kombajny) i założenia jest „Lite” (przynajmniej z nazwy). Z innej strony jest to baza o ciekawym stosunku kodu źródłowego do testów. Odsyłam do artykułu: Extremalne testowanie w SQLite Pawła Lipińskiego. Jak skorzystać z tej bazy jest już opisane na blogu Mirosława Stanka (część 1, część 2, część 3) jak i na helloandroid.  Ja nie będę po raz kolejny opisywał tego rozwiązania, ale postaram się poniżej pokazać inne (ciekawsze?) podejście do tematu, z wykorzystaniem obiektowej bazy danych.

DataBase For Objects, zwana powszechnie db4o, to interesująca alternatywa dla SQLite w systemie Android, jeśli chodzi o składowanie danych. Od wersji 7.5 biblioteka daje wsparcie dla tej platformy. Poniżej przedstawię jak zbudować projekt w NetBeans’ie 6.9 z wykorzystaniem tej biblioteki.

Tworzymy w NetBeans’ie nowy projekt typu AndroidApplication. Następnie do bibliotek dodajemy JAR’a zawierającego db4o.


Następnie warto skorzystać z klasy Db4oHelper przedstawionej przez Damasia Maneiro (niestety nie mam pojęcia jak to po polsku odmienić) w artykule na dzone.com: Using db4o in an Android application. Jest tam sporo kodu odwołującego się do klas z tamtejszego projektu, więc pozwoliłem sobie trochę ten kod zmienić do naszych potrzeb. Listing poniżej.

public class Db4oHelper {

    private static ObjectContainer oc = null;
    private Context context;

    public Db4oHelper(Context ctx) {
        context = ctx;
    }

    public ObjectContainer db() {
        try {
            if (oc == null || oc.ext().isClosed()) {
                oc = Db4oEmbedded.openFile(dbConfig(), 
                        db4oDBFullPath(context));
            }
            return oc;
        } catch (Exception ie) {
            Log.e(Db4oHelper.class.getName(), ie.toString());
            return null;
        }
    }

    private EmbeddedConfiguration dbConfig() throws IOException {
        EmbeddedConfiguration configuration = 
                Db4oEmbedded.newConfiguration();
        return configuration;
    }

    private String db4oDBFullPath(Context ctx) {
        return ctx.getDir("data", 0) + "/" + "database.db4o";
    }

    public void close() {
        if (oc != null) {
            oc.close();
        }
    }
}

Mamy do dyspozycji publiczny konstruktor, który jako argument przyjmuje Context – podobnie jak w SQLite. Potrzebne nam to będzie do umieszczenia pliku z bazą danych w miejscu, gdzie znajdują się dane aplikacji. Następnie mamy metodę db(), która zwraca nam instancję klasy odpowiedzialnej za połączenie z bazą. Mamy jeszcze metodę zwracającą nam konfigurację bazy (domyślna nam początkowo wystarczy) i ścieżkę do pliku z bazą. Na koniec metoda do zamknięcia połączenia z bazą.

Teraz można już napisać klasy, które chcemy składować w bazie danych. Mamy kilka możliwości jak to zrobić. Ja poniżej przedstawię prosty obiekt POJO i odpowiadającą mu klasę DAO. Można oczywiście to wszystko w jedną klasę wrzucić, ale tak będzie ładniej i poprawniej. Poniżej przykładowy kod klasy User.

public class User {

    private String login;
    private String password;

    public User(String login, String password) {
        this.login = login;
        this.password = password;
    }

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

W klasie są definiowane 2 pola typu String o nazwach login i password oraz odpowiadające im gettery i settery. No i jeszcze konstruktor przyjmujący te parametry. Jeśli ktoś woli programować w stylu funkcyjnym, to można zrobić pola final i wyrzucić settery. Db4o i tak zadziała, gdyż do ustawiania wartości wykorzystuje refleksje do pól składowych, a nie do setterów.

Proszę zwrócić uwagę, że klasa nie posiada żadnego pola w stylu ID. Dodawanie do klas persystentnych identyfikatorów, to niemalże konieczność w świecie relacyjnym, gdyż dzięki identyfikatorom tworzy się relacje i złączenia przy zapytaniach. W bazach obiektowych tego nie potrzebujemy! Mamy przecież referencje i kolekcje.

Poniżej kod klasy DAO dla klasy User:

public class UserDao {

    private ObjectContainer db;

    public UserDao(Context context) {
        Db4oHelper db4oHelper = new Db4oHelper(context);
        this.db = db4oHelper.db();
    }

    public void saveOrUpdate(User user) {
        db.store(user);
    }

    public void delete(User user) {
        db.delete(user);
    }

    public List<User> getAll() {
        return db.query(User.class);
    }
    
    public List<User> getByLogin(String login) {
        User example = new User(login, null);
        return db.queryByExample(example);
    }

    public List<User> getByLoginAndPassword(
                 String login, String password) {
        User example = new User(login, password);
        return db.queryByExample(example);
    }
}


Klasa posiada konstruktor, za pomocą którego do klasy przekazujemy obiekt typu Context. Jest to klasa android’owa, za pomocą której Db4oHelper może się dostać do odpowiedniego miejsca na zapisanie pliku z bazą. Następnie pobieramy i zapamiętujemy obiekt typu ObjectContainer pochodzący z biblioteki db4o i za jego pomocą odbywa się dostęp do danych. Dalej mamy operacje tworzenia, modyfikacji, usuwania i pobierania wszystkich obiektów - nic specjalnego, ot proste wywołanie pojedynczych metod.

Trochę (ale tylko troszkę) ciekawsza jest metoda getByLogin(), która korzysta z query by example. W skrócie, ta metoda wykonywania zapytań, polega na utworzeniu najpierw obiektu, który będzie przykładem, a następnie wywołaniu odpowiedniej metody zwracającej wszystko, co do danego wzrorca pasuje. Domyślne wartości pól klasy są dopasowywane dowolnie, a te zmodyfikowane dokładnie. W tym przypadku zostaną zwrócone obiekty posiadające ustawione pole login na takie samo jak przekazane w argumencie metody. No prościej się chyba nie da. Istnieją w db4o jeszcze inne bardziej wyrafinowane metody zapytań, ale to nie temat na ten wpis.

Ludzie doświadczeniem z dużymi systemami i relacyjnymi bazami danych, widząc powyższy kod, zaraz zaczną krzyczeć: „a co z dostępem równoległym?”, „a gdzie początek i koniec transakcji?”, itd. Otóż w ogólności db4o wspiera ACID, a poziom izolacji transakcji ma ustawiony na Read Committed. Pytanie z mojej strony: czy rzeczywiście wszędzie tego potrzebujemy? Z tego, co mi wiadomo, to zdarzenia z aktywności w Androidzie są obsługiwane synchronicznie, tzn. w tym samym wątku co GUI. Jeśli nie tworzymy dodatkowych wątków do obsługi zdarzeń (ze względu na ich banalność i szybkość), to nie potrzebujemy transakcji.

Dobra, to teraz by się przydało jakieś GUI dorobić, aby można było zobaczyć jakiekolwiek efekty działania. Po uruchomieniu aplikacji będą się pojawiać dwa przyciski: Create New User i Login. Te będą kierować już do następnych ekranów. Tam będzie można utworzyć nowego użytkownika (i oczywiście go zapisać w bazie), albo się zalogować do aplikacji (na podstawie loginu i hasła zapisanego w bazie). Nie pokazuję tutaj jak utworzyć GUI, gdyż nie jest to tematem tego artykułu. Całkowity kod zamieszczam na końcu wpisu.

Przy pierwszej próbie uruchomienia tak skonstruowanej aplikacji dostaniemy błąd. Jeżeli nie zepsuliśmy niczego po drodze to problem jest jasny. Samo dołączenie biblioteki db4o do projektu nie spowoduje jej dołączenia w trakcie budowy projektu. Należy jeszcze dodać do build.xml następujący target (lub podobny):


    
        
    


Uważam, że środowisko programistyczne powinno być na tyle inteligentne, aby się troszczyło za nas takimi detalami. No ale cóż, nie można mieć wszystkiego. Teraz już powinno pójść gładko i jeśli nic nie popsuliśmy w XML’ach to powinniśmy zobaczyć naszą aplikację. Przykładowe screeny poniżej:



Pod koniec chcę zaznaczyć, ze NetBeans 6.9 nie jest idealnym środowiskiem do pisania aplikacji androidowych (może trzeba jeszcze poczekać?). W trakcie pracy nad projektem natrafiłem na sporo utrudnień. Przykładowo, podczas refaktoringu, po przeniesieniu klasy aktywności, aplikacja przestawała działać. Trzeba było wówczas w AndroidManifest.xml trochę pogrzebać i poprawić nazwy klas. Również zmiana nazwy pakietu wymuszała dodatkowe grzebanie w project.properties.

Z ciekawości sprawdziłem jeszcze jak sobie ze wsparciem Androida radzi IntelliJ IDEA (tak w końcu ją zainstalowałem :) ). Tutaj przeniesienie klasy do innego pakietu nie stanowi problemu. Podobnie z dodaniem biblioteki db4o do projektu. Co do zmiany nazwy pakietu to napiszę o tym w następnym poście (aby wszystko na raz nie było w jednym). Dodatkowo Idea od razu pokazuje Log konsolę z Androida. Chcą ją aktywować w NetBeans'ie trzeba kliknąć: Window -> Output -> ADB Log.

Jeszcze w między czasie tworzenia tego wpisu (nie powstał on w jeden dzień) pojawił się NetBeans 7.0. Tam jednak nawet nie mogłem dodać żadnej zewnętrznej biblioteki do projektu android'owego, więc ewentualna migracja do nowego środowiska musi poczekać.

Link do kodu:

db4oOnAndroid.zip

git://github.com/mstachniuk/db4oOnAndroid.git

//Update (25.09.2011)
Jak słusznie zauważył mój przyjaciel Michał, skoro otwieramy połączenie do bazy danych, to musimy je również zamknąć. Można to zrobić w MainActivity.java w przeciążając metodę onDestroy():

@Override
protected void onDestroy() {
    Db4oHelper db4oHelper = new Db4oHelper(this);
    db4oHelper.close();
    super.onDestroy();
}

poniedziałek, 2 maja 2011

Warunkowa kompilacja j2me w srodowisku NetBeans

Pisanie aplikacji na urządzenia mobilne w j2me nie jest prostą sprawą. Niby specyfikacja KVM (Kilobyte Virtual Machine) jest jedna i narzucona przez Oracle’a (dawniej Sun’a), ale konkretną implementację dostarcza producent danego telefonu komórkowego. Ponadto mamy sporo różnych modelów telefonów, różniących się od siebie wielkością ekranu, ułożeniem przycisków, szybkością procesora itd. Często przychodzi potrzeba dostarczenia aplikacji zoptymalizowanej pod konkretny telefon. Przedstawię poniżej w artykule jak można tego dokonać za pomocą warunkowej kompilacji w NetBeans’ie.

Warunkowa kompilacja to bardzo stary pomysł znany mi z czasów języka C (a może i był używany wcześniej). Polega ona na dodaniu do kodu źródłowego pewnych instrukcji, które zostaną wykonane jeszcze przed właściwą kompilacją. Spowoduje to wybranie odpowiednich fragmentów kodu, które zostaną dołączone do wynikowego programu. Projektanci języka Java całkowicie zrezygnowali z warunkowej kompilacji ze względu, że jest ona wrogiem numer 1 obiektowości. Gdy J2ME ujrzała światło dzienne i gdy coraz więcej telefonów zaczęło wspierać tą funkcjonalność, zrozumiano że jednak pewna możliwość konfigurowania kodu przed właściwą kompilacją jest niezbędna. I tak się narodziła warunkowa kompilacja w NetBeans'ie. Oczywiście wiem, że obecnie mamy XML’e, Dependency injection, Inversion of control i inne możliwości konfiguracji naszych aplikacji, ale nie mają one zastosowania w świecie j2me. Powstały co prawda projekty Signal Framework i Spring ME coś ala IoC dla j2me, ale jak dla mnie to sztuka dla sztuki i zastosowanie zerowe (i tak trzeba całość kompilować).

Dobra czas przejść do konkretów. Poniżej prezentuję fragment kodu, który używam w swojej aplikacji, z wykorzystaniem warunkowej kompilacji:

private void drawHelpMenu(Graphics g) {
//#if MobileTouchScreen
    helpSprite.setPosition(getWidth() - helpSprite.getWidth(),
            getHeight() - helpSprite.getHeight());
    helpSprite.paint(g);
//#else
    String  back = I18n.getText(I18n.LESSON_HELP);
    g.setFont(getSmallFont());
    g.drawString(back, getWidth(), getHeight(),
            Graphics.RIGHT | Graphics.BOTTOM);
//#endif
}

Interesujące fragmenty to te zaczynające się od //#. Są to instrunkcje preprocesora. Jak to działa? Gdy mamy ustawione ability MobileTouchScreen, to wówczas linijki 3-5 zostaną skompilowane, w przeciwnym wypadku linijki 7-10 zostaną wstawione w ciało metody drawHelpMenu(...). Prawda ze proste?

Teraz nadszedł czas na zarządzanie naszymi “zdolnościami” (chyba nienajlepsze tłumaczenie). Klikamy prawym przyciskiem myszy na nazwę projektu i wybieramy z menu opcję Properties. Tam w kategorii Abilities możemy zobaczyć jakie mamy ustawione właściwości dla obecnej konfiguracji naszego projektu. Właściwości mogą posiadać wartość, ale nie muszą.


Proponuję teraz utworzyć sobie nową konfigurację. Z pola Project Configuration wybieramy Add Configuration I wpisujemy nazwę nowej konfiguracji I następnie klikamy OK. Możemy tutaj jeszcze wybrać szablon, na podstawie którego zostanie utworzona konfiguracja.


Po kliknięciu OK wracamy do poprzedniego ekranu i teraz możemy modyfikować naszą konfigurację. Najpierw musimy odznaczyć pole Use Values from „DefaultConfiguration”. Dzieki temu przycisk Add staje się aktywny. Klikamy i w nowym oknie wpisujemy nasze ability MobileTouchScreen.


Klikamy OK i w oknie z właściwościami naszego projektu pojawiła się nowa pozycja w tabelce właściwościami danej konfiguracji.

Teraz chcąc skompilować projekt, używając nowej konfiguracji, należy ją wybrać z menu w górnej części okna.


Jak popatrzymy na nasz kod to zobaczymy, że NetBeans koloruje tło pod kodem nalezącym do warunkowej kompilacji:


Dodatkowo dodał komentarze na początku linii pomiędzy //#else a //#endif. Dzięki temu kod nie będzie dołączony do kompilowanej aplikacji. Zmiany te są zapisywane w pliku na dysku (polecam sprawdzić) i NetBeans nie zawsze chce się odświeżyć (w sensie widoku pliku źródłowego). Warto wtedy zamknąć i otworzyć na nowo plik, aby wiedzieć co edytować. Ponadto mechanizm ten zapewnia bezbolesny eksport do innego środowiska IDE, które nie wspiera tego mechanizmu. Całość zostanie potraktowana jako zwykły komentarz.

Z drugiej strony mechanizm może utrudniać merge’owanie i śledzenie zmian w kodzie. No chyba że wszyscy będą pamiętać, aby przed commit’em jedną, zawsze tą samą konfigurację ustawić.

Czy blok if else endif to wszystko co daje nam warunkowa kompilacja w NetBeans? Nie. Z ciekawszych opcji mamy jeszcze //#condition NAZWA_ABILITY i dzięki której decydujemy czy dany plik ma być dołączony do danego build’a, czy nie. Deklaracja musi być jednak umieszczona na początku pliku!

No dobra, mamy już skonfigurowany kod, ale co z innymi zasobami? Np, z grafiką, którą np. potrzebujemy tylko do wersji z obsługiwanym ekranem dotykowym? Mamy możliwość zdefiniowania zasobów, które mają się w takiej konfiguracji znaleźć. W tym celu przechodzimy do właściwości projektu i wybieramy Librares & Resources. Odznaczamy Use Values from „DefaultConfiguration” i już możemy dodawać katalogi, biblioteki i inne wymagane projekty.

Nie pozostaje teraz nic innego jak wziąć się do roboty i zacząć odpowiednio dostosowywać nasze mobilne aplikacje.

czwartek, 28 kwietnia 2011

Asocjacje plików w Eclipse

Mój poprzedni wpis Podświetlanie zmian kodu w Eclipsie, w którym pokazałem pewną wskazówkę ułatwiająca naszą codzienną pracę, był bardzo często czytany. Z portalu dworld.pl, który blogrolluje (nie wiem na ile to dobre słowo) ciekawe okołojavowe wpisy z całej polskiej blogosfery, miałem ponad 100 przekierowań. Byłem, więc w czołówce najpopularniejszych wpisów z ostatniego miesiąca. Podbudowany tym wydarzeniem postanowiłem pójść za ciosem i przedstawić kolejną przydatną (może nie koniecznie ciekawą) opcję ze środowiska Eclipse. Pomysł na to podrzucił mi kolega z pracy - Sebastian - któremu za pomoc z tego miejsca dziękuję. Ułatwiło to trochę moją codzienną pracę.

Często się zdarza, ze w naszej pracy mamy nie tylko do czynienia z kodem, ale także z grafiką. Nawet proste strony internetowe zawierają zawsze jakąś grafikę. Najłatwiej ją nam wyszukać i otworzyć do edycji z pomocą opcji Open Resource (Ctrl + Shift + R). Widzimy wtedy okienko jak poniżej:


Niestety chcąc dokonać edycji, nie zawsze chcemy użyć do tego domyślnej aplikacji z systemu operacyjnego. Wybranie opcji Open With i znalezienie żądanego programu, nie powoduje, że nasz wybór będzie zapamiętany. A szkoda.


Chcąc zmienić domyślny edytor, np. grafiki, należy wejść w menu Window -> Preferences -> General -> Editors -> File Associacions. Dodajemy interesujące nas rozszerzenie i aplikację, którą chcemy urzywać do edycji.


W ramach ciekawostki powiem jeszcze, że MS Paint jest umieszczony w katalogu Windows\System32, gdyby ktoś szukał. Ciekawe położenie. Wygląda na to, że narzędzie to jest bardzo ważne w systemie i na pewno jest mocno używane przez resztę Windowsowych kluczowych funkcjonalności. Kojarzę, że we wcześniejszych wersjach, było chyba trochę lepiej, tzn. gdzieś indziej leżał ten program. Degradacja architektury?

wtorek, 5 kwietnia 2011

Podświetlanie zmian kodu w Eclipsie

Jeśli chodzi o preferowane środowiska programistyczne, to nie jestem ani zagorzałym fanem Eclipse, ani NetBeans'a. Każde z nich ma swoje wady i zalety, każde sprawdza się inaczej w różnych zadaniach. Muszę w końcu na poważnie usiąść do poznawania IntelliJ IDEA i może wtedy sprawa ulubionego IDE się wyjaśni. Tymczasem chcę opisać pewną funkcjonalność, do której się przyzwyczaiłem w NetBeans'ie, a której mi trochę brakowało w Eclipsie. Pokażę również, jak można ją aktywować w tym drugim środowisku.

Funkcjonalność o której wspomniałem to zaznaczanie fragmentu kodu, który został zmodyfikowany (lub dodany). W NetBeans'ie wyglada to tak:


Po lewej stronie, między numeracją linii a kodem, widzimy dwa paski: niebieski i zielony. W Eclipsie trochę mi tego brakowało, ale ostatnio poszukując rozwiązania czegoś tam, przypadkiem trafiłem na potrzebną mi opcję. Podświetlanie zmian w kodzie można włączyć w Preferences w Quick Diff. Najlepiej śledzić zmiany w stosunku do repozytorium, w moim przypadku SVN'a.


Przyda się jeszcze tuning kolorów. Te z NetBeanas'a całkiem mi pasują. Zmiany są kolorowane na (RGB) 160 200 255 (#A0C8FF), a dodane linijki na (RGB) 150 255 150 (#96FF96). Wprowadzając te kolory do Eclipse, okazuje się, że są one trochę inne niż w NetBeans'ie (słabiej widoczne, zlewają się z szarym paskiem).


Nie wiem czemu, ale środowisko (Eclipse Galilieo) trochę inaczej wyświetla / interpretuje te kolory. Po kilku eksperymentach z wartościami RGB, doszedłem do zbliżonych kolorów. Zmiany należy ustawić na 18 119 255 (#1277FF), a dodane fragmenty na 0 255 0 (#00FF00). Efekt jest następujacy.


No już lepiej :) Co prawda, już na tym prostym przykładzie widać, że te dwa środowiska, w różny sposób interpretują wprowadzane zmiany, czasami trochę błędnie wyświetlając, co zostało zmienione, a co dodane. No ale trudno. Ważne, że teraz widać, w której części pliku się coś zmieniło i czy dane zmiany są nam dalej potrzebne. Mi to pomaga przeglądając zmiany przed commitem.

środa, 16 marca 2011

Parsowanie plików Excela

Często zachodzi w naszym świecie potrzeba parsowania plików Excela. Jak to powiedział Sławek Sobótka,  podczas prezentacji pt. „Domain Driven Design” – Wszystko ma swoje miejsce i wszystko jest na miejscu [video 1] [video 2] w ramach WrocławJUG: „[...] ludzie biznesu traktują Excela jako notatnik”. I rzeczywiście. Dla ludzi, którzy mają styczność na codzień w pracy z tabelkami, Excel jest wygodnym i niezastąpionym narzędziem. Warto więc zaznajomić się ze sposobem odczytu danych z arkuszów kalkulacyjnych. Nie będę opisywał jak odczytywać diagramy UML’owe zapisane w Excelu (jak to przedstawił Sławek), ale pokażę poniżej proste parsowanie arkuszy kalkulacyjnych.

Szukając biblioteki javovej obsługującej wspomniane akrkusze natrafiłem najpierw na Java Excel API. Gdzieś przeczytałem, że jest ona łatwiejsza w użyciu niż inne rozwiązania i zacząłem z niej korzystać. Niestety szybko się okazało, że biblioteka słabo radzi sobie z krzaczkami (obcojęzycznymi znakami) i kontynuowałem moje poszukiwania. Natrafiłem wówczas na Apache POI.

Apache POI jest biblioteką nie tylko potrafiącą współpracować z plikami Excela, ale również innymi dokumentami Microsoft Office’a. Chcąc odczytać coś z arkusza kalkulacyjnego, musimy najpierw utworzyć obiekt typu Workbook.

Workbook wb = new HSSFWorkbook(
    new POIFSFileSystem(new FileInputStream(filename)));

Mając już taki obiekt, możemy sprawdzić z ilu zakładek składa się nasz dokument (wb.getNumberOfSheets()), pobrać wszystkie obrazki dokumentu (getAllPictures()), czcionkę z danej komórki (getFontAt(...)) i sporo innych. Aby odczytać dane z konkretnej zakładki, należy ją najpierw pobrać:

Sheet sheet = wb.getSheetAt(...);

Teraz jak już mamy obiekt typu Sheet możemy iterować po danej zakładce w następujący sposób:

for (Row row : sheet) {
    for (Cell cell : row) {
        // …
    }
}

Teraz zostaje już tylko odczytać zawartość danej komórki:

String str = cell.getStringCellValue();

Poniżej prezentuję prosty kod metody wyświetlającą zawartość całego pliku:

public static void showAll(String filename) throws IOException {
    Workbook wb = new HSSFWorkbook(
        new POIFSFileSystem(new FileInputStream(filename)));
    for(int i=0; i < wb.getNumberOfSheets(); i++) {
        Sheet sheet = wb.getSheetAt(i);
        for (Row row : sheet) {
            for (Cell cell : row) {
                String str = cell.getStringCellValue();
                System.out.println(str);
            }
        }
    }
}

Do czego używam tej biblioteki? Na przykład do internacjonalizacji aplikacji. W pliku Excela przygotowuję sobie tłumaczenia używane w tworzonej aktualnie aplikacji. Następnie puszczam taki plik przez mój mały programik, która czyta odpowiednie kolumny i generuje pliki properties. Zgodnie z tym co kiedyś mówił Marek Berkan podczas prezentacji "Automatyczne generowanie kodu" na 4Developers 2010 warto jest jak najwięcej kodu generować automatycznie. Dlatego, po za samym plikiem properties, generuję sobie również plik javowy, zawierający zdefiniowane stałe, których mogę używać w kodzie, chcąc się dostać do danego tekstu. Dzięki temu jak nazwa jakiejś właściwości się zmieni, wygeneruje się inny kod i kompilator będzie krzyczał, gdy gdzieś jeszcze używam nieaktualnej stałej. Chroni mnie to przed błędami w aplikacjach związanymi z tłumaczeniami.

Oczywiście sposobów na wykorzystanie owej biblioeki jest multum, więc zachęcam do testowania.

czwartek, 24 lutego 2011

Hamburg wrażenia po miesięcznym pobycie


W ostatnim przebywam tymczasowo w Hamburgu (już miesiąc), więc już najwyższy czas aby swoje przemyślenia wyrazić, co mi się tutaj podoba, a co nie. To co na samym początku, zaraz po przyjeździe tutaj mnie zachwyciło, to możliwość legalnego, bezstresowego spożywania alkoholu w miejscach publicznych. Zaraz po zameldowaniu się w hotelu, poszedłem z kolegami na piwko, oczywiście w plener. I nawet pogoda nie przeszkadzała (przynajmniej mi), a był zimny styczeń w tym roku. Szczęściem naszym było, że znaleźliśmy otwarty sklep. Ogółem w Niemczech wszystko w Niedziele jest zamknięte. Wyjątkiem są piekarnie (zazwyczaj czynne od rana do popołudnia), muzea i inne instytucje, które muszą być otwarte. Nawet wielkie centra handlowe i budki z kebabem są pozamykane. Znaleźć sklepy czynny w Niedzielę, to naprawdę trzeba mieć szczęście. Muszę przyznać, że w Niemczech jest duża różnorodność jeśli chodzi o gatunki piwa. Jest w czym wybierać i smakują dobrze. Zazwyczaj dla każdej marki piwa są 3 gatunki: pszeniczne, porter i pils. A często jest ich więcej. Inną typową sprawą w Niemczech są drzwi do mieszkań w bloku. Nie montuje się tutaj klamek od strony zewnętrznej i aby otworzyć drzwi potrzebne są klucze. Biada temu, kto zapomni klucza wychodząc z mieszkania. Mi się raz prawie to zdarzyło (chciałem tylko światło na korytarzu zapalić), ale na szczęście zdążyłem „złapać” zamykające się drzwi. Inaczej było by po imprezie ;) Inną ciekawostką, jest pralka, która zazwyczaj znajduje się w piwnicy. Ja akurat mam małą kawalerkę i jeszcze to jakoś rozumiem, ale podobno tutaj to typowe. Dodatkowo, aby skorzystać z takiej pralki, trzeba wrzucić pieniążki do skrzynki doprowadzającej prąd. W moim przypadku jest to 1,5 Euro. Dodatkowo zazwyczaj w takiej pralni znajduje się jeszcze mechaniczna suszarka (również na pieniążki). Co ważne, to to aby wrócić do pralni przed końcem czasu wyświetlanego na urządzeniu połykającym monety. Ja za pierwszym razem tego nie wiedziałem i musiałem dodatkowe monety wrzucić, aby przywrócić zasilenie na chwilę i otworzyć drzwi od pralki. Rozumiem, że Niemcy są skąpi, że cię nie opłaca itd, ale takie rozwiązanie mi się niezbyt podoba. Kolejna sprawa to komunikacja miejska. W Hamburgu jest ona bardzo rozwinięta. Mamy tutaj U-Bahn, S-Bahn, A-Bahn (cokolwiek to jest), kolej regionalną, Metrobus(typowe autobusy), Schnellbus (szybkie połączenie z dzielnicami robotniczymi), Ellbus (dla terenów gdzie nie ma U-Bahn’a, czy S-Bahna), nocne autobusy no i ... statki miejskie. W końcu to miasto portowe (drugi w Europie pod względem wielkości port obsługujący kontenerowce). Niestety za taką sieć komunkacyjną trzeba sporo płacić. Miasto jest podzielone na sporo stref, które decydują o cenie przejazdu. Przykładowo bilet miesięczny na 2 strefy to 58 Euro. Dodatkowo trzeba mieć zdjęcie, aby taki bilet kupić. Warto o tym pamiętać przed przyjazdem tutaj. Ponadto w autobusach panuje dziwny zwyczaj / obowiązek. Codziennie po 21 i w przez całą Niedzielę, wchodzi się przednimi drzwiami i pokazuje bilet kierowcy. Czasami niektórzy wchodzą środkowymi drzwiami, ale jak będzie upierdliwy kierowca, to będzie przez mikrofon prosił aby podejść do przodu i pokazać przejazdówkę. W przeciwnym przypadku będzie stał na przystanku, aż do skutku. No i kierowcy są tutaj bardziej uprzejmi i zawsze czekają na pasażerów biegnących z daleka na autobus i otwierają im drzwi. Bilet jednonarazowy można również kupić u kierowcy. Spodobał mi się ich sprzęt. Mają niewielki terminal, który drukuje bilet i fajny automat do wydawania reszty. Są na nim przyciski, które po naciśnięciu wypluwają daną monetę do „kubeczka”, z którego bardzo łatwo i wygodnie można zabrać drobne. Również informacja, o tym gdzie autobus jedzie i gdzie się zatrzymuje jest łatwo dostępna w autobusie. Przy ważniejszych przystankach, po za słownym komunikatem po Niemiecku, jest on również po Angielsku. No i oczywiście są wyświetlacze diodowe, wypisujące nazwę następnego przystanku. Nie lecą tam przesuwające się reklamy tekstowe jak np. we Wrocławiu. Również na większości przystanków są zamontowane tablice, informujące za ile minut będzie autobus / metro. Bardzo dobre rozwiązanie i co najważniejsze działa. Nie jest to zwykły plan rozkładu jazdy, a dokładna informacja co za jaki czas przyjedzie. Nie zdażyło mi się zauważyć, aby to błędnie funkcjonowało. Kiedy doczekamy się czegoś takiego w Polsce? Nie mam pojęcia. Standard komunikacyjny jest więc tutaj wysoki. Dodatkowo niedawno instytucja zajmująca się transportem miejskim, wprowadziła ciekawą „usługę”. Jeżeli transport spóźni się więcej niż 20 minut, to oddają połowę ceny biletu. Świadczy to o wysokim standardzie tych usług. Podobna promocja w Polsce mogła by zrujnować komunikację miejską w większych miastach. Dla tych którzy nie lubią komunikacji miejskiej, jest w Hamburgu ciekawa alternatywa. Otóż w wielu punktach miasta, są stojaki z rowerami. Po wcześniejszym zarejestrowaniu się, można taki rower wypożyczyć, przejechać potrzebny kawałek i oddać go na innym „przystanku”. Podobne rozwiązanie chce wprowadzić Wrocław. Tutaj to się sprawdza i ludzie korzystają z tego. No i pierwsze pół godziny gratis :) Jest tu sporo ścieżek rowerowych, albo wydzielonych pasów na ulicy, więc jest gdzie jeździć. Co zauważyłem, to sporo osób tutaj biega dla sportu, zwłaszcza w okolicach Alster. Aż głupio mi było tamtędy ostatnio iść. Czułem się jak jakbym biegał na autostradzie pomiędzy samochodami. Największe wrażenie zrobił na mnie mężczyzna, biegający razem z wózkiem dziecięcym (taka wyższa forma spaceru). W Hamburgu jest sporo mostów. Kiedyś myślałem, ze we Wrocławiu jest dużo mostów (220 wg Wikipedii). A tu jest 2500 mostów (również wg. Wikipedii)! Tak, to więcej niż w Wenecji, Amsterdamie i Londynie razem wziętych. Naprawdę, zazwyczaj z każdego mostu widać kilka następnych i czasem są kilka metrów od siebie. Co mnie jeszcze tutaj zaskoczyło, to spora liczba b-boy’i tańczących na ulicach, zbierając przy tym drobne. Poniżej linki do YouTube’a: B-boys aus Hamburg prt. 1 B-boys aus Hamburg prt. 2 Hamburg dance show Po za b-boy’ami spotkałem na ulicy jeszcze raperów, sprzedawających swoją muzykę. Po prostu szedłem sobie ulicą i ktoś się mnie pytał czy słucham hip-hopu. Odpowiadałem że tak i w tym momencie dawali mi swoje słuchawki i puszczali swój kawałek do posłuchania. Ich płyty można również kupić w normalnych sklepach, ale widocznie nie mają dużego popytu, skoro dodatkowo rozprowadzają je tą metodą (trochę jak nielegale). Co ważne są one po niższej cenie niż w sklepie. Jedna taką płytę kupiłem i dzięki temu mam rzadką okazję posłuchania rapu z Gujany. Odnośnie zakupów, to warto sprawdzić jaką ma się kartę płatniczą, przed przyjazdem tutaj. Otóż karty Visa, nie są akceptowane we wszystkich sklepach. Pytałem jednego Niemca dlaczego, to twierdził, ze to ze względów bezpieczeństwa. Na karcie Visa (i paru innych) widnieje numer, za pomocą którego można dokonywać zakupów, jak kartą kredytową. No i jak się płaci w sklepie to ktoś może zapamiętać ten numer i użyć w niepowołanym celu. W sumie to dobre myślenie. Mi ogółem cały ten pomysł robienia zakupów jedynie za pomocą numeru karty kredytowej bez dodatkowej autoryzacji się nie podoba, ale to już osobny temat. Upewnijcie się więc najpierw, czy w danym sklepie możecie płacić swoją kartą. Na koniec jeszcze mała ciekawostka. Jest piosenka o tym, jak w Hamburgu mówi się na „do widzenia”: In Hamburg sagt man Tschüss. O ile „Tschüss”, mówione na pożegnanie, jest ogólnie znane i używane w całych Niemczech, tak na przywitanie mówi się tutaj „Moin”. Jest to tutaj bardzo popularna forma przywitania, ładnie to brzmi, a w szkołach tego nie uczą. A szkoda.

sobota, 19 lutego 2011

Kilka porad na temat MIDP Visual Design w Netbeans'ie

Dzisiaj chciałbym opowiedzieć o kilku sztuczkach, w MIDP Visual Design, który jest dostępny w Netbeans’ie. Tak wiem, że technologia J2ME (czy też JME) jest już trochę przestarzała, gdyż teraz mamy Androidy iPhone’y, BlackBery, Windows 7 i inne ciekawsze cuda na kiju ;) Zamierzam jednak jeszcze kilka postów o j2me na blogu opublikować, ot tak dla potomności.

Z opcji MIDP Visual Design możemy skorzystać tworząc nowy projekt z grupy „Java ME” typu „Mobile Application” w NetBeans’ie. Wystarczy nie odznaczać (gdyż domyślnie jest zaznaczony) CheckBox’a „Create Hello MIDlet”.


Można również do istniejącego projektu dodać Visual MIDlet i uzyskamy podobny efekt.

Narzędzie pozwala nam w prosty sposób zbudować menu użytkownika, przechodzenie pomiędzy ekranami itp. Mamy ładny podgląd gdzie z danego miejsca możemy się przemieścić, czyli widzimy przepływ aplikacji (menu). Należy pamiętać, że generowane menu oparte jest na komponentach wysokopoziomowych (tych dziedziczących z klasy Screen). Przykładowy screen jak może taki przepływ wyglądać poniżej.


Oczywiście w ten sposób nie zrobimy całej aplikacji. Mamy możliwość dodawania kodu w pewnych miejscach i większość rzeczy można wyklikać. Generalnie narzędzie świetnie się nadaje do prototypowania, gdy na szybko trzeba coś pokazać. Nie widzę jednak przeciw wskazań, do użycia tego również na produkcji. Generowany kod jest całkiem dobry. Poniżej kilka wskazówek, związanych z tym oto narzędziem:

1. Możemy wybrać w jaki sposób poszczególne komponenty są inicjowane. Po wybraniu danego Screena w oknie Properties widzimy dostępne opcje. Po za zmianą wyświetlanego tekstu i innych właściwości, możemy wybrać czy mają być Lazy Initialized czy nie.


Proponuję każdemu sprawdzić, jaki kod zostaje wygenerowany przy zmianie tego komponentu. W małych aplikacjach może to nie mieć wielkiego znaczenia, czy komponent jest lazy czy nie, ale te duże / ciężkie komponenty (lub rzadko używane) powinny być leniwie inicjowane. Oczywiście część komponentów może być inicjowana tak, a druga inaczej.

2. Możemy również zmienić sposób generowania logiki, odpowiedzialnej za przechodzenie pomiędzy kolejnymi ekranami. Domyślnie to, która opcja z menu została wybrana, sprawdzane jest na podstawie etykiety (label’ki) przypisanej do danego elementu ekranu. Przykład poniżej:

public void mainMenuAction() {
    // enter pre-action user code here
    String __selectedString = getMainMenu().getString(getMainMenu().getSelectedIndex());
    if (__selectedString != null) {
        if (__selectedString.equals(“Etykieta”)) {
            // ...

Włączając opcję “Index Based Switch” powyższy kod zostanie przekształcony w coś takiego:

public void mainMenuAction() {
    // enter pre-action user code here
    switch (getMainMenu().getSelectedIndex()) {
        case 0:
            // ...

Zachowanie to może mieć znaczenie wówczas, gdy chcemy wprowadzić internacjonalizację do naszej aplikacji i podmianę tekstów na elementach „w locie”. W drugim przypadku logika aplikacji w żaden sposób nie zależy od przypisanej etykiety w danym komponencie, a jedynie na jego położeniu. Po takim zabiegu podmiana tekstów jest prostsza.

3. Odnośnie internacjonalizacji, to w większości kontrolek mamy możliwość podmiany wyświetlanego na niej tekstu (dziedziczymy z klasy Displayable metodę setTitle()). Problem jest jednak z obiektami typu Command, które są immutable, gdyż nie posiadają metod modyfikujących ich stan. Można za to skorzystać z następującej sztuczki: usunąć z danego ekranu istniejące obiekty Command, utworzyć je na nowo i podpiąć. Przykład poniżej:

getSettingsMenuList().removeCommand(getBackCommand());
backCommand = null;
getSettingsMenuList().addCommand(getBackCommand ());

W pierwszej linii usuwamy obecną powiązaną komendę z danym ekranem. W drugiej linii zerujemy referencję do obiektu zwracanego przed getBackCommand(). Na koniec tworzymy obiekt na nowo (wywolanie getBackCommand()) i przypisujemy nową komendę do żądanego elementu. Korzystamy tutaj z faktu, że dana komenda jest leniwie inicjowana i po wejściu w metodę  getBackCommand(), zostanie ona na nowo utworzona.

4. Generalnie często początkujący programiści zderzający się z MIDP Visual Design wkurzają się, że nie mogą tu wszystkiego zrobić czego chcą. Według mnie wszystko da się wyklikać (w zakresie oferowanym przez edytor), ewentualnie dopisując niewielkie kawałki kodu w miejscach, w których edytor nam na to pozwala. Na prawdę nie znalazłem niczego, w czym by edytor mnie ograniczał.

Jednakże chcąc się pozbyć wsparcia ze strony MIDP Visual Design można wykonać pewien trick. Usuwając z dysku plik .vmd i wszystkie komentarze, generowane przez edytor, z pliku .java. Oczywiście musimy zrobić to w innym edytorze tekstowym, a nie pod NetBeans’em. Nie będziemy mogli również wrócić do poprzedniego stanu, ani więcej zobaczyć przepływu aplikacji dla naszego kodu.

Usunięcie komentarzy (przynajmniej część) z pliku z kodem, jest równie ważne jak usunięcie pliku *.vmd. W przypadku usunięcia tylko tego pliku możemy napotkać na problemy z otwarciem projektu pod NetBeans’em.

Poniżej początki komentarzy dodawanych przez NetBeans’a:

//<editor-fold
//</editor-fold
//GEN-LINE
//GEN-BEGIN
//GEN-END

To tyle na dzisiaj. Zachęcam do korzystania z tego edytora. Jak było coś niejasne to piszcie!