Verwendung einer SQLite Datenbank in einer Android App

Veröffentlicht in: Android, Java | 0

Will man in einer Android Anwendung Daten speichern, kann hierzu bequem die in das Android SDK integrierte SQLite Datenbank verwendet werden. Diese ist besonders für den mobilen Anwendungsbereich konzipiert, da sie mit gerade mal 250 KByte extrem genügsam im Bezug auf Speicherplatz ist.

Im folgenden Beispiel sollen die Einstellungen einer Anwendung in der Datenbank gespeichert werden. Hierzu wird eine SettingsActivity erstellt – nicht vergessen, die Activity im Android Manifest zu definieren –  wobei das Layout in einer XML-Datei definiert ist. Als variables Feld erhält die Activity einen Benutzer-Namen, welcher vom Benutzer editiert werden kann. Außerdem gibt es den obligatorischen Speichern-Button, damit die Änderungen vom Benutzer auch gespeichert werden können:

<EditText
android:id=“@+id/name“
android:layout_width=“fill_parent“
android:layout_height=“wrap_content“  android:background=“@android:drawable/editbox_background“
android:text=““
android:padding=“3dip“ />

<Button
android:layout_column=“1″
android:id=“@+id/save“
android:layout_width=“wrap_content“
android:layout_height=“wrap_content“
android:layout_alignParentRight=“true“
android:layout_marginLeft=“10dip“
android:text=“Speichern“ />

Um in den Activities der Anwendung direkten Zugriff auf die UI-Komponenten zu haben, wird das Dependency Injection Framework roboguice verwendet, welches zur Zeit in Version 1.2.2 unter folgendem Link zum Download bereit liegt: http://code.google.com/p/roboguice/wiki/Downloads

Zusätzlich zu roboguice wird noch die Google Library guice benötigt, die man unter folgendem Link erhält: http://code.google.com/p/google-guice/downloads/list

Hierbei ist zu beachten, dass nicht die aktuelle Version 3.0, sondern guice-2.0-no_aop.jar benötigt wird. Beide Libraries werden dem Classpath hinzugefügt. Nun wird im Android Manifest unter Application der Name roboguice.application.RoboApplication gesetzt. Dies ist nötig, da die Activities nun nicht mehr von Activity oder ListActivity direkt erben, sondern von RoboActivity bzw. RoboListActivity.

In der folgenden SettingsActivity wird über die Annotation @InjectView per Dependency Injection auf die im XML definierten UI-Elemente per Id zugegriffen:

public class SettingActivity extends RoboActivity {

private DatabaseManager databaseManager;
@InjectView(R.id.name)
private EditText nameText;
@InjectView(R.id.save)
private Button saveButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.setting);
databaseManager = new DatabaseManager();
final Settings settings = databaseManager.fetchSetting();
nameText.setText(settings.getName());
saveButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View view) {
settings.setName(nameText.getText().toString());
if (settings.getId() == null) {
databaseManager.createSettings(settings);
} else {
databaseManager.updateSettings(settings);
}
}
});
}

@Override
protected void onDestroy() {
super.onDestroy();
databaseManager.close();
}
}

In der onCreate-Methode wird ein DatabaseManager erzeugt, der sich um die Datenbank Connection kümmert, aber hierzu später noch mehr. Ebenfalls über den DatabaseManager werden die Settings bezogen, die in der UI dargestellt werden. Hierzu wird dem nameText, der Name aus den Settings zugewiesen. Nun wird noch dem Save-Button ein onClickListener hinzugefügt, der die Änderungen der UI dem Settings Objekt übergibt und dieses anschließend in der Datenbank speichert.

Wichtig ist, dass in der onDestroy Methode der Activity, die Datenbank-Connection wieder geschlossen wird – verlässt der Benutzer also die Einstellungen wird automatisch die Datenbank Verbindung zurück gesetzt.

Nun zur eigentlichen Interaktion mit der Datenbank über den DatabaseManager:

public class DatabaseManager {

private static final String DB_FILE = „/data/data/myapp.db“;
private SQLiteDatabase database;

public DatabaseManager() {
File file = new File(DB_FILE);
if (!file.exists()) {
database = SQLiteDatabase.openOrCreateDatabase(file, null);
database.execSQL(„create table Settings (id integer primary key autoincrement not null, name text);“);
} else {
database = SQLiteDatabase.openOrCreateDatabase(file, null);
}
}

public void close() {
database.close();
}

public Settings createSettings(Settings settings) {
ContentValues initialValues = createContentValues(settings);
long id = database.insert(„Settings“, null, initialValues);
settings.setId((int) id);
return settings;
}

public long updateSettings(Settings settings) {
ContentValues updateValues = createContentValues(settings);
return database.update(„Settings“, updateValues, „id=“ + settings.getId(), null);
}

public Settings fetchSetting() {
try {
Cursor cursor = database.query(„Settings“,
new String[] {
„id“, „name“
}, null, null, null, null, null);
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
Integer id = cursor.getInt(cursor.getColumnIndexOrThrow(„id“));
String name = cursor.getString(cursor.getColumnIndexOrThrow(„name“);
Settings settings = new Settings(id, name);
return settings;
} else {
return new Settings();
}
} catch (SQLiteException e) {
return new Settings();
}
}

private ContentValues createContentValues(Settings settings) {
ContentValues values = new ContentValues();
values.put(„name“, settings.getName());
return values;
}
}

Im Konstruktor, der in der onCreate-Methode der SettingsActivity aufgerufen wurde, wird zunächst überprüft, ob die Datenbank-Datei bereits angelegt wurde. Ist dies nicht der Fall, werden die Settings der Anwendung Initial gespeichert und zusätzlich zum Öffnen der Datenbank muss die Settings Tabelle mittels SQL-Statement generiert werden.

Der nächste Aufruf der SettingsActivity lädt die bereits gespeicherten Settings über die fetchSettings-Methode. Hier wird auf der Datenbank eine Query gefeuert, der die gewünschten Properties mitgegeben werden und die sonst keinerlei Einschränkung enthält, es werden also wirklich alle Settings geladen. Das Ergebnis ist über einen Cursor zugreifbar, der zunächst über die moveToFirst-Methode auf die erste Position gesetzt werden muss. Die über den Cursor zugreifbaren Properties werden in einem Settings Objekt gespeichert, bzw. falls die Datenbank noch keinen Eintrag enthält wird ein neues Settings Objekt erzeugt und der SettingsActivity geliefert.

Werden die Einstellungen vom Benutzer geändert und der Speicher-Button betätigt, wird das Settings-Objekt entweder Initial in der Datenbank gespeichert oder aktualisiert. Dies hängt davon ab, ob die Settings bereits eine eindeutige Id enthalten. Bei dem Update-Statement wird eine Where Clause benötigt, die eine Einschränkung auf die gegebene Id – also den Primär Schlüssel – enthält. Andernfalls wird das Objekt neu erzeugt und die von der Datenbank vergebene Id in dem Settings-Objekt gespeichert.

Beim Insert bzw. Update wird nicht das Settings-Objekt selber verwendet sondern das generische Objekt ContentValues, dem per put-Befehl die einzelnen Properties zugewiesen werden können.

Sobald man nun noch die Permissions im Android-Manifest richtig gesetzt hat, steht dem ersten Probelauf jetzt nichts mehr im Wege.

<uses-permission android:name= „android.permission.WRITE_EXTERNAL_STORAGE“/>
<permission-group android:name=“android.permission-group.STORAGE“/>

Insgesamt lässt sich sagen, dass der Zugriff auf die SQLite Datenbank bei Android Anwendungen recht komfortable gestaltet ist, gerade weil hier alle benötigten Libraries bereits gegeben sind. Nur Schade, dass man nicht direkt mit Entities arbeiten kann, sondern dass diese auf die Datenbank Inhalte gemappt werden müssen. Es wäre angenehmer auf die Cursor zu verzichten und besser direkt über die Entity an die gewünschten Properties zu gelangen – ebenso wie beim Mappen der Properties auf die ContentValues ein zusätzlicher Schritt benötigt wird.