Tematem projektu jest aplikacja działająca na systemie mobilnym Android, która pozwoli użytkownikowi na odnalezienie najbliżej znajdujących się przystanków komunikacji miejskiej, zlokalizowanie siebie oraz przystanku na mapie oraz sprawdzenie rozkładu jazdy komunikacji miejskiej. W odróżnieniu od jakdojade.pl czy aplikacji Transportoid nie chcemy wyznaczać połączeń, które umożliwiają dostanie się do zamierzonej lokacji, a pomoc zagubionym mieszkańcom i turystom w znalezieniu najbliższego węzła komunikacji miejskiej.
Naszym celem jest zapoznanie się przy tworzeniu projektu z nowoczesnymi technologami związanymi z platformą Android oraz posiadanie pierwszej aplikacji w Android Market.
Przy implementacji aplikacji zostanie użyty język Java oraz Android SDK, a kluczowymi elementami będzie wykorzystanie GPS oraz Google API. Natomiast implementacja samej bazy danych zostanie wykonana w SQLite. Dystrybucja oraz aktualizowanie rozkładów jazdy w bazie będzie dokonywane za pomocą Android Market.
Istnieją trzy możliwości użycia aplikacji:
Poniżej opisane zostają wzorce, które zostały użyte projekcie w celu zoptymalizowania jej działania, głównie pod kątem bazy danych i operacji na niej.
Wzorzec ten polega jak największym opóźnieniu utworzeniu obiektów aż do momentu zapotrzebowania na dany obiekt. Ma to na celu ograniczenie liczby zapytań kierowanych do bazy danych co powoduje jej odciążenie i zmniejszenie zużycia zasobów co przekłada się na zminimalizowanie zapotrzebowania na energie co w przypadku aplikacji mobilnych jest priorytetową sprawą. W projekcie potrzebne linie i przystanki tworzone są w 2 przypadkach
Zatem żeby odciążyć bazę obiekty opakowujące przystanki i linie są wywoływane tylko wtedy gdy znajdują się one na ekranie mapy bądź zaczynają się na daną literę bądź cyfrę wpisaną w wyszukiwarce. Po zainicjalizowaniu przechowywane są na liście stworzonych obiektów. Gdy zajdzie potrzeba ponownego ich wykorzystania program sprawdzając listę zobaczy, że obiekty były już utworzone i nie wywoła zapytania do bazy co odciąży zasoby i przyspieszy aplikację.
Z reguły w przypadku użycia wzorca leniwej inicjalizacji korzysta się również z drugiego wzorca - mapowania danych. Opisuje on tabele zawarte w bazie danych w postaci obiektów języka Java prezentując odpowiednie wiersze w postaci pól. Dodatkowo klasy te zawierają w sobie pomocnicze metody do przeszukiwania bazy z poziomu encji takie jak wyszukiwanie przystanków dla danego rejonu bądź linii na daną literę alfabetu. Przez zastosowanie tego wzorca ułatwiamy programiście dostęp do danych.
Ostatnim użyty przez nas wzorzec naturalnie wypływa z używania technologii androidowych. Ponieważ bazę i zapytania do niej konstruujemy za pomocą DatabaseAdaptera to programista nie jest zmuszony do bezpośredniego wpisywania kwerend SQLowych, a używa funkcji w języku Java, które je obudowują. Przyczynia się to do zwiększenia przejrzystości kodu i łatwiejszego rozszerzania aplikacji.
// STOPS public static final String PK_KEY_STOPID = "stop_id"; public static final String STOPID_OPTIONS = "INTEGER PRIMARY KEY AUTOINCREMENT"; public static final String KEY_LOCALISATIONX = "localisationX"; public static final String LOCALISATIONX_OPTIONS = "REAL NOT NULL"; public static final String KEY_LOCALISATIONY = "localisationy"; public static final String LOCALISATIONY_OPTIONS = "REAL NOT NULL"; public static final String KEY_NAME = "name"; public static final String NAME_OPTIONS = "TEXT NOT NULL"; // LINES public static final String PK_KEY_LINEID = "line_id"; public static final String LINEID_OPTIONS = "INTEGER PRIMARY KEY AUTOINCREMENT"; public static final String KEY_NUMBER = "number"; public static final String NUMBER_OPTIONS = "INTEGER NOT NULL"; public static final String FK_KEY_FROMID = "from_id"; public static final String FROMID_OPTIONS = "INTEGER NOT NULL"; public static final String FK_KEY_DESTINATIONID = "destination_id"; public static final String DESTINATIONID_OPTIONS = "INTEGER NOT NULL"; // CONNECTIONS public static final String FK_KEY_STOPID = "stop_id"; public static final String STOPID_FK_OPTIONS = "INTEGER NOT NULL"; public static final String FK_KEY_LINEID = "line_id"; public static final String LINEID_FK_OPTIONS = "INTEGER NOT NULL"; public static final String KEY_ORDERID = "order_id"; public static final String ORDERID_OPTIONS = "INTEGER NOT NULL"; public static final String KEY_DAY = "day"; public static final String DAY_OPTIONS = "TEXT NOT NULL"; public static final String KEY_HOUR = "hour"; public static final String HOUR_OPTIONS = "INTEGER NOT NULL"; public static final String KEY_MINUTE = "minute"; public static final String MINUTE_OPTIONS = "INTEGER NOT NULL"; public static final String KEY_LEGEND = "legend"; public static final String LEGEND_OPTIONS = "TEXT"; // CREATE STOPS private static final String CREATE_STOPS = "create table " + TABLE_STOPS + " (" + PK_KEY_STOPID + " " + STOPID_OPTIONS + ", " + KEY_LOCALISATIONX + " " + LOCALISATIONX_OPTIONS + ", " + KEY_LOCALISATIONY + " " + LOCALISATIONY_OPTIONS + ", " + KEY_NAME + " " + NAME_OPTIONS + ");"; // CREATE LINES private static final String CREATE_LINES = "create table " + TABLE_LINES + " (" + PK_KEY_LINEID + " " + LINEID_OPTIONS + ", " + KEY_NUMBER + " " + NUMBER_OPTIONS + ", " + FK_KEY_FROMID + " " + FROMID_OPTIONS + ", " + FK_KEY_DESTINATIONID + " " + DESTINATIONID_OPTIONS + ", " + "FOREIGN KEY(" + FK_KEY_FROMID + ") REFERENCES " + TABLE_STOPS + "(" + PK_KEY_STOPID + "), " + "FOREIGN KEY(" + FK_KEY_DESTINATIONID + ") REFERENCES " + TABLE_STOPS + "(" + PK_KEY_STOPID + ")" + ");"; // CREATE CONNECTIONS private static final String CREATE_CONNECTIONS = "create table " + TABLE_CONNECTIONS + " (" + FK_KEY_STOPID + " " + STOPID_FK_OPTIONS + ", " + FK_KEY_LINEID + " " + LINEID_FK_OPTIONS + ", " + KEY_ORDERID + " " + ORDERID_OPTIONS + ", " + KEY_DAY + " " + DAY_OPTIONS + ", " + KEY_HOUR + " " + HOUR_OPTIONS + ", " + KEY_MINUTE + " " + MINUTE_OPTIONS + ", " + KEY_LEGEND + " " + LEGEND_OPTIONS + ", " + "FOREIGN KEY(" + FK_KEY_STOPID + ") REFERENCES " + TABLE_STOPS + "(" + PK_KEY_STOPID + "), " + "FOREIGN KEY(" + FK_KEY_LINEID + ") REFERENCES " + TABLE_LINES + "(" + PK_KEY_LINEID + ")" + ");";
W języku SQL kwerendy te wyglądją następująco:
CREATE TABLE stops ( stop_id INTEGER PRIMARY KEY AUTOINCREMENT, localisationX REAL NOT NULL, localisationy REAL NOT NULL, name TEXT NOT NULL); CREATE TABLE lines ( line_id INTEGER PRIMARY KEY AUTOINCREMENT, number INTEGER NOT NULL, from INTEGER NOT NULL, destination INTEGER NOT NULL, FOREIGN KEY(from) REFERENCES stops(stop_id), FOREIGN KEY(destination) REFERENCES stops(stop_id)); CREATE TABLE connections ( stop_id INTEGER NOT NULL, line_id INTEGER NOT NULL, order_id INTEGER NOT NULL, day TEXT NOT NULL, hour INTEGER NOT NULL, minute INTEGER NOT NULL, legend TEXT, FOREIGN KEY(stop_id) REFERENCES stops(stop_id), FOREIGN KEY(line_id) REFERENCES lines(line_id));
Baza danych spełnia 1NF ponieważ każda składowa w każdej kropce jest atomowa i nie da się jej podzielić.
Baza danych spełnia 2NF ponieważ spełnia 1NF oraz każdy element jest zależnie funkcyjny od kluczy poszczególnych tabeli (jedyną tabelą, która nie posiada klucza głównego jest tabela connections, jest ona tabelą asocjacyjną i nie posiada niekluczowych elementów).
Baza danych spełnie 3NF ponieważ spełnia 2NF oraz każdy atrybut jest bezpośrednio zależny od klucza głównego w poszczególnych tabelach.
W projekcie operacje na danych przeprowadzane są dwupoziomowo. Budowa bezpośrednich zapytań jest funkcjonalnością klasy DatabaseAdapter natomiast wstępne formowanie argumentów jest zadaniem odpowiednich klas obudowujących - Stop, Line i Connection.
public Cursor getAllStops() { String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, KEY_LOCALISATIONY, KEY_NAME }; return _database.query(TABLE_STOPS, columns, null, null, null, null, null); } public Cursor getAllLines() { String[] columns = { PK_KEY_LINEID, KEY_NUMBER, FK_KEY_FROMID, FK_KEY_DESTINATIONID }; return _database.query(TABLE_LINES, columns, null, null, null, null, null); } public Cursor getAllConnections() { String[] columns = { FK_KEY_STOPID, FK_KEY_LINEID, KEY_ORDERID, KEY_DAY, KEY_HOUR, KEY_MINUTE, KEY_LEGEND }; return _database.query(TABLE_CONNECTIONS, columns, null, null, null, null, null); } public Cursor getStop(int stopId) { String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, KEY_LOCALISATIONY, KEY_NAME }; String where = PK_KEY_STOPID + "=" + stopId; Cursor cursor = _database.query(true, TABLE_STOPS, columns, where, null, null, null, null, null); return cursor; } public Cursor getStop(String name) { String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, KEY_LOCALISATIONY, KEY_NAME }; String where = KEY_NAME + "= '" + name + "'"; Cursor cursor = _database.query(true, TABLE_STOPS, columns, where, null, null, null, null, null); return cursor; } public Cursor getStopsExcept(Area area, List<Integer> stopIds) { String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, KEY_LOCALISATIONY, KEY_NAME }; String where = area.getLeft().toString() + " < " + KEY_LOCALISATIONX + " AND " + KEY_LOCALISATIONX + " < " + area.getRight().toString() + " AND " + area.getBottom().toString() + " < " + KEY_LOCALISATIONY + " AND " + KEY_LOCALISATIONY + " < " + area.getTop().toString(); if (!stopIds.isEmpty()) { where = where + " AND " + PK_KEY_STOPID + " NOT IN ("; for (Integer id : stopIds) { if (!where.endsWith("(")) { where = where + ", "; } where = where + id.toString(); } where = where + ")"; } Cursor cursor = _database.query(true, TABLE_STOPS, columns, where, null, null, null, null, null); return cursor; } public Cursor getLine(int lineId) { String[] columns = { PK_KEY_LINEID, KEY_NUMBER, FK_KEY_FROMID, FK_KEY_DESTINATIONID }; String where = PK_KEY_LINEID + " = " + lineId; Cursor cursor = _database.query(true, TABLE_LINES, columns, where, null, null, null, null, null); return cursor; } public Cursor getConnection(int stopId, int lineId) { String[] columns = { FK_KEY_STOPID, FK_KEY_LINEID, KEY_ORDERID, KEY_DAY, KEY_HOUR, KEY_MINUTE, KEY_LEGEND }; String where = FK_KEY_STOPID + "=" + stopId + " AND " + FK_KEY_LINEID + "=" + lineId; Cursor cursor = _database.query(TABLE_CONNECTIONS, columns, where, null, null, null, null); return cursor; } public Cursor getStops(Character letter, List<Integer> ids) { String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, KEY_LOCALISATIONY, KEY_NAME }; String where = KEY_NAME + " LIKE '" + Character.toUpperCase(letter) + "%'"; if (ids.size() > 0) { where += PK_KEY_STOPID + " IS NOT IN ("; for(Integer i : ids) { if(! where.endsWith("(")) { where += ", "; } where += i; } where += ")"; } return _database.query(TABLE_STOPS, columns, where, null, null, null, KEY_NAME); } public Cursor getLinesByStopId(int stopId) { String[] columns = {FK_KEY_LINEID}; String where = FK_KEY_STOPID + "=" + stopId; return _database.query(true ,TABLE_CONNECTIONS, columns, where, null, null, null, KEY_ORDERID, null); } public Cursor getStopsByLineId(int lineId) { String[] columns = {FK_KEY_STOPID}; String where = FK_KEY_LINEID + "=" + lineId; return _database.query(true ,TABLE_CONNECTIONS, columns, where, null, null, null, KEY_ORDERID, null); } public Cursor getLinesByLetter(Character letter) { String[] columns = { PK_KEY_LINEID, KEY_NUMBER, FK_KEY_FROMID, FK_KEY_DESTINATIONID }; String where = "CAST(" + KEY_NUMBER + " AS TEXT) LIKE '" + letter + "%'"; return _database.query(TABLE_LINES, columns, where, null, null, null, null); }
private static Stop getStopFromCache(final int id) { for (Stop stop : _stopList) { if (stop.get_id() == id) return stop; } return null; } public static Stop getStopFromCache(String name) { for (Stop stop : _stopList) { if (stop.getName() == name) return stop; } return null; } private static Stop getStopFromCursor(final Cursor cursor) { final int id = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.PK_KEY_STOPID)); final Pair<Double, Double> coordinates = new Pair<Double, Double>( (double) cursor.getFloat(cursor .getColumnIndex(DatabaseAdapter.KEY_LOCALISATIONY)), (double) cursor.getFloat(cursor .getColumnIndex(DatabaseAdapter.KEY_LOCALISATIONX))); final String name = cursor.getString(cursor .getColumnIndex(DatabaseAdapter.KEY_NAME)); return new Stop(id, coordinates, name); } private static List<Stop> changeCursorInToList(Cursor cursor) { final List<Stop> stops = new ArrayList<Stop>(); if (cursor != null && cursor.moveToFirst()) while (true) { stops.add(getStopFromCursor(cursor)); if (!cursor.moveToNext()) break; } return stops; } private static Stop getStopFromDB(final int id, final DatabaseAdapter dba) { final Cursor cursor = dba.getStop(id); Stop result = null; if (cursor.moveToFirst()) result = getStopFromCursor(cursor); cursor.close(); return result; } private static Stop getStopFromDB(final String name, final DatabaseAdapter dba) { final Cursor cursor = dba.getStop(name); Stop result = null; if (cursor.moveToFirst()) result = getStopFromCursor(cursor); cursor.close(); return result; } public static Stop getStop(int id, DatabaseAdapter dba) { Stop stop = getStopFromCache(id); if (stop == null) { stop = getStopFromDB(id, dba); if (stop != null) { _stopList.add(stop); } } return stop; } public static Stop getStop(String name, DatabaseAdapter dba) { Stop stop = getStopFromCache(name); if (stop == null) { stop = getStopFromDB(name, dba); if (stop != null) { _stopList.add(stop); } } return stop; } public static Stop getStop(final Pair<Double, Double> coordination, final DatabaseAdapter dba) { Double radius = BASE_RADIUS; Area area = null; Stop stop = null; while (stop == null || radius < 5 * BASE_RADIUS) { Pair<Double, Double> topLeft = new Pair<Double, Double>( coordination.first + radius, coordination.second - radius); Pair<Double, Double> bottomRight = new Pair<Double, Double>( coordination.first - radius, coordination.second + radius); area = new Rectangle(topLeft, bottomRight); final List<Stop> stopsFromDB = getStops(area, dba); _stopList.addAll(stopsFromDB); stop = getStopFromCache(coordination, radius); radius = radius + BASE_RADIUS; } return stop; } private static Stop getStopFromCache( final Pair<Double, Double> coordination, final Double radius) { Pair<Double, Stop> result = new Pair<Double, Stop>(Double.MAX_VALUE, null); for (Stop stop : _stopList) { Double distance = Math.sqrt(Math.pow(stop.get_coordinates().first - coordination.first, 2) + Math.pow(stop.get_coordinates().second - coordination.second, 2)); if (distance < radius && distance < result.first) { result = new Pair<Double, Stop>(distance, stop); } } return result.second; } private static List<Stop> getStopsFromCache(final Area area) { final List<Stop> stopsInArea = new ArrayList<Stop>(); for (Stop stop : _stopList) { if (area.isPointInArea(stop.get_coordinates())) stopsInArea.add(stop); } return stopsInArea; } private static List<Stop> getStopsFromCache(final String name) { final List<Stop> stopsByLetter = new ArrayList<Stop>(); for (Stop stop : _stopList) { if (stop.getName().toUpperCase().startsWith(name.toUpperCase())) stopsByLetter.add(stop); } return stopsByLetter; } private static List<Stop> getStopsFromDB(final Area area, final DatabaseAdapter dba) { final List<Integer> except = new ArrayList<Integer>(); final List<Stop> stopsInArea = getStopsFromCache(area); for (Stop stop : stopsInArea) except.add(stop.get_id()); Cursor cursor = dba.getStopsExcept(area, except); final List<Stop> result = changeCursorInToList(cursor); cursor.close(); return result; } private static int getStopIdFromCursor(final Cursor cursor) { return (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.FK_KEY_STOPID)); } private static List<Stop> getStopsByLineIdFromDB(final int lineId, final DatabaseAdapter dba) { final List<Stop> stops = new ArrayList<Stop>(); final Cursor cursor = dba.getStopsByLineId(lineId); if (cursor != null && cursor.moveToFirst()) while (true) { Stop result = getStop(getStopIdFromCursor(cursor), dba); stops.add(result); if (!cursor.moveToNext()) break; } cursor.close(); return stops; } private static List<Stop> getStopsFromDB(final Character letter, final DatabaseAdapter dba) { List<Integer> intsList = new ArrayList<Integer>(); List<Stop> stopsList = getStopsFromCache(letter.toString().toUpperCase()); for(Stop s : stopsList) { intsList.add(s.get_id()); } Cursor cursor = dba.getStops(letter, intsList); List<Stop> sl = changeCursorInToList(cursor); cursor.close(); return sl; } public static List<Stop> getStops(final Area area, final DatabaseAdapter dba) { if (area != null) { final List<Stop> stops = getStopsFromDB(area, dba); _stopList.addAll(stops); } return getStopsFromCache(area); } public static List<Stop> getStops(final String name, final DatabaseAdapter dba) { Character letter = null; if (name.length() > 0) { letter = name.toUpperCase().charAt(0); } if (letter == null) { return null; } if (!_letters.contains(letter)) { final List<Stop> stops = getStopsFromDB(letter, dba); _stopList.addAll(stops); _letters.add(letter); } return getStopsFromCache(name); } public static List<Stop> getStopsByLineId(int lineId, DatabaseAdapter dba) { return getStopsByLineIdFromDB(lineId, dba); }
private static Line getLineFromCache(final int id) { for (Line line : _lineList) { if (line.get_id() == id) return line; } return null; } private static List<Line> getLinesFromCache(final String name) { final List<Line> linesByLetter = new ArrayList<Line>(); for (Line line : _lineList) { if (new Integer(line.getNumber()).toString().toUpperCase() .startsWith(name.toUpperCase())) linesByLetter.add(line); } return linesByLetter; } private static Line getLineFromCursor(final Cursor cursor) { final int id = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.PK_KEY_LINEID)); final int number = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.KEY_NUMBER)); final int from = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.FK_KEY_FROMID)); final int destination = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.FK_KEY_DESTINATIONID)); return new Line(id, number, from, destination); } private static int getLineIdFromCursor(final Cursor cursor) { return (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.FK_KEY_LINEID)); } private static List<Line> getLineByStopIdFromDB(final int stopId, final DatabaseAdapter dba) { final List<Line> lines = new ArrayList<Line>(); final Cursor cursor = dba.getLinesByStopId(stopId); if (cursor != null && cursor.moveToFirst()) while (true) { Line result = getLine(getLineIdFromCursor(cursor), dba); lines.add(result); if (!cursor.moveToNext()) break; } cursor.close(); return lines; } private static Line getLineFromDB(final int id, final DatabaseAdapter dba) { final Cursor cursor = dba.getLine(id); Line result = null; if (cursor.moveToFirst()) result = getLineFromCursor(cursor); cursor.close(); return result; } private static List<Line> changeCursorInToList(Cursor cursor) { final List<Line> lines = new ArrayList<Line>(); if (cursor != null && cursor.moveToFirst()) while (true) { lines.add(getLineFromCursor(cursor)); if (!cursor.moveToNext()) break; } return lines; } private static List<Line> getLinesFromDB(final Character letter, final DatabaseAdapter dba) { List<Integer> intsList = new ArrayList<Integer>(); List<Line> linesList = getLinesFromCache(letter.toString().toUpperCase()); for(Line s : linesList) { intsList.add(s.get_id()); } Cursor cursor = dba.getLinesByLetter(letter); List<Line> ll = changeCursorInToList(cursor); cursor.close(); return ll; } public static Line getLine(int id, DatabaseAdapter dba) { Line line = getLineFromCache(id); if (line == null) { line = getLineFromDB(id, dba); if (line != null) { _lineList.add(line); } } return line; } public static List<Line> getLines(final String name, final DatabaseAdapter dba) { Character letter = null; if (name.length() > 0) { letter = name.toUpperCase().charAt(0); } if (letter == null) { return null; } if (!_letters.contains(letter)) { final List<Line> stops = getLinesFromDB(letter, dba); _lineList.addAll(stops); _letters.add(letter); } return getLinesFromCache(name); } public static List<Line> getLinesByStopId(int stopId, DatabaseAdapter dba) { return getLineByStopIdFromDB(stopId, dba); }
private static Connection getConnectionFromCache(final int stopId, final int lineId) { for (Connection conn : _connectionList) { if (conn.get_stopId() == stopId && conn.get_lineId() == lineId) return conn; } return null; } private void addTime(Pair<String, Pair<Integer, Integer>> time) { if (_TimeTable.containsKey(time.first)) { List<Pair<Integer, Integer>> timeList = _TimeTable.get(time.first); timeList.add(time.second); _TimeTable.put(time.first, timeList); } else { List<Pair<Integer, Integer>> timeList = new ArrayList<Pair<Integer, Integer>>(); timeList.add(time.second); _TimeTable.put(time.first, timeList); } } private static Connection getConnectionFromCursor(final Cursor cursor) { final int stopId = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.FK_KEY_STOPID)); final int lineId = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.FK_KEY_LINEID)); final int orderId = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.KEY_ORDERID)); final String legend = cursor.getString(cursor .getColumnIndex(DatabaseAdapter.KEY_LEGEND)); return new Connection(stopId, lineId, orderId, legend); } private static Pair<String, Pair<Integer, Integer>> getTimeFromCursor( final Cursor cursor) { final String day = cursor.getString(cursor .getColumnIndex(DatabaseAdapter.KEY_DAY)); final int hour = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.KEY_HOUR)); final int minute = (int) cursor.getLong(cursor .getColumnIndex(DatabaseAdapter.KEY_MINUTE)); return new Pair<String, Pair<Integer, Integer>>(day, new Pair<Integer, Integer>(hour, minute)); } private static Connection getConnectionFromDB(final int stopId, final int lineId, final DatabaseAdapter dba) { final Cursor cursor = dba.getConnection(stopId, lineId); Connection result = null; if (cursor.moveToFirst()) result = getConnectionFromCursor(cursor); while (true) { result.addTime(getTimeFromCursor(cursor)); if (!cursor.moveToNext()) break; } cursor.close(); return result; } public static Connection getConnection(int stopId, int lineId, DatabaseAdapter dba) { Connection conn = getConnectionFromCache(stopId, lineId); if (conn == null) { conn = getConnectionFromDB(stopId, lineId, dba); if (conn != null) { _connectionList.add(conn); } } return getConnectionFromDB(stopId, lineId, dba); }
Baza danych aplikacji mobilnej została zaimplementowana z wykorzystaniem motoru bazy SQLite. Elementy tworzące bazę, zostały zaprezentowane w projekcie logicznym.
W aplikacji istnieją następujące przepływy danych przez widoku (aktywności Androida):
Każdorazowo pobierane są odpowiednie dane z bazy (przystanki, linie, połączenia) ze zminimalizowanym obciążeniem bazy danych poprzez zastosowanie interfejsu lazy load (dane inicjowane są dopiero gdy są potrzebne i są przechowywane żeby uniknąć niepotrzebnych zapytać do bazy).
Widok mapy
Widok linii przy wyszukiwaniu
Widok linii dla danego przystanka
Aplikacja aktualnie spełnia wszystkie podstawowe założenia, które założyliśmy w fazie projektowej. W przyszłości planujemy rozwój:
Google dostarczyło nam narzędzie, które jest łatwo przyswajalne i dobrze opisane w dokumentacji wraz z licznymi przykładami użycia. Sprawiło to, że napisanie aplikacji na system Android nie sprawiło zespołowi problemów. SQLite jako baza danych dla aplikacji mobilnych wydaje się odpowiednim wyborem. Nie dostarcza co prawda zbyt wielu funkcjonalności, ale jest prosta w obsłudze, a do tego lekka (co powoduje małe zużycie energii co w aplikacjach mobilnych jest kluczowe) i szybka.