So konfigurieren Sie Apollo für die Arbeit mit GraphQL in Android

Warum ein Artikel


Vor kurzem musste ich die Arbeit mit einem Backend in GraphQL implementieren. Im Gegensatz zu REST gibt es nicht so viele Tutorials zum Einrichten auf Android, und die meisten davon sind nicht mehr ganz relevant.

Was ist GraphQL?


GraphQL ist eine modische Alternative zur REST-API, mit der Sie Daten optimierter abfragen können und nur die Daten angeben, die Sie benötigen.

Umgebungseinstellung


Wir werden Anfragen an den Server über Apollo stellen - die derzeit beliebteste Bibliothek für die Arbeit mit GraphQL.

Lass uns zur Arbeit gehen. Fügen wir dem Manifest zunächst die erforderlichen Berechtigungen für die Arbeit mit dem Netzwerk hinzu:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Als nächstes müssen Sie Apollo verbinden. Wir gehen zum Hauptgebäude build.gradle und fügen im Abschnitt Abhängigkeiten die folgende Zeile hinzu:

classpath 'com.apollographql.apollo:apollo-gradle-plugin:2.0.0'

Jetzt müssen Sie der build.gradle-Datei des App-Moduls zusätzliche Abhängigkeiten hinzufügen:

implementation("com.apollographql.apollo:apollo-runtime:2.0.0")
implementation "com.apollographql.apollo:apollo-android-support:2.0.0"

Fügen Sie außerdem die Plug-In-Verbindung ganz oben in der Datei hinzu:

apply plugin: 'com.apollographql.apollo'

Nachdem das Projekt synchronisiert wurde, müssen wir die Codegenerierung der Modelle konfigurieren, mit denen wir Abfragen an GraphQL durchführen.

Lassen Sie uns die Entwicklungsumgebung minimieren und ein Terminal öffnen. Gehen Sie zu dem Ordner mit Ihrem Projekt:

cd /Users/user/Desktop/ProjectName

Wenn Sie noch kein npm haben, laden Sie es zuerst von der offiziellen Website herunter.

Installieren Sie apollo-codegen - ein Tool, mit dem Sie schema.json herunterladen können - eine Datei, die als Apollo-Quelle für die Generierung von Modellen dient:

npm install apollo-codegen

Laden Sie schema.json herunter (Sie müssen sich in dem Verzeichnis Ihres Projekts befinden, in dem der Ordner node_modules angezeigt wurde):

node_modules/.bin/apollo-codegen download-schema https://   api/ --output schema.json

Jetzt sehen wir im Projektordner die Datei schema.json. Es bleiben Apollo-Dateien zum Generieren von Modellen. Führen Sie dazu die folgenden Schritte aus.

Gehen Sie zum App-Ordner Ihres Projekts und dann zu src -> main. Hier müssen wir einen graphQL-Ordner erstellen. Hier werden wir unsere .graphql-Dateien hinzufügen.

Wir kopieren die im vorherigen Schritt heruntergeladene Datei in den erstellten Ordner - schema.json.
Die Umgebung ist eingerichtet . Gehen Sie zum Code

Modellcodegenerierung


Beginnen wir mit den .graphql-Dateien. Anfragen werden hier gespeichert.
In GraphQL gibt es zwei Arten von Abfragen:
Abfrage - Analogon der GET-
Mutation - Analogon von POST / PUT / DELETE

Angenommen, Sie bieten den Administratoren dieses Hotels einen Service zum Einchecken von Gästen in ein Hotelzimmer an. Die Anwendung kann 3 Funktionen ausführen. Melden Sie sich beim Benutzer (Administrator) an, erhalten Sie Informationen zum Raum anhand der ID und

füllen Sie die Gäste aus. Erstellen Sie die Datei loginUser.graphql in dem Verzeichnis app / src / main / graphQL, das im vorherigen Abschnitt erstellt wurde. Mit dieser Datei generiert Apollo ein Modell für den Benutzernamen.

Dateiinhalt:

mutation loginUser($email:String!, $password:String!) {
    login(
        user: {
            email: $email,
            password: $password
        }
    ){
        email,
        token,
        refreshToken
    }
}

Außerdem benötigen wir die Datei getRoom.graphql, mit deren Hilfe ein Modell generiert wird, um das Hotelzimmer zu erhalten:

query getRoom($room_id: String) {
    room(room_id: $room_id) {
        title,
        room_number,
        cheked_in_family_name,
        has_minibar
    }
}

Und die endgültige Datei ist checkUserIn.graphql check-in. Verwendet auch Mutation:

mutation checkInFamily($room_id: String!, $family_name: String!) {
    room(
        room: {
            title: $family_name,
            room_id: $room_id
        }
    ){
        room_id,
        family_name,
        minibar_products{
            title,
            weight
        }
    }
}

Wir erstellen das Projekt und sehen 3 generierte Modelle im Ordner app / build / generate / source / apollo / debug / service: GetRoomQuery, CheckUserInMutation, LoginUserMutation

Abfrageausführung


Erstellen Sie eine Singleton-NetworkService-Klasse, die ApolloClient bereitstellt. Wir machen 2 Methoden darin. getApolloClient () zum Ausführen von Anforderungen, für die kein Token oder zusätzliche Parameter erforderlich sind, und getApolloClientWithTokenInterceptor (), in das das Token geworfen wird, für Anforderungen:

import com.apollographql.apollo.ApolloClient
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request

class NetworkService {

  fun getApolloClient(): ApolloClient {
    val okHttp = OkHttpClient
      .Builder()
      .build()

    return ApolloClient.builder()
      .serverUrl(BASE_URL)
      .okHttpClient(okHttp)
      .build()
  }

  fun getApolloClientWithTokenInterceptor(token: String): ApolloClient {

    val httpClient = OkHttpClient.Builder()
      .addInterceptor(Interceptor { chain: Interceptor.Chain ->
        val original: Request = chain.request()
        
        val builder: Request.Builder = original
          .newBuilder()
          .method(original.method, original.body)
        
        builder.header("Authorization", "Bearer $token")
        return@Interceptor chain.proceed(builder.build())
      })
      .build()

    return ApolloClient.builder()
      .serverUrl(BASE_URL)
      .okHttpClient(httpClient)
      .build()
  }

  companion object {
    private var mInstance: NetworkService? = null

    fun getInstance(): NetworkService? {
      if (mInstance == null) {
        mInstance = NetworkService()
      }
      return mInstance
    }
  }
}

Nun gehen wir zu unserer Aktivität oder unserem Fragment, hier implementieren wir die Ausführung von Anfragen. Um zu beginnen, melden Sie sich an:

  private fun loginUser() {

    val client = NetworkService.getInstance()?.getApolloClient()
    val loginMutation = LoginUserMutation
      .builder()
      .email(emailEdit.text.toString())
      .password(passwordEdit.text.toString())
      .build()

    client
      ?.mutate(loginMutation)
      ?.enqueue(object : ApolloCall.Callback<LoginUserMutation.Data>() {

        override fun onResponse(response: Response<LoginUserMutation.Data>) {
          if (!response.hasErrors()) {
            val token = response.data?.login()?.token()
            val email = response.data?.login()?.email()
            // ,   ui,     
            runOnUiThread {
              //   ,  
            }
          }
        }

        override fun onFailure(e: ApolloException) {}
      })
  }

Dies ist ein Beispiel für die Arbeit mit Mutationen. Wir haben diese Anforderung ohne Token im Header ausgeführt. Schauen wir uns nun ein Beispiel für die Arbeit mit Abfragen an und versuchen, Informationen über den Raum zu erhalten. Angenommen, ein Raum wird uns nur mit einem Token gegeben. Wir führen die Anfrage wie folgt aus:

  private fun getRoom() {
    val token = "123456"
    val client = NetworkService.getInstance()
      ?.getApolloClientWithTokenIntercetor(token)

    val roomId = "123"
    val allRoomsQuery = GetRoomQuery(Input.fromNullable(roomId))

    client
      ?.query(allRoomsQuery)
      ?.enqueue(object : ApolloCall.Callback<GetRoomQuery.Data>() {

        override fun onResponse(response: Response<GetRoomQuery.Data>) {
          if (!response.hasErrors()) {
            val familyName = response.data?.room()?.family_name()
          }
        }

        override fun onFailure(e: ApolloException) {}
      })
  }

Versuchen Sie als Hausaufgabe, sich eine Implementierung der Ansiedlung von Gästen zu schreiben.

Nützliche Links:

Apollo-Client-Dokumentation für Android
Github Apollo-Client Android

All Articles