Démarrer avec AWS
Créer une application Android
Créer une application Android simple avec AWS Amplify

ajouter une api et une base de données
Module 4 : ajouter une API GraphQL et une base de données
Dans ce module, vous allez utiliser la ligne d'interface de commande Amplify et des bibliothèques pour configurer et ajouter une API GraphQL à votre application.
Introduction
Maintenant que nous avons créé et configuré l'application avec un système d'authentification, nous allons ajouter une API ainsi que les opérations CRUD (créer, lire, modifier, supprimer) sur une base de données.
Dans ce module, vous allez ajouter une API à votre application en utilisant l'interface de ligne commande Amplify et des bibliothèques. Vous allez créer une API GraphQL exploitant AWS AppSync (un service GraphQL géré), qui est soutenu par Amazon DynamoDB (une base de données NoSQL). Pour une introduction à GraphQL, cliquez ici.
Vous allez créer une application de prise de notes permettant aux utilisateurs de créer, supprimer et répertorier les notes. Cet exemple démontre clairement comment créer de nombreux types populaires d'applications CRUD+L (créer, lire, mettre à jour, supprimer et répertorier).
Ce que vous apprendrez
- Créer et déployer une API GraphQL
- Écrire du code front-end pour interagir avec l'API
Concepts clés
API : fournit une interface de programmation qui permet à plusieurs intermédiaires logiciels de communiquer et d'interagir.
GraphQL : une implémentation d'API côté serveur et de langage de requête basée sur une représentation typée de votre application. Cette représentation d'API est déclarée en utilisant un schéma basé sur le système de type GraphQL. (Pour en savoir plus sur GraphQL, consultez cette page.)
Durée
20 minutes
Services utilisés
Implémentation
-
Créer un service d'API GraphQL et une base de données
Pour créer l'API GraphQL et sa base de données, ouvrez un terminal et exécutez cette commande depuis le répertoire de votre projet :
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
L'éditeur de texte par défaut choisi à l'initialisation du projet (amplify init) s'ouvre avec un schéma de données préconfiguré.
Supprimez ce schéma et remplacez-le par celui de notre application GraphQL :
type NoteData @model @auth (rules: [ { allow: owner } ]) { id: ID! name: String! description: String image: String }
Le modèle de données comprend une classe NodeData et quatre propriétés : « id » et « name » sont obligatoires, tandis que « description » et « image » sont des chaînes facultatives.
Le transformateur @model indique que nous souhaitons créer une base de données pour stocker les données.
Le transformateur @auth ajoute des règles d'authentification permettant l'accès à ces données. Pour ce projet, nous n'accorderons l'accès qu'au propriétaire de NodeData.
Après avoir enregistré, revenez à votre terminal pour indiquer à la CLI Amplify que vous avez terminé.
? Press enter to continue, press enter.
Ce message de confirmation s'affiche après quelques secondes :
GraphQL schema compiled successfully.
-
Générer le code côté client
Grâce à la définition du modèle de données GraphQL que nous venons de créer, Amplify génère le code côté client (c.-à-d. code Swift) pour représenter les données dans notre application.
Pour générer le code, exécutez la commande suivante dans votre terminal :
amplify codegen models
Des fichiers Java sont ainsi créés dans le répertoire java/com/amplifyframework.datastore.generated.model, comme on le voit ci-dessous :
➜ 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
Les fichiers sont automatiquement importés dans votre projet.
-
Déployer le service d'API et la base de données
Pour déployer l'API backend et la base de données que nous venons de créer, ouvrez votre terminal et exécutez cette commande :
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
Ce message de confirmation s'affiche après quelques minutes :
✔ All resources are updated in the cloud GraphQL endpoint: https://yourid.appsync-api.eu-central-1.amazonaws.com/graphql
-
Ajouter la bibliothèque client d'API au projet Android Studio
Avant d'accéder au code, revenez sous Android Studio, ajoutez cette dépendance au build.gradle de votre module avec les autres implémentations 'amplifyframework' ajoutées auparavant, puis cliquez sur Sync Now (Synchroniser maintenant) :
dependencies { implementation 'com.amplifyframework:aws-api:1.4.0' implementation 'com.amplifyframework:aws-auth-cognito:1.4.0' }
-
Initialiser les bibliothèques Amplify à l'exécution
Ouvrez Backend.kt et ajoutez une ligne dans la séquence d'initialisation d'Amplify en suivant la méthode initialize(). Le bloc try/catch complet doit ressembler à ceci :
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) }
-
Ajouter un pont entre le modèle de données GraphQL et le modèle d'application
Notre projet dispose déjà d'un modèle de données pour représenter une note. Dans ce tutoriel, nous continuerons d'utiliser ce modèle et allons fournir un moyen simple pour convertir un objet NoteData en Note. Ouvrez UserData.kt et ajoutez deux composants : une propriété dynamique renvoyant un objet NoteData à partir d'un élément UserData.Note, et l'inverse : une méthode statique qui accepte un objet NoteData d'API et renvoie un élément Userdata.Note.
Dans la classe de données Note, ajoutez ces lignes :
// 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 } }
Pensez à importer la classe NoteData à partir du code généré.
-
Ajouter des méthodes CRUD d'API à la classe Backend
Nous allons ajouter trois méthodes d'appel de notre API : interroger la Note, créer une nouvelle Note et supprimer une Note. Ces méthodes fonctionnent sur le modèle de données de l'application (Note) pour simplifier l'interaction à partir de l'interface utilisateur. Elles permettent de convertir en toute transparence une Note en objets NoteData GraphQL.
Ouvrez le fichier Backend.kt et ajoutez cet extrait de code à la fin de la classe Backend :
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) } ) }
Pensez à importer ModelQuery, ModelMutation et NoteData à partir du code généré.
Enfin, nous devons appeler l'API pour interroger la liste de notes de l'utilisateur connecté au démarrage de l'application.
Dans le fichier Backend.kt, modifiez la méthode updateUserData(withSignInStatus: Boolean) comme suit :
// 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() } }
Maintenant, il ne nous reste plus qu'à créer un élément d'interface utilisateur permettant de créer une nouvelle note et de supprimer une note de la liste.
-
Ajouter un bouton pour ajouter une note
Maintenant que le backend et le modèle de données sont en place, il nous faut permettre aux utilisateurs de créer et de supprimer une note.
a. Dans Android Studio, sous res/layout, créez une nouvelle disposition : cliquez sur le bouton droit de la souris sur layout (disposition) et choisissez New, (Nouveau) puis Layout Resource File (Fichier de ressource de disposition). Nommez ce fichier activity_add_note et acceptez toutes les autres valeurs par défaut. Cliquez sur OK.
Ouvrez le fichier activity_add_note que nous venons de créer, puis remplacez le code généré par l'extrait ci-dessous :
<?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>
Cette disposition très simple permet de saisir un titre et une description de note.
b. Ajoutez une classe AddNoteActivity.
Sous java/com.example.androidgettingstarted, créez un nouveau fichier Kotlin AddActivityNote.kt. Ouvrez-le puis ajoutez ce code :
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" } }
Enfin, sous manifests (manifestes), ouvrez AndroidManifest.xml et ajoutez cet élément d'activité où vous le souhaitez dans le nœud d'application.
<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. Ajoutez un bouton d'action flottant (FloatingActionButton) « Add Note » (Ajouter une note) dans l'activité principale. Sous res/layout, ouvrez activity_main.xml et ajoutez cet extrait de code au-dessus du bouton d'action flottant existant.
<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"/>
Ajoutez une icône « Add Note » (Ajouter une note) dans res/drawable. Cliquez avec le bouton droit de la souris sur drawable, sélectionnez New (Nouveau), puis Vector Asset (Ressource vectorielle). Saisissez le nom ic_baseline_add et choisissez l'icône Ajouter depuis le Clip Art. Cliquez sur Next (Suivant) puis Finish (Terminer).
d. Ajoutez du code pour gérer le bouton « Add Note ».
Afin de rendre pleinement fonctionnel le bouton « Add Note »; nous devons le faire apparaître ou disparaître en fonction de la valeur isSignedIn. Nous devons en outre ajouter du code afin de gérer le comportement du bouton lors de son utilisation.
Ouvrez mainActivity.kt et ajoutez cet extrait à la fin de la méthode onCreate() :
// register a click listener fabAdd.setOnClickListener { startActivity(Intent(this, AddNoteActivity::class.java)) }
Ensuite, toujours dans la méthode onCreate(), remplacez UserData.isSignedIn.observe par :
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) } })
Pour vérifier que tout fonctionne comme prévu, générez le projet. Cliquez dans le menu Build (Générer) et sélectionnez Make Project (Créer le projet), ou, sur Mac, appuyez sur ⌘F9. Il ne devrait pas y avoir d'erreur.
Quand vous exécutez l'application, un bouton « Add Note » (Ajouter une note) apparaîtra ou disparaîtra lorsqu'un utilisateur se connecte ou se déconnecte. Vous pouvez désormais ajouter une note.
-
Ajouter un comportement « balayer pour supprimer »
Le comportement « balayer pour supprimer » peut être créé en ajoutant un gestionnaire tactile à la liste de notes. Le gestionnaire tactile fait apparaître l'arrière-plan rouge ainsi que l'icône Supprimer. Il appelle la méthode Backend.delete() lorsque le bouton est relâché.
a. Créez une nouvelle classe SimpleTouchCallback. Sous java/com, cliquez avec le bouton droit de la souris sur example.androidgettingstarted, sélectionnez New (Nouveau) puis Kotlin File (Fichier Kotlin). Saisissez le nom SwipeCallback.
Collez le code ci-dessous dans ce nouveau fichier :
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) } }
Les lignes de code importantes se trouvent dans la méthode onSwiped(). Celle-ci est appelée à la fin du geste de balayage. Nous récupérons la position de l'élément balayé dans la liste, puis supprimons la note correspondante de la structure UserData (l'UI est mise à jour) et du backend cloud.
b. Maintenant que la classe est créée, nous allons ajouter une icône « Supprimer » dans res/drawable. Cliquez avec le bouton droit de la souris sur drawable, sélectionnez New (Nouveau), puis Vector Asset (Ressource vectorielle). Saisissez le nom ic_baseline_delete_sweep et choisissez l'icône « delete sweep » depuis le Clip Art. Cliquez sur Next (Suivant) puis Finish (Terminer).
c. Collez le code ci-dessous dans ce nouveau fichier afin d'ajouter le gestionnaire du geste « balayer pour supprimer » à la vue RecyclerView.
Sous java/com/example.androidgettingstarted, ouvrez MainActivity.kt et ajoutez ces deux lignes de code dans setupRecyclerView :
// add a touch gesture handler to manager the swipe to delete gesture val itemTouchHelper = ItemTouchHelper(SwipeCallback(this)) itemTouchHelper.attachToRecyclerView(recyclerView)
-
Générer et tester
Pour vérifier que tout fonctionne comme prévu, générez et exécutez le projet. Cliquez sur l'icône Exécuter ▶️ dans la barre d'outils ou tapez ^ R. Il ne devrait pas y avoir d'erreur.
Si vous êtes toujours connecté, l'application démarre à partir d'une liste vide. Elle comporte désormais un bouton « Add Note » permettant d'ajouter une note. Appuyez sur le symbole du bouton en question, saisissez un titre, une description, puis appuyez sur le bouton Add Note. La note devrait apparaître dans la liste.
Vous pouvez désormais supprimer une note en balayant sa ligne vers la gauche.
Voici la procédure complète.
Conclusion
Vous avez créé une application Android ! En utilisant AWS Amplify, vous avez ajouté une API GraphQL et configuré des fonctionnalités de création, de lecture et de suppression dans votre application.
Dans le prochain module, nous ajouterons une interface utilisateur ainsi que des règles de gestion des images.