Mobile App Development ORM

Werbung
Mobile App
Development
- Persistenz -
Inhalt
• Shared Preferences
• Dateien
• Datenbanken
• ORM
Mobile App Development
2
Shared Preferences
Shared Preferences
• Framework zum Speichern von Key-ValuePaaren
• Datentypen: boolean, float, int, long, String
• Daten werden in einer Datei gespeichert
• Nach Deinstallation der App werden
Preferences-Dateien mit gelöscht
Mobile App Development
4
Shared Preferences
// DemoActivity für SharedPreferences
public class SharedPrefsDemoActivity extends Activity {
public static final String PREFS_FILE = "prefsFile";
public static final String MY_KEY = "myKey";
private int mMyMember;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Aufrufen der SharedPreferences
SharedPreferences prefs = getSharedPreferences(PREFS_FILE, MODE_PRIVATE);
// Abruf des Wertes für MY_KEY (default: 0)
mMyMember = prefs.getInt(MY_KEY, 0);
}
@Override
protected void onStop() {
super.onStop();
// Aufruf der SharedPreferences
SharedPreferences prefs = getSharedPreferences(PREFS_FILE, MODE_PRIVATE);
// Editor aufrufen, um Änderungen zu machen
Editor editPrefs = prefs.edit();
// Key-Value-Paar anlegen/aktualisieren
editPrefs.putInt(MY_KEY, mMyMember);
// Änderungen schreiben
editPrefs.commit();
}
}
Mobile App Development
5
Dateien Dateien
• Zwei mögliche Plätze zum Speichern von
Dateien:
• Internal Storage
geräteinterner Speicher
• External Storage
externer Speicher, wie SD-Karten
Mobile App Development
7
Interner Speicher
• Standardmäßig sind Dateien, die auf dem
internen Speicher abgelegt werden privat,
d.h. andere Anwendungen können nicht
darauf zugreifen
• Wird die Anwendung deinstalliert, werden
auch die internen Dateien gelöscht
Mobile App Development
8
Interner Speicher
• Lesen von Dateien
•
Context.openFileInput(...) liefert
einen FileInputStream
• Die weitere Verarbeitung erfolgt wie bei
Standard Datei- und Stream-Verarbeitung
in Java
Mobile App Development
9
Interner Speicher
• Datei lesen
private void readFile(String fileName) throws Exception {
/* openFileInput() öffnet die Datei mit dem angegebenen Namen
* und liefert eine FIS zurück */
FileInputStream fis = openFileInput(fileName);
BufferedReader reader = new BufferedReader(
new InputStreamReader(fis));
String line = "";
/* Auslesen der Datei */
while ( (line = reader.readLine()) != null) {
/* zeilenweise Verarbeitung */
Log.d(LOG, line);
}
/* reader und stream schließen */
}
Mobile App Development
10
Interner Speicher
• Dateien schreiben
•
Context.openFileOutput(...) liefert
einen FileOutputStream
• existiert die Datei noch nicht, wird sie
automatische angelegt
• das Schreiben der Datei funktioniert wie
auch bei Standard Java
Mobile App Development
11
Interner Speicher
• Dateien schreiben
private void writeFile(String fileName, String content)
throws Exception {
/* openFileOutput öffnet einen FOS, der mit der angegebenen
Datei verbunden ist und legt die Datei an, wenn sie noch
nicht existiert */
FileOutputStream fos = openFileOutput(fileName, MODE_PRIVATE);
OutputStreamWriter osw = new OutputStreamWriter(fos);
/* Inhalt in die Datei schreiben */
osw.write(content);
/* writer und stream schließen */
osw.close();
}
Mobile App Development
12
Interner Speicher
• Android unterschiedet verschiedene Modi
zum Anlegen einer Datei
•MODE_PRIVATE
• Datei ist privat, kann also nur von der
erstellenden App gelesen werden
• wenn Datei bereits vorhanden, wird sie
überschrieben
Mobile App Development
13
Interner Speicher
•MODE_APPEND
• Wenn Datei bereits vorhanden, wird
neuer Inhalt angehängt
•MODE_WORLD_READABLE
• Alle anderen Apps haben lesenden
Zugriff
Mobile App Development
14
Interner Speicher
•MODE_WORLD_WRITEABLE
• Alle anderen Apps haben schreibenden
Zugriff auf die Datei
Mobile App Development
15
Interner Speicher
• Es gibt einige weitere nützliche Methoden,
um mit Dateien und Verzeichnisse zu
arbeiten
• Context.getFilesDir()
Absoluter Pfad zum dem Verzeichnis, in
dem interne Dateien abgelegt werden
• Context.getDir(name,
mode)
Erzeugt oder öffnet ein Verzeichnis
Mobile App Development
16
Interner Speicher
• Weitere Methoden
• Context.deleteFile(name)
Entfernt die angegebene Datei
• Context.fileList()
Listet alle mit der App assoziierten
(internen) Dateien auf
Mobile App Development
17
Interner Speicher
• Weitere Methoden
• Context.getCacheDir()
Gibt den Pfad zum Verzeichnis für CacheDateien zurück
• File.createTempFile(prefix,
suffix)
Erzeugt eine temporäre Datei im tempVerzeichnis
Mobile App Development
18
Externer Speicher
• Im Gegensatz zum internen Speicher gibt
es bei externem Besonderheiten zu
beachten:
• Daten können von jedermann gelesen
werden
• Speicher muss nicht immer verfügbar sein (SD-Karte kann entfernt werden)
Mobile App Development
19
Externer Speicher
• Verfügbarkeit des ext. Speichers prüfen
private boolean mExtStorageReadable = false;
private boolean mExtStorageWritable = false;
...
// Überprüft, ob der externe Speicher gelesen und geschrieben werden kann
private void checkAvailability() {
// Status des externen Speichers abrufen
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// Speicher kann gelesen und geschrieben werden
mExtStorageReadable = true;
mExtStorageWritable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// Speicher kann nur gelesen werden
mExtStorageReadable = true;
mExtStorageWritable = false;
} else {
// Speicher ist z.B. entfernt oder hat kein unterstütztes Dateisystem
mExtStorageReadable = false;
mExtStorageWritable = false;
}
}
Mobile App Development
20
Externer Speicher
• Unterscheidung zwischen App-eigenen
Daten und geteilten Daten
• App-eigene Daten sind nur für die eigene
App relevant und werden bei
Deinstallation mit gelöscht
• Geteilte Daten können auch von anderen
Apps interpretiert werden (Musik, Bilder, etc.)
Mobile App Development
21
Externer Speicher
• Zugriff auf App-eigene Daten
// Methode, um auf externen Speicher zuzugreifen
private void accessFiles() {
// Verfügbarkeit des ext. Speichers prüfen
checkAvailability();
...
// bis API Level 7 (Eclair)
File extStore = Environment.getExternalStorageDirectory();
File extAppDir7 = new File(extStore,
"Android" + File.separator +
"data" + File.separator + getPackageName() + File.separator + "files");
extAppDir7.mkdirs();
// ab API Level 8 (Froyo)
File extAppDir8 = getExternalFilesDir(null);
// ...
// Standard Java Dateiarbeit, wie gehabt
// ...
}
Mobile App Development
22
Externer Speicher
• Geteilte Daten sollten in öffentlichen
Verzeichnissen abgelegt werden
• Bsp.: /Music, /Podcasts, /Alarms, /Download, etc.
Mobile App Development
23
Externer Speicher
• auf geteilte Daten zugreifen
private void accessPublicFiles() {
// Verfügbarkeit des ext. Speichers prüfen
checkAvailability();
...
// öffentliches Verzeichnis für Musik
File musicDir = null;
// bis API Level 7
File extDir = Environment.getExternalStorageDirectory();
musicDir = new File(extDir, "Music");
// ab API Level 8
musicDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
// Danach, wie gewohnt
File musicFile = new File(musicDir, "myMusic.mp3");
musicFile.createNewFile();
}
Mobile App Development
24
Externer Speicher
• Temporäre Dateien auf den externen
Speicher schreiben
private void cacheFiles() {
// Verfügbarkeit des ext. Speichers prüfen
checkAvailability();
// ...
File cacheDir = null;
// bis API Level 7
File extDir = Environment.getExternalStorageDirectory();
cacheDir = new File(extDir, "/Android/data/" + getPackageName() + "/cache/");
// ab API Level 8
cacheDir = getExternalCacheDir();
// Erstellen der temporären Datei
File cacheFile = File.createTempFile("myPrefix", "mySuffix", cacheDir);
}
Mobile App Development
25
Datenbanken
SQLite
• SQLite ist die Standard Datenbank-Engine,
die in Android verwendet wird
• optimiert für Embedded Systems
• zeichnet sich aus durch
• Abgeschlossenheit, Serverlosigkeit
• Minimal- bis 0-Konfiguration
• unterstützt Standard SQL
Mobile App Development
27
SQLite
• Daten werden in SQLite durch sogenannte
Storage Classes repräsentiert
• Jedem Wert wird ein Datentyp zugeordnet
Mobile App Development
28
Storage Classes
Datentyp
Beschreibung
NULL
Null-Wert
INTEGER
vorzeichenbehaftete Ganzzahl (1-8 Byte)
REAL
Dezimalzahl (8 Byte)
TEXT
String
BLOB
Binärdaten
Mobile App Development
29
Storage Classes
Datentyp Konvertierung
Boolean INTEGER
Date/
Time
Darstellung
0 = false, 1 = true
TEXT
ISO8601: YY-MM-DD HH:MM.SS.SSS
REAL
Zeit in Tagen seit 01.01.4713 v.Chr. 12:00
INTEGER
UNIX-Zeit, Sekunden seit
1970-01-01 00:00:00 UTC
Mobile App Development
30
Datentypen
• Alle Standard SQL-Datentypen können
verwendet werden
• Integer, Numeric, Decimal, Float, Real,
Double
• Varchar, Char
• Date, Time, Timestamp
• Boolean
• Blob, Clob
Mobile App Development
31
Operatoren
||
*
+
<<
<
=
IN
AND
OR
/
>>
<=
==
LIKE
%
&
>
!=
|
>=
<>
GLOB
IS
IS NOT
• siehe: http://www.sqlite.org/lang_expr.html
Mobile App Development
32
DatenbankfunkConen
• Kernfunktionen
• abs, hex, min, max, random, round, ...
• substr, trim, replace, upper, lower, ...
• siehe: http://www.sqlite.org/
lang_corefunc.html
Mobile App Development
33
DatenbankfunkConen
• Aggregatfunktionen
• min, max, avg, count, sum
• weitere: http://www.sqlite.org/
lang_aggfunc.html
• Datum & Zeit
• date, time, datetime, strftime
• http://www.sqlite.org/lang_datefunc.html
Mobile App Development
34
SQLiteOpenHelper
• abstrakte Oberklasse für DB-Zugriffe
• empfohlene Schnittstelle zum
• Erstellen, Öffnen und
• Aktualisieren
von Datenbanken
Mobile App Development
35
SQLiteOpenHelper
•
Wenn von SQLiteOpenHelper abgeleitet wird,
müssen folgende Methode überschrieben
werden
•
Konstruktor: Hier wird dem super
Konstruktor der Context, Datenbankname
und -version mitgegeben
•
onCreate(): wird aufgerufen, wenn die
Datenbank erstellt wird. Hier werden SQL
Statements ausgeführt, um die Tabellen zu
erstellen.
Mobile App Development
36
SQLiteOpenHelper
•
onUpgrade(): wird aufgerufen, wenn eine
•
onDowngrade(): ist das Pendant zu der
Methdode onUpgrade() (ab API 11)
Datenbankversion gefunden wurde, die
älter ist, als die aktuelle. Hier werden alte
Datenbanktabellen angepasst.
Mobile App Development
37
SQLiteOpenHelper
• Beispiel: Erstellung einer Datenbank, mit der TODOs gespeichert werden können
• Datenmodell:
todo
date
Integer
title
Text
description
Text
Mobile App Development
38
DatabaseHelper
// Hilfsklasse zum Erstellen, Aktualisieren und Öffnen der Datenbank
public class DatabaseHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "todo.db";
private static final int DATABASE_VERSION = 1;
public
public
public
public
public
public
public
static
static
static
static
static
static
static
final
final
final
final
final
final
final
String
String
String
String
String
String
String
// Datenbankname
// Datenbankversion
TABLE_NAME = "todo";
DATE_FIELD_NAME = "date";
DATE_FIELD_TYPE = "INTEGER";
TITLE_FIELD_NAME = "title";
TITLE_FIELD_TYPE = "TEXT";
DESCR_FIELD_NAME = "description";
DESCR_FIELD_TYPE = "TEXT";
// SQL statement zum Erstellen der Tabelle
private static final String TABLE_CREATE =
"CREATE TABLE " + TABLE_NAME + "("
+ DATE_FIELD_NAME + " " + DATE_FIELD_TYPE + ", "
+ TITLE_FIELD_NAME + " " + TITLE_FIELD_TYPE + ", "
+ DESCR_FIELD_NAME + " " + DESCR_FIELD_TYPE + ")";
// Konstruktor
public DatabaseHelper(Context
super(context,
DATABASE_NAME,
null,
DATABASE_VERSION);
}
context) {
// der Context
// Datenbankname
// CursorFactory
// Datenbankversion
// ...
Mobile App Development
39
DatabaseHelper
// Hilfsklasse zum Erstellen, Aktualisieren und Öffnen der Datenbank
public class DatabaseHelper extends SQLiteOpenHelper {
// Wird aufgerufen, wenn die DB das erste mal erstellt wird
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(TABLE_CREATE);
} catch (SQLException ex) {
Log.e("DatabaseHelper", "error creating tables", ex);
}
}
public void onUpgrade(SQLiteDatabase db, int oldVer, int newVer) {
// alte Tabellen auslesen und löschen, neue Tabellen anlegen
// alten Inhalt in neue Tabellen einfügen
}
// ab API-Level 11
public void onDowngrade(SQLiteDatabase db, int oldVer, int newVer) {
// alte Tabellen auslesen und löschen, neue Tabellen anlegen
// alten Inhalt in neue Tabellen einfügen
}
}
Mobile App Development
40
Daten abfragen
• Mit der Methode getReadableDatabase()
wird aus dem SQLiteOpenHelper eine
lesbare Datenbankinstanz erzeugt
• Mit der query()-Methode der DB Instanz
können Daten gelesen werden
• Die query() Methode besitzt einige
Parameter, die einer SQL-Abfrage
entsprechen
Mobile App Development
41
Daten abfragen
•
query()-Parameter
• table: der Tabellenname
• columns: String Array, das die
abzufragenden Spaltennamen enthält
oder null für alle Spalten
• selection: entspricht der WHERE-Klausel
eines SQL-Statements ohne das WHERE
Schlüsselwort
Mobile App Development
42
Daten abfragen
•
query()-Parameter
• selectionArgs: dient der Parametrisierung
der selection
• groupBy: dient der Gruppierung von
Zeilen in Verbindung mit
Aggregatfunktionen
• having: Filter für Gruppierungen
Mobile App Development
43
Daten abfragen
•
query()-Parameter
• orderBy: dient der Sortierung des
Ergebnisses nach einer Spalte
• limit: begrenzt die Anzahl der Ergebnisse
Mobile App Development
44
Daten abfragen
// Methode zum abfragen von Daten aus der DB
private void doSelect() {
// Datenbank zum Lesen öffnen
SQLiteDatabase db = new DatabaseHelper(this).getReadableDatabase();
// query(String table, String[] columns, String selection, String[]
//
selectionArgs, String groupBy, String having, String orderBy,
//
String limit)
Cursor result = db.query(
DatabaseHelper.TABLE_NAME, null, null, null, null, null,
DatabaseHelper.DATE_FIELD_NAME, null);
// Ergebnis verarbeiten
// Datenbank schließen
db.close();
}
Mobile App Development
45
Daten abfragen
• Die query() Methode der DatenbankInstanz liefert einen Cursor zurück
• Cursor repräsentieren das Ergebnis der
Datenbankabfrage
• Ein Cursor besitzt einen Zeiger, mit dem
man datensatzweise über das ResultSet
navigieren kann
Mobile App Development
46
Daten abfragen
• Nützliche Cursor Methoden
•
getCount(): gibt die Anzahl der
•
moveTo[First, Next, Last]():
•
getPosition(): gibt die aktuelle
Datensätze im Cursor an
bewegt den Datensatzzeiger im Cursor
Zeigerposition im Cursor zurück
Mobile App Development
47
Daten abfragen
• Nützliche Cursor Methoden
•
getColumnIndex(): gibt den Index zum
•
get[Long,String,Int,Short,...]():
übergebenen Spaltennamen zurück
gibt den Wert im Datensatz für den
angegebenen Spaltenindex zurück
Mobile App Development
48
Daten abfragen
// Methode zum abfragen von Daten aus der DB
private void doSelect() {
// Datenbank zum Lesen öffnen und query ausführen
Cursor result = db.query(...);
if (result.moveToFirst()) { // wenn Cursor nicht leer
// Spaltenindex vom Datums- und Titel-Feld abrufen
int dateIdx = result.getColumnIndex(DatabaseHelper.DATE_FIELD_NAME);
int titleIdx = result.getColumnIndex(DatabaseHelper.TITLE_FIELD_NAME);
do {
// Datum und Titel auslesen
long date = result.getLong(dateIdx);
String title = result.getString(titleIdx);
// ... und verarbeiten
Log.d(LOG_TAG, new Date(date) + " " + title);
} while (result.moveToNext()); // solange Datensätze vorhanden
}
// Cursor direkt wieder schließen, um Ressourcen freizugeben
result.close();
// Datenbank schließen
db.close();
}
Mobile App Development
49
Daten abfragen
• Zur Abfrage von Daten können auch
Standard SQL Statements verwendet
werden
• Optional können diese parametrisiert
werden
Mobile App Development
50
Daten abfragen
public void doRawSelect() {
// Datenbank zum Lesen öffnen
SQLiteDatabase db = new DatabaseHelper(this).getReadableDatabase();
// SQL Statement erstellen
String sql = "SELECT * FROM " + DatabaseHelper.TABLE_NAME +
" ORDER BY " + DatabaseHelper.DATE_FIELD_NAME;
// query ausführen
Cursor result = db.rawQuery(sql, null);
if (result.moveToFirst()) { // wenn Cursor nicht leer
// Spaltenindex vom Datums- und Titel-Feld abrufen
int dateIdx = result.getColumnIndex(DatabaseHelper.DATE_FIELD_NAME);
int titleIdx = result.getColumnIndex(DatabaseHelper.TITLE_FIELD_NAME);
do { // für jeden Datensatz
// Datum und Titel auslesen
long date = result.getLong(dateIdx);
String title = result.getString(titleIdx);
// ... und verarbeiten
Log.d(LOG_TAG, new Date(date) + " " + title);
} while (result.moveToNext()); // solange Datensätze vorhanden
}
result.close();
// Cursor schließen
db.close(); // Datenbank schließen
}
Mobile App Development
51
Daten einfügen
• Um Datensätze in die Datenbank
einzufügen werden ContentValues Objekte
verwendet
• Ein ContentValues Objekt repräsentiert
einen Datensatz in einer Tabelle
• Ein CotentValues Objekt besitzt put()
Methoden, die einen Schlüssel und einen
Wert erhalten
Mobile App Development
52
Daten einfügen
• Schlüssel ist der Spaltenname aus der
Datenbank und dazu der zu setzende Wert
• ContentValues Objekte werden unter
Angabe der Tabelle per insert() Methode
in die Datenbank eingefügt
Mobile App Development
53
Daten einfügen
// Methode zum Einfügen von Daten in die DB
private void doInsert() {
// Datenbank zum Schreiben öffnen
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// Datensatz erstellen
ContentValues vals = new ContentValues();
vals.put(DatabaseHelper.DATE_FIELD_NAME, System.currentTimeMillis());
vals.put(DatabaseHelper.TITLE_FIELD_NAME, "meinTitel");
vals.put(DatabaseHelper.DESCR_FIELD_NAME, "meineBeschreibung");
// Datensatz in die Datenbank einfügen
db.insert(DatabaseHelper.TABLE_NAME, null, vals);
// Datenbank schließen
db.close();
}
Mobile App Development
54
Daten aktualisieren
• Die Aktualisierung von Datensätzen
funktioniert ähnlich wie das Einfügen
• Es wird ein ContentValues Objekt mit den
neuen Daten erzeugt und per update()
Methode abgeschickt
Mobile App Development
55
Daten aktualisieren
// Mehtode zum Aktualisieren von Datensätzen
private void doUpdate() {
// Datenbank zum Schreiben öffnen
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// ContentValues Objekt mit den neuen Daten erzeugen
ContentValues values = new ContentValues();
values.put(DatabaseHelper.DATE_FIELD_NAME, new java.util.Date().getTime());
// Datensätze unter Angabe des Tabellennamens und einer
// WHERE Bedingung aktualisieren
int rows = db.update(DatabaseHelper.TABLE_NAME, values,
DatabaseHelper.DATE_FIELD_NAME + " LIKE 'b%'", null);
Log.d(LOG_TAG, "affected rows: " + rows);
// Datenbank schließen
db.close();
}
Mobile App Development
56
Daten löschen
• Datensätze werden gelöscht, indem eine
WHERE Bedingung angegeben wird, die auf
die zu löschende Datensätze zutrifft
Mobile App Development
57
Daten löschen
// Methode zum Löschen von Datensätzen
private void doDelete() {
// Datenbank zum Schreiben öffnen
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// Datensätze unter Angabe einer WHERE Bedingung löschen
int rows = db.delete(DatabaseHelper.TABLE_NAME,
DatabaseHelper.DATE_FIELD_NAME + " < date('now')", null);
// Anzahl gelöschter Datensätze löschen
Log.d(LOG_TAG, rows + " rows deleted");
// Datenbank schließen
db.close();
}
Mobile App Development
58
Raw Queries
• Einfügen, Aktualisieren und Löschen
private void doRawInsertUpdateDelete() {
// Datenbank zum Schreiben öffnen
SQLiteDatabase db = new DatabaseHelper(this).getWritableDatabase();
// insert statement erstellen & abschicken
String insertSQL = "INSERT INTO " + DatabaseHelper.TABLE_NAME + " VALUES("
+ System.currentTimeMillis() + ", 'mein Titel', 'meineBeschreibung')";
db.execSQL(insertSQL);
// update statement erstellen & abschicken
String updateSQL = "UPDATE " + DatabaseHelper.TABLE_NAME + " SET "
+ DatabaseHelper.TITLE_FIELD_NAME + "=neuerTitel WHERE "
+ DatabaseHelper.DATE_FIELD_NAME + "<" + System.currentTimeMillis();
db.execSQL(updateSQL);
// delete statement erstellen & abschicken
String deleteSQL = "DELETE FROM " + DatabaseHelper.TABLE_NAME + " WHERE "
+ DatabaseHelper.TITLE_FIELD_NAME + " LIKE 'n%'";
db.execSQL(deleteSQL);
db.close(); // Datenbank schließen
}
Mobile App Development
59
TransakConen
• Standardmäßig wird jedes SQL-Statement
in einer eigenen Transaktion ausgeführt
• Gründe, um einen eigenen
Transaktionsrahmen zu setzen:
• „succeed or fail as a whole“
• Performance
Mobile App Development
60
TransakConen
• Transaktionen werden mit
beginTransaction() gestartet
• ... und mit endTransaction() beendet
• Um die Transaktion als erfolgreich zu
markieren, muss vor dem Aufruf von
endTransaction() die Methode
setTransactionSuccessful()
aufgerufen werden
Mobile App Development
61
TransakConen
• Transaktionen ausführen
private void doTransaction() {
// Datenbank zum Schreiben öffnen
SQLiteDatabase db = new DatabaseHelper(this).getWritableDatabase();
try {
// Transaktion starten
db.beginTransaction();
/* auf der Datenbank arbeiten */
// Transaktion als erfolgreich markieren
db.setTransactionSuccessful();
} catch (Exception ex) {
// Fehler behandeln
} finally {
// Transaktion beenden
db.endTransaction();
}
}
Mobile App Development
62
CursorAdapter
CursorAdapter
• Die Ergebnisse von Datenbankabfragen
können über einen Adapter direkt in
AdapterViews (ListView, SpinnerView)
dargestellt werden
• Hierfür werden CursorAdapter verwendet,
die den AdapterViews ebenso wie
ArrayAdapter ihre Daten zur Verfügung
stellen
Mobile App Development
64
CursorAdapter
• Exkurs: Eigene List Layouts
• Einträge in einer Liste, also die Darstel-
lung der einzelnen Einträge, können mit
einem eigenen Layout versehen werden
• Layout-Dateien werden genauso erstellt,
wie bei Activity-Layouts
Mobile App Development
65
CursorAdapter
• Exkurs: Eigene List Layouts
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="..."
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Mobile App Development
66
CursorAdapter
• Exkurs: Eigene List Layouts
• Layout verwenden
private void useCustomListLayout() {
// ListView Referenz abrufen
ListView listView =
(ListView) findViewById(R.id.listView1);
// Adapter bekommt die Layout Referenz
// zum angepassten Layout
ArrayAdapter<String> listAdapter =
new ArrayAdapter<String>(
this, R.layout.list_item);
// ... wie gehabt
}
Mobile App Development
67
CursorAdapter
• CursorAdapter stellen eine Brücke
zwischen AdapterViews und Cursor
Objekten her
• Im CursorAdapter wird angegeben, welche
Spalte auf welches Element im ListItem
Layout abgebildet wird
• Das Auslesen des Cursors und Setzen der
ListItems wird vom Adapter übernommen
Mobile App Development
68
CursorAdapter
private void setListCursorAdapter() {
// Datenbank öffnen und Abfrage ausführen
SQLiteDatabase db = new DbHelper(this).getReadableDatabase();
String sql = "select _id, title, date from todo";
Cursor cursor = db.rawQuery(sql, null);
// gibt an, welche Spalten in der ListView dargestellt werden sollen
String[] from = new String[] {"title", "date"};
// gibt an, welche Spalte in welcher View dargestellt werden soll
int[] to = new int[]{R.id.title, R.id.date};
// CursorAdapter erstellen
SimpleCursorAdapter sca = new SimpleCursorAdapter(this,
R.layout.list_item, cursor, from, to, 0);
// Adapter sezten
ListView lv = (ListView) findViewById(R.id.listView1);
lv.setAdapter(sca);
}
Mobile App Development
69
CursorAdapter
• Hinweis: Um einen SimpleCursorAdapter zu nutzen,
muss die verwendete Tabelle eine Spalte
„_id“ besitzen!
Mobile App Development
70
Objekt-­‐relaConales Mapping
ORM
• Problemstellung
• Objekt-Modell und Relationales Modell
unterscheiden sich
• Objekte müssen immer wieder in
Tabellen-Datensätze konvertiert werden
und umgekehrt
Mobile App Development
72
ORM
• Problemstellung
• Hohe Fehleranfälligkeit durch
Verwendung von Plain SQL (Typo‘s)
• große Menge an SQL Statements muss
programmiert werden
Mobile App Development
73
ORM
• Beispiel
• zwei Tabellen mit einfacher 1:nVerknüpfung
todo
date
title
description
fk_todo_lis
t
Integer
Text
Text
Integer
Mobile App Development
1
n
74
todo_group
id
name
Integer
Text
ORM
• Beispiel
public class TodoDbHelper extends SQLiteOpenHelper {
public static final String LOG_TAG = TodoDbHelper.class.getName();
public static final String DATABASE_NAME = "groupedtodo.db";
private static final int DATABASE_VERSION = 1;
// table creation attributes
public static final String TODO_TABLE_NAME = "todo";
public static final String TODO_ID_FIELD_NAME = "_id";
public static final String TODO_ID_FIELD_TYPE = "INTEGER PRIMARY KEY AUTOINCREMENT";
public static final String TODO_DATE_FIELD_NAME = "date";
public static final String TODO_DATE_FIELD_TYPE = "INTEGER";
public static final String TODO_TITLE_FIELD_NAME = "title";
public static final String TODO_TITLE_FIELD_TYPE = "TEXT";
public static final String TODO_PARENT_FIELD_NAME = "parent";
public static final String TODO_PARENT_FIELD_TYPE = "INTEGER";
public static final String TODO_PARENT_FOREIGN_KEY = "CONSTRAINT fk_parent FOREIGN KEY (parent) REFERENCES todoGroup(_id)";
public
public
public
public
public
static
static
static
static
static
final
final
final
final
final
String
String
String
String
String
TODO_GROUP_TABLE_NAME = "todo_group";
TODO_GROUP_ID_FIELD_NAME = "_id";
TODO_GROUP_ID_FIELD_TYPE = "INTEGER PRIMARY KEY AUTOINCREMENT";
TODO_GROUP_NAME_FIELD_NAME = "name";
TODO_GROUP_NAME_FIELD_TYPE = "TEXT";
// ...
}
Mobile App Development
75
ORM
• Beispiel
public class TodoDbHelper extends SQLiteOpenHelper {
// ...
// SQL statement for table creation
private static final String TODO_TABLE_CREATE =
"CREATE TABLE " + TODO_TABLE_NAME + "("
+ TODO_ID_FIELD_NAME + " " + TODO_ID_FIELD_TYPE + ", "
+ TODO_DATE_FIELD_NAME + " " + TODO_DATE_FIELD_TYPE + ", "
+ TODO_TITLE_FIELD_NAME + " " + TODO_TITLE_FIELD_TYPE + ", "
+ TODO_PARENT_FIELD_NAME + " " + TODO_PARENT_FIELD_TYPE + ", "
+ TODO_PARENT_FOREIGN_KEY + ")";
// SQL statement for TodoList table creation
private static final String TODO_GROUP_TABLE_CREATE =
String.format("CREATE TABLE %s (%s %s, %s %s)", TODO_GROUP_TABLE_NAME,
TODO_GROUP_ID_FIELD_NAME, TODO_GROUP_ID_FIELD_TYPE,
TODO_GROUP_NAME_FIELD_NAME, TODO_GROUP_NAME_FIELD_TYPE);
}
Mobile App Development
76
ORM
• Lösung
• Objekt-relationales Mapping (ORM)
ermöglicht die Abbildung von Objekten
auf Tabellen
• Abstraktion der Datenbankzugriffe durch
Methoden zum Erstellen, Löschen, ...
• Foreign-Key Beziehungen zwischen
Datensätzen werden in Objektreferenzen
umgewandelt
Mobile App Development
77
ORM
• Lösung
• Es muss kein SQL mehr geschrieben
werden (kann aber)
• Es gibt viele verschiede ORM
Frameworks (OrmLite, greenDAO, ...)
• Hier in der Vorlesung wird OrmLite
exemplarisch für Android vorgestellt
Mobile App Development
78
ORM
• OrmLite (http://ormlite.com)
• Sehr einfaches Orm-Framework
• schnelle Einarbeitungszeit
• Es muss nur eine jar-Datei eingebunden
werden
• Entities (Objekte) werden mit
Annotations versehen
Mobile App Development
79
ORM
• Todo Beispiel
@DatabaseTable(tableName="todolist")
public class TodoList {
@DatabaseTable(tableName="todo")
public class Todo {
@DatabaseField(generatedId=true)
private int id;
@DatabaseField
private long date;
@DatabaseField
private String title;
@DatabaseField
private String description;
@DatabaseField(foreign=true)
private TodoList parent;
@DatabaseField(generatedId=true)
private int id;
@DatabaseField
private String name;
@ForeignCollectionField
private Collection<Todo> todos;
public Todo() {
// setter & getter
public TodoList() {
}
}
}
// setter & getter
}
Mobile App Development
80
ORM
• OrmDbHelper
public class OrmDbHelper extends OrmLiteSqliteOpenHelper {
public static final String LOG = OrmDbHelper.class.getName();
public static final String DB_NAME = "todo.db";
public static final int DB_VERSION = 1;
public OrmDbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db, ConnectionSource source) {
// Registrierung der Klassen beim ORM Framework
try {
TableUtils.createTable(source, Todo.class);
TableUtils.createTable(source, TodoList.class);
} catch (SQLException ex) {
Log.e(LOG, "error creating tables", ex);
}
}
}
Mobile App Development
81
ORM
• Data Access Objects
• Der Zugriff auf die Entitäten, heißt
Suchen, Einfügen, Aktualisieren und
Löschen und wird über sog. DAOs
realisiert
• Die DAOs kapseln die eigentlichen
Datenbankzugriffe (SELECT, INSERT, ...)
• Für jede Entität wird ein eigenes DAO
erstellt
Mobile App Development
82
ORM
• Data Access Object erstellen
public class OrmDbHelper extends OrmLiteSqliteOpenHelper {
// ... onCreate() ...
// Erzeugung des Data Access Objects
public Dao<Todo, Integer> createTodoDAO() {
try {
return DaoManager.createDao(connectionSource, Todo.class);
} catch (SQLException ex) {
Log.e(LOG, "error creating DAO for Todo class", ex);
}
return null;
}
}
Mobile App Development
83
ORM
• Daten suchen und manipulieren
private void handleTodos() throws SQLException {
// Data Access Object für Todo erstellen
final Dao<Todo, Integer> todoDAO = dbHelper.createTodoDAO();
// query all todos from db
final List<Todo> allTodos = todoDAO.queryForAll();
// create new todo
Todo todo = new Todo(new Date(), "Test title", "Test description");
todoDAO.create(todo);
// delete todo element with id=5
todoDAO.deleteById(5);
// update todo element
todoDAO.update(todo);
}
Mobile App Development
84
Herunterladen