Von Android 2 nach Android 4 in sieben Schritten – Teil 2

Aus 2 mach 4

Im Teil 1 des Workshops zeigten die Anpassungen am Manifest sowie die neuen Fragmente und Layoutvorgaben im Detail. Der zweite Teil unseres Workshops zeigt die Anpassungen an der Datenbank, den neuen ActionBar und das modifizierte Preference-System.

Aus Android User 08/12

Bis einschließlich Android 2 war der SQLiteOpenHelper das Werkzeug der Wahl. Hier wurden vom Datenbank-Entwickler eines Projekts an zentraler Stelle alle Zugriffsmethoden erstellt auf die dann die Entwickler der grafischen Oberflächen zugreifen konnten. Der SQLiteOpenHelper stellt alle Mechanismen rund um die SQLite Datenbank zur Verfügung. Vom Anlegen und Upgrade der Datenbank selbst, bis hin zum Manipulieren der Datenbankinhalte lässt sich alles mit einer Subclass dieser Klasse erledigen. In den Activities wiederum wurden seitens der UI-Entwickler AsyncTasks genutzt um Daten unabhängig vom sensiblen UI-Thread zu manipulieren. Ab Android 3 gibt es hierfür die Loader.

Auf Heft-CD

Teil 1 des Workshops sowie den Quellcode und Binaries der hier besprochenen App finden Sie auf der Heft-CD.

CursorLoader

Leider wurden auch hier die Programmierer bestehender Anwendungen vermeintlich nicht mitgenommen. Das gesamte Klassengerüst funktioniert nur noch mit einem ContentProvider und nicht mehr mit einem Cursor. Das ist insofern verständlich, da das Cursor-Objekt erst durch die Loader geladen werden soll. In der Zeit vor dem CursorLoader hat der Entwickler den Cursor selbst laden müssen und, wenn er anständig gearbeitet hat, dazu eigenständige Tasks verwendet. Diese Bürde wird dem Entwickler nun mit den neuen CursorLoadern abgenommen.

Obwohl in der Android-Dokumentation nach wie vor an vielen Stellen explizit darauf hingewiesen wird, dass ContentProvider eigentlich nur für Zugriffe von außen gedacht sind, ist die Übergabe eines Query-String an den CursorLoader nicht möglich. Somit ist der Zugriff mittels CursorLoader auf lokale und App-interne Datenbestände ohne ContentProvider nicht möglich.

In unserer Beispiel-App (auf der Heft-CD) griffen wir zu einem Kniff, um nicht auch noch das komplette Datenbank-Handling in einen ContentProvider verlagern zu müssen. Eine Kopie der neuen AsyncTaskLoader Klasse (MyCursorLoader) mit einem minimalen Eingriff hilft dabei, auch ohne ContentProvider die Liste aller Einträge zu lesen. So ganz nebenbei macht die neue App dadurch von dem vorzüglichen Komfort der Loader Gebrauch, wie Listing 1 zeigt.

Listing 1

CursorLoader ohne ContentProvider

public class MyCursorLoader extends AsyncTaskLoader<Cursor> {
        // Ein neuer Konstruktor ...
    public MyCursorLoader(final Context context, final String selection, final String[] selectionArgs) {
        super(context);
        this.observer = new ForceLoadContentObserver();
        this.selection = selection;
        this.selectionArgs = selectionArgs;
    }
        // ... und eine gapatchte Methode
    @Override
    public Cursor loadInBackground() {
        Cursor cursor = null;
        if (uri != null) {
                cursor = getContext().getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
        } else if (selection != null) {
                        // Neu
                cursor = MyApplication.getSqliteDatabase().rawQuery(selection, selectionArgs);
        }
        if (cursor != null) {
            cursor.getCount();
            registerContentObserver(cursor, observer);
        }
        return cursor;
    }
}

Die neuen Loader kümmern sich um die asynchrone Datenbeschaffung, benachrichtigen mittels Callbacks über den aktuellen Status und sind auch für die Verwaltung der Cursor zuständig. Die unter Android 2 eminent wichtige Methode startManagingCursor() ist somit ebenfalls obsolet. Ein Beispiel zeigt Listing 2 .

Listing 2

Die neuen CursorLoader

public class FragmentList extends ListFragment
        implements LoaderManager.LoaderCallbacks<Cursor> {
        @Override
        public void onActivityCreated(Bundle bundle) {
                getActivity().getLoaderManager().initLoader(MyConstants.LDR_TABLE1LIST, null, this);
            adapter = new SimpleCursorAdapter(context,
                                                                                  R.layout.fragmentlist_row,
                                                                                  null,
                                                                                  new String[] { Table1.DESCRIPTION },
                                                                                  new int[] { R.id.fragmentlist_row_description },
                                                                                  0);
            setListAdapter(adapter);
        }
        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
                MyCursorLoader loader = null;
                switch (id) {
                        case MyConstants.LDR_TABLE1LIST:
                                loader = new MyCursorLoader(context,
                                                                                        MySQLiteOpenHelper.TABLE1_FETCH,
                                                                                        null);
                            break;
                }
                return loader;
    }
        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
                adapter.swapCursor(null);
        }
        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
                adapter.swapCursor(cursor);
                setListShown(true);
        }
}

Ein SimpleCursorAdapter wird angelegt und mit der Liste verbunden. Den Parameter für den Cursor lassen wir auf null, denn die neuen CursorLoader erlauben den dynamischen Austausch von Cursorn. Mit initLoader() wird der Datenbeschaffungsvorgang in Gang gesetzt. Der Eintrag onCreateLoader() fordert das Fragment auf, den CursorLoader zu liefern. Sobald die Daten anstehen, benachrichtigt der onLoadFinished() das Fragment und der Cursor wird dem Adapter bekannt gegeben.

Die Datenbank

Durch den Kniff mit dem eigenen CursorLoader befinden sich das Anlegen und Verwalten der Datenbank sowie alle Zugriffsmethoden nach wie vor in einer Unterklasse des SQLiteOpenHelper. Die Datenbank wird in der Tablet-App nach wie vor an zentraler Stelle geöffnet und wieder geschlossen. Dazu bietet sich eine Subclass von Application (MyApplication) an. Damit Android diese Klasse berücksichtigt, muss es einen entsprechenden Eintrag im application Tag des Manifest geben (android:name="MyApplication"). Wie bei Java üblich gibt es auch bei Android immer verschiedene Wege zum Ziel. Ob man die Application-Klasse oder eigenständige Singletons zur zentralen Verwaltung der Datenbank nimmt oder die Datenbank in jedem Fragment oder jeder Activity öffnet, bleibt dem Entwickler überlassen. Die hier genutzte Methode hat sich im jahrelangen Praxistest bestens bewährt.

Letztendlich hebelt man mit einer zentralen Datenbankinstanz und dem Ignorieren der ContentProvider das auf Wiederverwendbarkeit getrimmte Konzept der Fragmente wieder aus. Das Fragment muss in unserem Beispiel Zugriff auf die Datenbankinstanz haben während die Verwendung von ContentProvidern dies besser kapseln könnte. Die zuvor gestellte Frage nach den Gründen für die Einschnitte bei der Datenbank wird somit an dieser Stelle beantwortet. Für eine Kleinstanwendung wie die Beispiel-App ist die gezeigte Herangehensweise durchaus in Ordnung. Jeder Entwickler sollte sich aber zum Start eines neuen Projektes genau überlegen wie re-usable seine Fragmente werden sollen. Gibt es nur den Hauch eines Zweifels dann sind ContentProvider ab sofort ein absolutes Muss.

Diesen Artikel kaufen

Als digitalen Artikel

Diesen Artikel als PDF kaufen.

Preis € 0,99



comments powered by Disqus
Android User empfiehlt den Preisvergleich idealo

Neueste Artikel

Samsung neues Galaxy S4 schlägt ein wie eine Bombe. In nur wenigen Tagen gingen Millionen des neuen Top-Modells über den Ladentisch. Doch ist das S4 so gut wie sein Ruf, oder ist der Erfolg nur den Samsung-Fans zu verdanken? Wir prüfen das Galaxy S4 für unseren ausführlichen Online-Test auf Herz und Nieren.

Am Anfang dieses Knobelspiels stehen Sie vor einem kleinen Acker bestehend aus vier Feldern. Diese müssen Sie bewässern. Das gelingt in den ersten Leveln problemlos, doch danach wird es deutlich schwieriger.

Der Bayerische Rundfunk möchte jüngeres Publikum ansprechen. Dazu startet der Sender heute seinen neuen Sender "PULS" zusammen mit Apps für iOS und natürlich auch Android. Die Apps dienen nicht nur als Online-Radio und News-Kanal. Mit Bewertungsmöglichkeiten und interaktiven Elementen sind Sie bei PULS immer am Puls der Zeit.