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.

Restlet – REST im OSGi Kontext

Veröffentlicht in: Eclipse, Java, RCP | 2

Der nachfolgende Artikel beschäftigt sich mit der Erstellung einer Client-Server Architektur, die mittels REST-Schnittstelle kommuniziert und auf dem OSGi-Framework aufbaut. Als REST-Framework wird http://www.restlet.org/ verwendet.

Zur Zeit liegt Restlet in der aktuellen Version 2.0.10 unter http://www.restlet.org/downloads/ zum Download bereit.

Zunächst wird für die Eclipse IDE eine neue Target-Platform erstellt, der als Location die Software Site http://download.eclipse.org/releases/indigo – die Indigo Update Site – hinzugefügt und hier die Equinox Target Components ausgewählt werden. Weiterhin wird als zweite Location auf das lib-Directory des entpackten Restlet-Archives verwiesen.

Kommen wir nun zur eigentlichen Programmierung. Insgesamt werden drei Plug-in Projects benötigt, jeweils ein Client/Server-Projekt und ein Projekt, welches die POJOs beinhaltet, die sowohl auf dem Client, als auch auf dem Server benötigt werden. In Letzteres wird eine User-Klasse erstellt, die zunächst nur aus Vor- und Nachname besteht.

Auf der Server-Seite wird eine eigene Implementierung der Klasse ServerResource benötigt, die eine Methode bereitstellt, um einen beliebigen User zu erhalten:

public class UserResource extends ServerResource {

@Get
public User getUser() {
return new User(„Max“, „Mustermann“);
}
}

Um beim Starten bzw. Stoppen des Server-Bundles den von Restlet bereitgestellten Jetty-Server hoch bzw. herunterzufahren, sind folgende zusätzliche Einträge im Server-Activator nötig:

    private Server server;
private int port = 8182;

public void start(BundleContext bC) throws Exception {
// … do what you have to do, while starting
server = new Server(Protocol.HTTP, port, UserResource.class);
server.start();
}

public void stop(BundleContext bC) throws Exception {
// … do what you have to do, while stopping …
server.stop();
}

Um den Server starten zu können, wird eine OSGI-Framework Run-Configuration benötigt, die zusätzlich zu den Equinox Target Components die Restlet-Bundles beinhaltet. Sollten einige der Restlet-Extensions Probleme bereiten, können diese getrost weg geklickt werden. Nun kann die soeben erstellte Run-Configuration ausgeführt und unter der URL http://localhost:8182/ die UserResource aufgerufen werden.

Natürlich sollte die UserResource nicht nur im Browser abrufbar sein, sondern von einem OSGi-Client konsumiert werden. Daher wird noch ein weiteres Bundle benötigt, wobei die Kommunikation mit dem Server denkbar einfach einfällt:

ClientResource clientResource = new ClientResource(„http://localhost:8182“);
User user = clientResource.get(User.class);

Wie man sieht, verwendet der Client ebenfalls das User-POJO, wobei die Serialisierung bzw. Deseriablisierung des User-Objekts vom Restlet-Framework umgesetzt wird. Dadurch lässt sich schnell und einfach eine REST-Kommunikation im OSGI Kontext realisieren. Zusätzlich zum OSGI Framework (Edition for Java SE) steht das Restlet-Framework noch für die Google AppEngine, GWT und die Android Entwicklung zur Verfügung. Schön das man nicht für jede Plattform ein unterschiedliches REST-Framework verwenden muss.