Erste Schritte mit AWS
Erstellen einer Android-Anwendung
Erstellen einer einfachen Android-Anwendung mit AWS Amplify

API und Datenbank hinzufügen
Modul 4: Hinzufügen einer GraphQL-API und einer Datenbank
In diesem Modul verwenden Sie die Amplify-CLI und -Bibliotheken zur Konfiguration und zum Hinzufügen einer GraphQL-API zu Ihrer Anwendung.
Einführung
Nachdem wir die Anwendung mit Benutzerauthentifizierung erstellt und konfiguriert haben, fügen wir nun eine API und CRUD-Operationen (Create, Read, Update, Delete) auf einer Datenbank hinzu.
In diesem Modul fügen Sie Ihrer Amplify-Anwendung unter Verwendung der Amplify-CLI und -Bibliotheken eine API hinzu. Die API, die Sie erstellen werden, ist eine GraphQL-API, die AWS AppSync (einen verwalteten GraphQL-Service) nutzt, der von Amazon DynamoDB (einer NoSQL-Datenbank) unterstützt wird. Eine Einführung in GraphQL finden Sie auf dieser Seite.
Die App, die Sie bauen werden, ist eine Notizbuch-App, mit der Benutzer Notizen erstellen, löschen und auflisten können. Dieses Beispiel gibt Ihnen eine gute Idee, wie Sie viele beliebte Arten von CRUD+L-Anwendungen (Erstellen, Lesen, Aktualisieren, Löschen und Auflisten) erstellen können.
Lerninhalte
- Erstellen und Bereitstellen einer GraphQL-API
- Schreiben von Front-End-Code zur Interaktion mit der API
Wichtige Konzepte
API – Stellt eine Programmierschnittstelle zur Verfügung, die Kommunikation und Interaktionen zwischen mehreren Software-Vermittlern ermöglicht.
GraphQL – Eine Abfragesprache und serverseitige API-Implementierung auf der Grundlage einer typisierten Darstellung Ihrer Anwendung. Diese API-Darstellung wird unter Verwendung eines Schemas deklariert, das auf dem GraphQL-Typsystem basiert. (Weitere Informationen über GraphQL finden Sie auf dieser Seite.)
Veranschlagte Zeit
20 Minuten
Verwendete Services
Implementierung
-
Erstellen eines GraphQL-API-Service und einer dazugehörigen Datenbank
Um die GraphQL-API und die dazugehörige Datenbank zu erstellen, öffnen Sie ein Terminal und führen Sie diesen Befehl von Ihrem Projektverzeichnis aus:
amplify add api ? Please select from one of the below mentioned services: select GraphQL and press enter ? Provide API name: select the default, press enter ? Choose the default authorization type for the API: use the arrow key to select Amazon Cognito User Pool and press enter ? Do you want to configure advanced settings for the GraphQL API: select the default No, I am done and press enter ? Do you have an annotated GraphQL schema? keep the default N and press enter ? What best describes your project: choose any model, we are going to replace it with our own anyway. Press enter ? Do you want to edit the schema now? type Y and press enter
Der Standardtexteditor, den Sie bei der Initialisierung des Projekts (amplify init) wählen, wird mit einem vordefinierten Datenschema geöffnet.
Löschen Sie das Schema und ersetzen Sie es durch unser Anwendungs-GraphQL-Schema:
type NoteData @model @auth (rules: [ { allow: owner } ]) { id: ID! name: String! description: String image: String }
Das Datenmodell besteht aus einer Klasse NoteData und 4 Eigenschaften: ID und Name sind obligatorisch. Beschreibung und Bild sind optionale Zeichenfolgen.
Der @Modell-Transformator zeigt an, dass wir eine Datenbank zur Speicherung dieser Daten erstellen möchten.
Der @auth-Transformator fügt Authentifizierungsregeln hinzu, um den Zugriff auf diese Daten zu ermöglichen. Für dieses Projekt möchten wir, dass nur der Eigentümer von NoteData Zugang zu diesen Daten hat.
Wenn Sie fertig sind, vergessen Sie nicht zu speichern und gehen Sie dann zurück zu Ihrem Terminal, um Amplify CLI mitzuteilen, dass Sie fertig sind.
? Press enter to continue, press enter.
Nach einigen Sekunden sollten Sie eine Erfolgsmeldung sehen:
GraphQL schema compiled successfully.
-
Clientseitigen Code erzeugen
Basierend auf der soeben von uns erstellten GraphQL-Datenmodelldefinition generiert Amplify clientseitigen Code (d. h. Swift-Code) zur Darstellung der Daten in unserer Anwendung.
Um den Code zu generieren, führen Sie in Ihrem Terminal den folgenden Befehl aus:
amplify codegen models
Dies erzeugt Java-Dateien im Verzeichnis java/com/amplifyframework.datastore.generated.model, wie Sie mit sehen können:
➜ Android Getting Started git:(master) ✗ ls -al app/src/main/java/com/amplifyframework/datastore/generated/model total 24 drwxr-xr-x 4 stormacq admin 128 Oct 7 15:27 . drwxr-xr-x 3 stormacq admin 96 Oct 7 15:27 .. -rw-r--r-- 1 stormacq admin 1412 Oct 7 15:27 AmplifyModelProvider.java -rw-r--r-- 1 stormacq admin 7153 Oct 7 15:27 NoteData.java
Die Dateien werden automatisch in Ihr Projekt importiert.
-
Bereitstellung von API-Service und -Datenbank
Um die Backend-API und die Datenbank, die wir gerade erstellt haben, bereitzustellen, gehen Sie zu Ihrem Terminal und führen Sie den Befehl aus:
amplify push # press Y when asked to continue ? Are you sure you want to continue? accept the default Y and press enter ? Do you want to generate code for your newly created GraphQL API type N and press enter
Nach einigen Minuten sollten Sie eine Erfolgsmeldung sehen:
✔ All resources are updated in the cloud GraphQL endpoint: https://yourid.appsync-api.eu-central-1.amazonaws.com/graphql
-
Hinzufügen der API-Client-Bibliothek zum Android Studio-Projekt
Bevor Sie zum Code gehen, gehen Sie zurück zu Android Studio, fügen Sie die folgende Abhängigkeit zur build.gradle Ihres Moduls zusammen mit anderen "Amplifyframework"-Implementierungen hinzu, die Sie zuvor hinzugefügt haben, und klicken Sie auf Jetzt synchronisieren, wenn Sie dazu aufgefordert werden:
dependencies { implementation 'com.amplifyframework:aws-api:1.4.0' implementation 'com.amplifyframework:aws-auth-cognito:1.4.0' }
-
Initialisieren von Amplify-Bibliotheken zur Laufzeit
Öffnen Sie die Datei Amplify Backend.kt und fügen Sie eine Zeile in der Amplify Initialisierungssequenz in der initialize()-Methode hinzu. Der vollständige Try/Catch-Block sollte wie folgt aussehen:
try { Amplify.addPlugin(AWSCognitoAuthPlugin()) Amplify.addPlugin(AWSApiPlugin()) Amplify.configure(applicationContext) Log.i(TAG, "Initialized Amplify") } catch (e: AmplifyException) { Log.e(TAG, "Could not initialize Amplify", e) }
-
Bridging zwischen GraphQL-Datenmodell und App-Modell hinzufügen
Unser Projekt verfügt bereits über ein Datenmodell zur Darstellung einer Notiz. In diesem Tutorial werden wir weiterhin dieses Modell verwenden und eine einfache Möglichkeit zur Konvertierung von Anmerkungsdaten in eine Notiz bereitstellen. Öffnen Sie UserData.kt und fügen Sie zwei Komponenten hinzu: eine dynamische Eigenschaft, die ein NoteData-Objekt aus einer UserData.note zurückgibt, und das Gegenteil: eine statische Methode, die eine API NoteData akzeptiert und eine Userdata.note zurückgibt.
Fügen Sie innerhalb der Datenklasse Hinweis Folgendes hinzu:
// return an API NoteData from this Note object val data : NoteData get() = NoteData.builder() .name(this.name) .description(this.description) .image(this.imageName) .id(this.id) .build() // static function to create a Note from a NoteData API object companion object { fun from(noteData : NoteData) : Note { val result = Note(noteData.id, noteData.name, noteData.description, noteData.image) // some additional code will come here later return result } }
Stellen Sie sicher, dass Sie die NoteData-Klasse aus dem generierten Code importieren.
-
Hinzufügen von API CRUD-Methoden zur Backend-Klasse
Fügen wir 3 Methoden zum Aufruf unserer API hinzu: eine Methode zum Abfragen der Notiz, eine Methode zum Erstellen einer neuen Notiz und eine Methode zum Löschen einer Notiz. Beachten Sie, dass diese Methode auf dem App-Datenmodell arbeitet (Hinweis), um eine einfache Interraktion von der Benutzeroberfläche aus zu ermöglichen. Diese Methode konvertiert Note transparent in die NoteData-Objekte von GraphQL.
Öffnen Sie die Datei Backend.kt und fügen Sie den folgenden Snippet am Ende der Backend-Klasse hinzu:
fun queryNotes() { Log.i(TAG, "Querying notes") Amplify.API.query( ModelQuery.list(NoteData::class.java), { response -> Log.i(TAG, "Queried") for (noteData in response.data) { Log.i(TAG, noteData.name) // TODO should add all the notes at once instead of one by one (each add triggers a UI refresh) UserData.addNote(UserData.Note.from(noteData)) } }, { error -> Log.e(TAG, "Query failure", error) } ) } fun createNote(note : UserData.Note) { Log.i(TAG, "Creating notes") Amplify.API.mutate( ModelMutation.create(note.data), { response -> Log.i(TAG, "Created") if (response.hasErrors()) { Log.e(TAG, response.errors.first().message) } else { Log.i(TAG, "Created Note with id: " + response.data.id) } }, { error -> Log.e(TAG, "Create failed", error) } ) } fun deleteNote(note : UserData.Note?) { if (note == null) return Log.i(TAG, "Deleting note $note") Amplify.API.mutate( ModelMutation.delete(note.data), { response -> Log.i(TAG, "Deleted") if (response.hasErrors()) { Log.e(TAG, response.errors.first().message) } else { Log.i(TAG, "Deleted Note $response") } }, { error -> Log.e(TAG, "Delete failed", error) } ) }
Stellen Sie sicher, dass Sie die Klassen ModelQuery, ModelMutation und NoteData aus dem generierten Code importieren.
Schließlich müssen wir die API aufrufen, um beim Start der Anwendung die Liste der Notiz für den aktuell angemeldeten Benutzer abzufragen.
Aktualisieren Sie in der Datei Backend.ktf die Methode updateUserData(withSignInStatus: Boolesches Wert) so, dass sie wie folgt aussieht:
// change our internal state and query list of notes private fun updateUserData(withSignedInStatus : Boolean) { UserData.setSignedIn(withSignedInStatus) val notes = UserData.notes().value val isEmpty = notes?.isEmpty() ?: false // query notes when signed in and we do not have Notes yet if (withSignedInStatus && isEmpty ) { this.queryNotes() } else { UserData.resetNotes() } }
Jetzt muss nur noch ein Stück der Benutzeroberfläche erstellt werden, um eine neue Notiz zu erstellen und eine Notiz aus der Liste zu löschen.
-
Hinzufügen einer Bearbeitungsschaltfläche zum Hinzufügen einer Notiz
Nachdem das Backend und die Datenmodellteile nun vorhanden sind, besteht der letzte Schritt in diesem Abschnitt darin, den Benutzern die Möglichkeit zu geben, eine neue Notiz zu erstellen und diese zu löschen.
a. Erstellen Sie in Android Studio unter res/layout ein neues Layout: Klicken Sie mit der rechten Maustaste auf Layout und wählen Sie Neu, dann Layout-Ressourcendatei. Nennen Sie ihn activity_add_note und akzeptieren Sie alle anderen Standardwerte. Klicken Sie auf OK.
Öffnen Sie die soeben erstellte Datei activity_add_note und ersetzen Sie den generierten Code durch Einfügen des unten stehenden Codes:
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:fillViewport="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="8dp"> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="Create a New Note" android:textSize="10pt" /> <EditText android:id="@+id/name" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:hint="name" android:inputType="text" android:lines="5" /> <EditText android:id="@+id/description" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:hint="description" android:inputType="textMultiLine" android:lines="3" /> <Space android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <Button android:id="@+id/addNote" style="?android:attr/buttonStyleSmall" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:backgroundTint="#009688" android:text="Add Note" /> <Button android:id="@+id/cancel" style="?android:attr/buttonStyleSmall" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:backgroundTint="#FFC107" android:text="Cancel" /> </LinearLayout> </ScrollView>
Dies ist ein sehr einfaches Layout, das die Eingabe eines Notizentitels und einer Beschreibung ermöglicht.
b. Fügen Sie eine AddNoteActivity-Klasse hinzu.
Erstellen Sie unter java/com.example.androidgettingstarted eine neue Kotlin-Datei AddActivityNote.kt, öffnen Sie sie und fügen Sie diesen Code hinzu:
package com.example.androidgettingstarted import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_add_note.* import java.util.* class AddNoteActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_add_note) cancel.setOnClickListener { this.finish() } addNote.setOnClickListener { // create a note object val note = UserData.Note( UUID.randomUUID().toString(), name?.text.toString(), description?.text.toString() ) // store it in the backend Backend.createNote(note) // add it to UserData, this will trigger a UI refresh UserData.addNote(note) // close activity this.finish() } } companion object { private const val TAG = "AddNoteActivity" } }
Öffnen Sie schließlich unter Manifeste die Datei AndroidManifest.xml und fügen Sie dieses Aktivitätselement an einer beliebigen Stelle innerhalb des Anwendungsknotens hinzu.
<activity android:name=".AddNoteActivity" android:label="Add Note" android:theme="@style/Theme.GettingStartedAndroid.NoActionBar"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.example.androidgettingstarted.MainActivity" /> </activity>
c. Fügen Sie eine FloatingActionButton "Notiz hinzufügen" in der Hauptaktivität hinzu. Öffnen Sie unter res/layout die Datei activity_main.xml und fügen Sie diese über der vorhandenen Schaltfläche für schwebende Aktionen hinzu.
<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fabAdd" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:visibility="invisible" android:src="@drawable/ic_baseline_post_add" app:fabCustomSize="60dp" app:fabSize="auto"/>
Fügen Sie ein Symbol für "Notiz hinzufügen" in Res/Drawable hinzu. Klicken Sie mit der rechten Maustaste auf zeichenbar, wählen Sie Neu, dann Vektor-Asset. Geben Sie ic_baseline_add als Namen ein und wählen Sie das Hinzufügen-Symbol aus der Clip-Art. Klicken Sie auf Weiter und Beenden.
d. Code hinzufügen, um die Schaltfläche "Notiz hinzufügen" zu behandeln.
Die letzten beiden Dinge, die man tun muss, um eine voll funktionsfähige "Hinzufügen-Schaltfläche" zu erhalten, sind, die Schaltfläche je nach isSignedIn-Wert ein- oder ausblenden zu lassen und natürlich Code hinzuzufügen, um Tippvorgänge auf der Schaltfläche zu verwalten.
Öffnen Sie mainActivity.kt und fügen Sie diese am Ende der Methode onCreate() hinzu:
// register a click listener fabAdd.setOnClickListener { startActivity(Intent(this, AddNoteActivity::class.java)) }
Ersetzen Sie dann noch in der Methode onCreate() die Datei UserData.isSignedIn.observe durch diese:
UserData.isSignedIn.observe(this, Observer<Boolean> { isSignedUp -> // update UI Log.i(TAG, "isSignedIn changed : $isSignedUp") //animation inspired by https://www.11zon.com/zon/android/multiple-floating-action-button-android.php if (isSignedUp) { fabAuth.setImageResource(R.drawable.ic_baseline_lock_open) Log.d(TAG, "Showing fabADD") fabAdd.show() fabAdd.animate().translationY(0.0F - 1.1F * fabAuth.customSize) } else { fabAuth.setImageResource(R.drawable.ic_baseline_lock) Log.d(TAG, "Hiding fabADD") fabAdd.hide() fabAdd.animate().translationY(0.0F) } })
Um zu überprüfen, ob alles wie erwartet funktioniert, erstellen Sie das Projekt. Klicken Sie auf das Menü Build und wählen Sie Projekt machen oder, auf Macs, geben Sie ⌘F9 ein. Es sollte kein Fehler vorliegen.
Wenn Sie die Anwendung ausführen, sehen Sie die Schaltfläche "Notiz hinzufügen", die erscheint, wenn sich ein Benutzer anmeldet, und verschwindet, wenn sich ein Benutzer abmeldet. Sie können nun eine Notiz hinzufügen.
-
Hinzufügen eines Streichen-zum-Löschen-Verhaltens
Das Streichen-zu-Löschen-Verhalten kann durch Hinzufügen eines Touch-Handlers zur Liste der Notizen hinzugefügt werden. Der Touch-Handler ist dafür zuständig, den roten Hintergrund und das Löschsymbol zu zeichnen und die Methode Backend.delete() aufzurufen, wenn der Touch losgelassen wird.
a. Erstellen Sie eine neue Klasse SimpleTouchCallback. Klicken Sie unter java/com mit der rechten Maustaste auf example.androidgettingstarted, wählen Sie Neu und dann Kotlin-Datei, geben Sie als Namen SwipeCallback ein.
Fügen Sie den untenstehenden Code in diese neue Datei ein:
package com.example.androidgettingstarted import android.graphics.Canvas import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import android.util.Log import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView // https://stackoverflow.com/questions/33985719/android-swipe-to-delete-recyclerview class SwipeCallback(private val activity: AppCompatActivity): ItemTouchHelper.SimpleCallback( 0, ItemTouchHelper.LEFT ) { private val TAG: String = "SimpleItemTouchCallback" private val icon: Drawable? = ContextCompat.getDrawable( activity, R.drawable.ic_baseline_delete_sweep ) private val background: ColorDrawable = ColorDrawable(Color.RED) override fun onChildDraw( c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean ) { super.onChildDraw( c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive ) val itemView = viewHolder.itemView val backgroundCornerOffset = 20 val iconMargin = (itemView.height - icon!!.intrinsicHeight) / 2 val iconTop = itemView.top + (itemView.height - icon.intrinsicHeight) / 2 val iconBottom = iconTop + icon.intrinsicHeight val iconRight: Int = itemView.right - iconMargin if (dX < 0) { val iconLeft: Int = itemView.right - iconMargin - icon.intrinsicWidth icon.setBounds(iconLeft, iconTop, iconRight, iconBottom) background.setBounds( itemView.right + dX.toInt() - backgroundCornerOffset, itemView.top, itemView.right, itemView.bottom ) background.draw(c) icon.draw(c) } else { background.setBounds(0, 0, 0, 0) background.draw(c) } } override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { Toast.makeText(activity, "Moved", Toast.LENGTH_SHORT).show() return false } override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) { Toast.makeText(activity, "deleted", Toast.LENGTH_SHORT).show() //Remove swiped item from list and notify the RecyclerView Log.d(TAG, "Going to remove ${viewHolder.adapterPosition}") // get the position of the swiped item in the list val position = viewHolder.adapterPosition // remove to note from the userdata will refresh the UI val note = UserData.deleteNote(position) // async remove from backend Backend.deleteNote(note) } }
Die wichtigen Codezeilen befinden sich in der Methode onSwiped(). Diese Methode wird aufgerufen, wenn die Streichbewegung beendet ist. Wir sammeln die Position in der Liste für das durchgezogene Element und entfernen den entsprechenden Hinweis aus der UserData-Struktur (dies aktualisiert die UI) und aus dem Cloud-Backend.
b. Nun, da wir eine Klasse haben, fügen wir ein "Löschen"-Symbol in res/drawable hinzu. Klicken Sie mit der rechten Maustaste auf zeichenbar, wählen Sie Neu, dann Vektor-Asset. Geben Sie ic_baseline_delete_sweep als Namen ein und wählen Sie das Symbol "delete sweep" aus der Clip-Art. Klicken Sie auf Weiter und Beenden.
c. Fügen Sie den unten stehenden Code in diese neue Datei ein: Fügen Sie den Gesture-Handler "Streichen zum Löschen" zur RecyclerView hinzu.
Öffnen Sie unter java/com/example.androidgettingstarted die Datei MainActivity.kt und fügen Sie diese beiden Codezeilen in setupRecyclerView hinzu:
// add a touch gesture handler to manager the swipe to delete gesture val itemTouchHelper = ItemTouchHelper(SwipeCallback(this)) itemTouchHelper.attachToRecyclerView(recyclerView)
-
Erstellen und testen
Um zu überprüfen, ob alles wie erwartet funktioniert, bauen und betreiben Sie das Projekt. Klicken Sie auf das Symbol Ausführen ▶️ in der Symbolleiste oder geben Sie ^ R ein. Es sollte kein Fehler vorliegen.
Vorausgesetzt, Sie sind noch angemeldet, startet die App auf der Mitarbeiterliste. Es hat jetzt eine Schaltfläche Notiz hinzufügen, um eine Notiz hinzuzufügen. Tippen Sie auf das Zeichen Notiz hinzufügen, geben Sie einen Titel und eine Beschreibung ein, tippen Sie auf die Schaltfläche Notiz hinzufügen und die Notiz sollte in der Liste erscheinen.
Sie können Notiz löschen, indem Sie eine Zeile nach links streichen.
Hier ist der vollständige Ablauf.
Fazit
Sie haben jetzt eine Android-App erstellt! Mit AWS Amplify haben Sie eine GraphQL-API hinzugefügt und die Erstellungs-, Lese- und Löschfunktionen in Ihrer Anwendung konfiguriert.
Im nächsten Modul werden wir die Benutzeroberfläche und das Verhalten zur Verwaltung von Bildern hinzufügen.