Mobilne aplikacije temeljijo na spletni povezavi, od koder dobijo vse podatke, ki jih potrebujejo za delovanje in prikaz, vendar spletna povezava ni vedno na voljo, na primer v tujini, kadar nočemo plačevati dragega gostovanja, ali pa kje v gorah, kjer ni signala, ali pa zmanjka elektrike in podobno V takšnih trenutkih ne moremo prikazati podatkov s spleta, kljub vsemu pa želimo uporabniku aplikacije omogočiti uporabo te. Za ta namen si podatke, ki jih dobimo s spleta, shranimo na napravi. V tem delu si bomo pogledali, kam in kako shranimo različne podatke.

Različni tipi podatkov

Podatke v grobem delimo na 3 tipe.

Unikatni podatki
To so neponavljajoči podatki, ki imajo običajno manjšo količino vsebine. Lahko so vpisani le enkrat ali pa večkrat, vendar si v zapomnimo le zadnjo vrednost. Primeri takšnih podatkov so: datum zadnje sinhronizacije, uporabnikovo ime, statistika uporabe aplikacije, najboljši rezultat v igri in podobno

Seznami podatkov
To je ena ali več skupin podatkov, ki jih pridobivamo skozi čas in si vsakega posebej zapomnimo. Običajno si shranimo datum, ko je ta podatek nastal, avtorja tega podatka in seveda vsebino podatka. To so na primer sporočila (sms, Facebook in podobno), podatki o vremenu za vsak dan, seznam vseh uporabnikov neke storitve in podobno.

Datoteke
Kadar imamo opravka z veliko količino podatkov, jih shranimo v posamezno datoteko. Lahko gre za datoteke, ki jih ustvarimo na napravi ali pa jih samo prekopiramo s strežnika. To so lahko slike, videi, glasba ali pa dokumenti s podatki.

Pa si poglejmo, kako v Androidu shranimo vsak tip podatkov.

Unikatni podatki

Unikatne podatke zapisujemo z razredom SharedPreferences. Vsak podatek je zapisan kot par ključ-vrednost, pri čemer je ključ tipa String, vrednost pa katerikoli primitiven tip (String, int, long, boolean in podobno).

Instanco SharedPreferences dobimo v okviru aktivnosti s klicem:
SharedPreferences prefs = getSharedPreferences(“app_preferences”, 0);

Prvi parameter nam pove mesto shranjevanja unikatnih podatkov. Lahko bi namreč imeli več mest, na primer za vsakega uporabnika aplikacije svoje. Vendar večinoma tega ne potrebujemo (eno napravo Android običajno uporablja le ena oseba) in lahko uporabimo isto mesto za shranjevanje skozi celotno aplikacijo.

Drugi parameter pove, kako javno naj bodo shranjeni ti podatki. Številka 0 pomeni, da bodo vidni le v tej aplikacije, kar večinoma zadostuje.

Najprej moramo kakšen podatek shraniti, to storimo tako, da odpremo editor in vanj vnesemo ključ podatka in vrednost:

SharedPreferences.Editor editor = prefs.edit();
editor.putLong("last_refresh", System.currentTimeMillis());
editor.commit();

V prvi vrstici najprej omogočimo shranjevanje podatkov, tako da pridobimo razred Editor.
V drugi vrstici shranimo trenutni čas (ki je tipa long) pod ključ “last_refresh”.

V tretji vrstici pa se zgodi dejansko shranjevanje, ta korak je pomemben, saj brez njega podatek ne bo res shranjen.

Zdaj ko imamo podatek shranjen, ga pa lahko tudi preberemo. To storimo tako:
long lastRefreshTime = prefs.getLong("last_refresh", 0);

Prvi parameter je ključ, ki smo ga uporabili pri shranjevanju. Tako vemo, kateri podatek bomo dobili. Drugi podatek pa je privzeta vrednost, ki jo dobimo, če še nismo nikoli shranili podatka s tem ključem (na primer pred prvim osveževanjem podatkov).

Za druge tipe podatkov so nam na voljo metode getBoolean, getFloat, getInt, getLong, getString in getStringSet (za seznam String-ov). Ekvivalentno seveda tudi za shranjevanje.

Seznami podatkov

Za sezname podatkov uporabljamo bazo podatkov, kot je v navadi tudi v drugih vejah programiranja. Android že nativno podpira in vsebuje baze SQLite in vse, kar moramo narediti je, da jo definiramo in napolnimo. Za to razširimo razred SQLiteOpenHelper, ki večino dela opravi namesto nas:

public class MessageDatabase extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = “our_app_database”;

public MessageDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(“CREATE TABLE message (time INT, user TEXT, content TEXT)”);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}

public List<Message> readMessages() {
List<Message> messages = new ArrayList<Message>();
String selectQuery = "SELECT * FROM message";

SQLiteDatabase db = getReadableDatabase();
Cursor c = db.rawQuery(selectQuery, null);

if (c.moveToFirst()) {
do {
long time = c.getLong(c.getColumnIndex(“time”));
String user = c.getString(c.getColumnIndex(“user”));
String content= c.getString(c.getColumnIndex(“content”));

messages.add(new Message(new Date(time), user, content));
} while (c.moveToNext());
}

return messages;
}

public long saveMessage(Message message) {
SQLiteDatabase db = getWritableDatabase();

ContentValues values = new ContentValues();
values.put("time", message.getDate().getTime());
values.put("user", message.getUser());
values.put("content", message.getContent());

db.insert(“message”, null, values);
}
}

V metodi onCreate poskrbimo, da se ustvari tabela, ko prvič poženemo aplikacijo.
Metoda onUpgrade skrbi za nadgradnjo baze, če se pozneje spomnimo, da bi dodali kakšen stolpec ali pa celo tabelo. Pri tem moramo povišati verzijo baze DATABASE_VERSION, da lahko vemo na primer, da moramo “pri prehodu z verzije 13 na 14 dodati stolpec already_seen”.

V metodi readMessages preberemo vsa sporočila, ki jih imamo shranjena v bazi z imenom “message” in jih pretvorimo v seznam objektov Message. Tu ne potrebujemo zapisljive baze, zato jo pridobimo z metodo getReadableDatabase.

V metodi saveMessage pa shranimo dano sporočilo v bazo. Ker bomo zapisovali v bazo, jo dobimo z metodo getWritableDatabase.

S klicem teh metod lahko osnovno upravljamo bazo in shranjujemo vanjo podatke. Baze podatkov so osnova skoraj vsake aplikacije, zato je pametno čim več časa posvetiti načrtovanju strukture baze, saj preprosto pridobimo ali izgubimo pri hitrosti aplikacije.

Datoteke

Večina aplikacij Android ne potrebuje upravljanja datotek, saj baza povsem zadostuje, razen če gre za upravljanje multimedijskih datotek (deljenje slik, glasbe in podobno). Datoteke lahko zapisujemo na 2 mesti, na interni pomnilnik ali pa na kartico SD.

Zapisovanje podatkov na interni pomnilnik je sila preprosto. Že aktivnost namreč ponuja metodo openFileOutput, nakar le podamo bite, ki jih želimo zapisati, in že je vsebina v datoteki:
FileOutputStream outputStream = openFileOutput(“file.txt”, Context.MODE_PRIVATE);
outputStream.write(“Vsebina datoteke”.getBytes());
outputStream.close();

Ta koda ustvari datoteko file.txt, v katero zapiše tekst “Vsebina datoteke”. V praksi bo vsebina pridobljena dinamično in ime datoteke najbrž tudi, vendar to zadostuje za prikaz preprostosti shranjevanja.

Branje podatkov pa začnemo z metodo openFileInput, ki ji podamo ime datoteke (v našem primeru “file.txt”) in dobimo tok podatkov, po katerem nato preberemo podatke.

FileInputStream inputStream = openFileInput(“file.txt”);
byte[] data = new byte[inputStream.available()];
inputStream.read(data);
inputStream.close();

Drugo mesto za shranjevanje datotek pa je kartica SD, kamor želimo shraniti datoteke, do katerih želimo dostopati, na primer po povezavi z računalnikom. Primer takšnih podatkov so slike s fotoaparata na telefonih, ki so običajno v mapi DCIM/Camera na kartici. Za zapisovanje datotek najprej potrebujemo pravice za pisanje na eksterni pomnilnik. To dosežemo tako, da v datoteko AndroidManifest.xml dodamo pravico:

<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

</manifest>

Zdaj ko smemo pisati, poiščemo prostor na zunanji kartici. Dobimo ga z metodo:
File externalStorage = Environment.getExternalStorageDirectory();

In nato še določimo pot do datoteke:
File file = new File(externalStorage, “file.txt”);

Tok za zapisovanje nato dobimo tako:
FileOutputStream outputStream = new FileOutputStream(file);

Tok za branje pa tako:
FileInputStream inputStream = new FileInputStream(file);

Vse preostalo je enako kot pri pisanju in branju podatkov z lokalnega pomnilnika.
Zaključek
Zdaj so naši podatki varno shranjeni na napravi in uporabnik lahko dostopa do njih hitro in brez internetne povezave. Dobra praksa je, da uporabniku, takoj kot prižge našo aplikacijo, prikažemo najprej shranjene podatke, nato pa v ozadju s strežnika poberemo posodobljene in jih nato posodobimo še na ekranu.

Za tehnično plat je tako poskrbljeno, vi pa si morate domisliti zanimive vsebine, ki bi jih shranili in ponudili uporabnikom.

Moj mikro, september – oktober 2014 | Rok Končina