J2ME v kostce – persistence dat
Skoro každá aplikace si potřebuje ukládat některé údaje potřebné pro její vlastní běh, aby s jejich zjišťováním neobtěžovala uživatele při každém spuštění znovu. Standardní J2ME nemá k ukládání dat souborový systém, ale pouze velmi jednoduché ukládání polí bajtů, označované krycím názvem RMS (Record Management System neboli systém správy záznamů).
Idea úložiště záznamů
Úložiště záznamů neboli databáze (Record store) se skládá ze seznamu záznamů. Tyto záznamy tvoří pole bajtů a jsou indexovány od „1“. Jednotlivé databáze musí mít v rámci jedné sady midletů unikátní názvy. Za konzistenci databáze při práci s ní odpovídá implementace Javy, takže všechny operace nad databází jsou atomické (například záznam může být buď uložený nebo neuložený, žádný stav „napůl uložený“ neexistuje). Stejně také platforma odpovídá za zachování dat přes vypnutí telefonu či výměnu baterie. Při odstranění sady midletů z telefonu jsou automaticky odstraněny i všechny její databáze.
Stejně jako midlety mají přístup jen ke zdrojům obsaženým v téže sadě midletů, mají i přístup do databáze omezený pouze na ty, které vytvořil midlet z téže sady midletů. Tato omezení vyplývají ze snahy zamezit rozšíření virů na mobilních telefonech a zaručit větší bezpečnost, než panuje v internetovém světě.
Balík javax.microedition.rms
Třídy a rozhraní specifická pro práci s databázemi se nacházejí ve výše uvedeném balíku. Základní třídou je RecordStore
. Databáze se otevírá statickou metodou openRecordStore(String recordStoreName, boolean createIfNecessary)
a zavírá metodou closeRecordStore()
, přičemž kolikrát byla otevřena, tolikrát musí být i zavřena, aby byla opravdu uzavřena. Sluší se databázi zavírat i na dobu, kdy je aplikace v pasivním stavu (zapauzovaná).
Pro ukládání a načítání dat se hodí použít výstupní a vstupní proudy z balíku java.io
. Jako příklad si dnes vezmeme primitivní telefonní seznam (ukázky kódu uvedené v článku jsou zjednodušeným výtahem z příkladu).
Zápis záznamu do databáze:
String name = „blablabla“;
String number = „123456789“;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(buffer);
RecordStore store = null;
try {
// otevřeme databázi
store = RecordStore.openRecordStore(„autobus“, true);
// zapíšeme jméno do výstupního proudu
dos.writeUTF(name);
// zapíšeme číslo do výstupního proudu
dos.writeUTF(number);
// převedeme ByteArrayOutputStream na pole bajtů
byte[] bytes = buffer.toByteArray();
// přidáme záznam do databáze
store.addRecord(bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// ať se děje, co se děje, zavřeme databázi i výstupní proud
store.closeRecordStore();
dos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
Přečtení záznamu z databáze (domyslete si obalení try – catch blokem):
byte[] bytes = store.getRecord(i);
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
String name = dis.readUTF();
String number = dis.readUTF();
V databázi je samozřejmě možné záznamy mazat – metoda deleteRecord(int id)
, či smazat celou databázi – metoda deleteRecordStore(String name)
, ale na tom není nic zajímavého. Zajímavější je třídění či filtrování záznamů.
Třídění záznamů
K třídění záznamů je určeno rozhraní RecordComparator
, které má jedinou metodu compare(byte[] b1, byte[] b2)
, kde b1 a b2 jsou dva záznamy z databáze. Zde je implementace této metody z příkladu:
public int compare(byte[] b1, byte[] b2) {
try {
// přečtení prvního textu z pole bajtů,
// v našem případě to je jméno
String s1 =
new DataInputStream(
new ByteArrayInputStream(b1)).readUTF();
String s2 =
new DataInputStream(
new ByteArrayInputStream(b2)).readUTF();
// porovnání jmen z prvního a druhého záznamu
int i = s1.compareTo(s2);
if (i == 0) {
// jména jsou stejná
return RecordComparator.EQUIVALENT;
} else if (i < 0) {
// první jméno je dříve než druhé
return RecordComparator.PRECEDES;
} else {
// první jméno je později než druhé
return RecordComparator.FOLLOWS;
}
} catch (Exception e) {
e.printStackTrace();
}
// nastane-li nějaká neočekávaná výjimka, musíme také něco vrátit
return RecordComparator.EQUIVALENT;
}
Filtrování záznamů
Chceme-li filtrovat záznamy v databázi, musíme implementovat rozhraní RecordFilter
, které má také pouze jednu metodu, a to matches(byte[] candidate)
.
// vrátí true, pokud jméno v záznamu začíná na daný řetězec
public boolean matches(byte[] b) {
try {
String s =
new DataInputStream(
new ByteArrayInputStream(b)).readUTF();
// aby nezáleželo na velikosti písmen, je prefix při zadání
// zkonvertován na malá písmena a zde
// konvertujeme text, který porovnáváme
if (s.toLowerCase().startsWith(prefix)) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Teď už jen stačí zadat metodě enumerateRecords(RecordFilter filter, RecordComparator comparator, boolean keepUpdated)
třídy RecordStore
naši implementaci filtru a třídy, která záznamy porovnává, a vrácená enumerace bude setříděná a bude obsahovat pouze záznamy, které náš filtr schválí. Chceme-li získat enumeraci všech záznamů, můžeme zadat místo filtru pouze null
.
Celý příklad…
…se skládá ze dvou souborů:
- AddressMIDlet.java – hlavní třída příkladu
- Storage.java – třída pro práci s databází
- přidat k příkladu mazání záznamů
- přidat možnost zadání více údajů, než jen jména a telefonního čísla (např. data narození, velikosti bot, počtu zubů apod.)
- přidat formulář na vyhledávání, který použije již implementovaný
RecordComparator
- rozšířit vyhledávání i na jiné položky než jméno
- Telefony podporující Javu – aktuální seznam
- Forum Nokia
- Siemens
- Motorola
- Sonny Ericsson
- Wireless java – komunita vývojářů s diskusním fórem, články a dalšími odkazy
- J2ME – specifikace, dokumentace
Pokud se zde náhodou najde nějaký laskavý čtenář, kterého příklad zaujal, může si na procvičení tématu udělat následující rozšíření, která by z dnešního příkladu udělala něco prakticky použitelného:
Odkazy, zdroje
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
AI v programování: Jak používat GitHub Copilot (část 1)
12. února 2024 -
AI a internetové podvody
29. října 2024 -
AI na dosah ruky: Jak je to s AI v osobních zařízeních?
22. ledna 2024
Nejnovější
-
Výkonný a kompaktní: ASOME Max Studio s výjimečným poměrem cena/výkon
11. listopadu 2024 -
Šokující data od Microsoftu: Kyberútoky rostou o stovky procent!
8. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024 -
OpenAI představilo novou funkci ChatGPT Search
6. listopadu 2024
Jeanes
Čvn 22, 2010 v 16:18Zdravim, diky za ukazkovy priklad. Trochu jsem se zadrhl na smazani a upravy zaznamu. Problem zrejme spociva v pridelovani ID. Pokud mazu zaznam pouze pomoci deleteREcord(int id) tak pak je pri dalsim zapisu zaznamu problem v tom, ze se zrejme ukladaji na mista vymaznych zaznamu a dale pak je problem. Diky za odpoved