Einführung in Retrofit

Einführung in die Nachrüstung

1. Überblick

Retrofit ist ein typsicherer HTTP-Client für Android und Java - entwickelt von Square (Dagger,Okhttp).

In diesem Artikel erklären wir die Verwendung von Retrofit, wobei der Schwerpunkt auf den interessantesten Funktionen liegt. Insbesondere werden wir die synchrone und asynchrone API, ihre Verwendung bei der Authentifizierung, Protokollierung und einige gute Modellierungsmethoden erläutern.

2. Einrichten des Beispiels

Zunächst fügen wir die Retrofit-Bibliothek und den Gson-Konverter hinzu:


    com.squareup.retrofit2
    retrofit
    2.3.0


    com.squareup.retrofit2
    converter-gson
    2.3.0

Die neuesten Versionen finden Sie unterRetrofit undconverter-gson im Maven Central-Repository.

3. API-Modellierung

REST-Endpunkte von Retrofit-Modellen sind Java-Schnittstellen und daher sehr einfach zu verstehen und zu nutzen.

Wir modellieren dieuser API von GitHub. Dies hat einenGET-Endpunkt, der diesen im JSON-Format zurückgibt:

{
  login: "mojombo",
  id: 1,
  url: "https://api.github.com/users/mojombo",
  ...
}

Retrofit funktioniert, indem über eine Basis-URL modelliert wird und Schnittstellen die Entitäten vom REST-Endpunkt zurückgeben.

Der Einfachheit halber werden wir einen kleinen Teil des JSON übernehmen, indem wir unsereUser-Klasse modellieren, die die Werte annimmt, wenn wir sie erhalten haben:

public class User {
    private String login;
    private long id;
    private String url;
    // ...

    // standard getters an setters

}

Wir können sehen, dass wir für dieses Beispiel nur eine Teilmenge von Eigenschaften verwenden. Retrofit won’t complain about missing properties – since it only maps what we need, es wird sich nicht einmal beschweren, wenn wir Eigenschaften hinzufügen, die nicht im JSON enthalten sind.

Jetzt können wir zur Schnittstellenmodellierung übergehen und einige der Nachrüstanmerkungen erläutern:

public interface UserService {

    @GET("/users")
    public Call> getUsers(
      @Query("per_page") int per_page,
      @Query("page") int page);

    @GET("/users/{username}")
    public Call getUser(@Path("username") String username);
}

Die mit Anmerkungen versehenen Metadaten reichen aus, damit das Tool funktionsfähige Implementierungen generieren kann.

Die Annotation@GETteilt dem Client mit, welche HTTP-Methode und auf welcher Ressource verwendet werden soll. Wenn Sie beispielsweise eine Basis-URL von "https://api.github.com" angeben, wird die Anforderung an "https:" gesendet. //api.github.com/users ”.

Das führende "/" auf unserer relativen URLtells Retrofit that it is an absolute path on the host.

Beachten Sie außerdem, dass wir vollständig optionale@Query-Parameter verwenden, die als null übergeben werden können, wenn wir sie nicht benötigen. Das Tool ignoriert diese Parameter, wenn sie keine Werte haben.

Und zu guter Letzt können wir mit@Path einen Pfadparameter angeben, der anstelle des im Pfad verwendeten Markups platziert wird.

4. Synchronous/Asynchronous API

Um einen HTTP-Anforderungsaufruf zu erstellen, müssen wir zuerst unser Retrofit-Objekt erstellen:

OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
Retrofit retrofit = new Retrofit.Builder()
  .baseUrl("https://api.github.com/")
  .addConverterFactory(GsonConverterFactory.create())
  .client(httpClient.build())
  .build();

Retrofit bietet einen praktischen Builder für die Erstellung unseres gewünschten Objekts. It needs the base URL which is going to be used for every service call and a converter factory - kümmert sich um das Parsen der von uns gesendeten Daten und auch um die Antworten, die wir erhalten.

In diesem Beispiel verwenden wirGsonConverterFactory, wodurch unsere JSON-Daten der zuvor definierten KlasseUserzugeordnet werden.

Es ist wichtig zu beachten, dass verschiedene Fabriken unterschiedlichen Zwecken dienen. Denken Sie also daran, dass wir Fabriken auch für XML, Protopuffer oder sogar für ein benutzerdefiniertes Protokoll erstellen können. Eine Liste der bereits implementierten Fabriken finden Sie unterhere.

Die letzte Abhängigkeit istOKHttpClient - ein HTTP- und HTTP / 2-Client für Android- und Java-Anwendungen. Dies wird sich um die Verbindung zum Server und das Senden und Abrufen von Informationen kümmern. Wir können auch Header und Interceptors für jeden Anruf hinzufügen, die wir in unserem Abschnitt zur Authentifizierung sehen werden.

Nachdem wir unser Retrofit-Objekt haben, können wir unseren Serviceabruf erstellen. Schauen wir uns an, wie dies synchron durchgeführt wird:

UserService service = retrofit.create(UserService.class);
Call callSync = service.getUser("eugenp");

try {
    Response response = callSync.execute();
    User user = response.body();
} catch (Exception ex) { ... }

Hier sehen wir, wie Retrofit den Aufbau unserer Serviceschnittstelle übernimmt, indem wir den Code eingeben, der für die Anforderung erforderlich ist, basierend auf unseren vorherigen Anmerkungen.

Danach erhalten wir einCall<User>-Objekt, das zum Ausführen der Anforderung an die GitHub-API verwendet wird. The most important method here is execute,, mit dem ein Aufruf synchron ausgeführt wird und der den aktuellen Thread während der Datenübertragung blockiert.

Nachdem der Aufruf erfolgreich ausgeführt wurde, können wir dank unsererGsonConverterFactory den Hauptteil der Antwort abrufen - bereits auf einem Benutzerobjekt.

Das Tätigen eines synchronen Anrufs ist sehr einfach. In der Regel verwenden wir jedoch eine nicht blockierende asynchrone Anforderung:

UserService service = retrofit.create(UserService.class);
Call callAsync = service.getUser("eugenp");

callAsync.enqueue(new Callback() {
    @Override
    public void onResponse(Call call, Response response) {
        User user = response.body();
    }

    @Override
    public void onFailure(Call call, Throwable throwable) {
        System.out.println(throwable);
    }
});

Anstelle der Methode execute verwenden wir jetzt die Methodeenqueue, die eineCallback<User>-Sinterschnittstelle als Parameter verwendet, um den Erfolg oder Misserfolg der Anforderung zu behandeln. Beachten Sie, dass dies in einem separaten Thread ausgeführt wird.

Nachdem der Anruf erfolgreich beendet wurde, können wir den Body auf dieselbe Weise wie zuvor abrufen.

5. Erstellen einer wiederverwendbarenServiceGenerator-Klasse

Nachdem wir nun gesehen haben, wie unser Retrofit-Objekt erstellt und eine API verwendet wird, können wir sehen, dass wir den Builder nicht immer wieder schreiben möchten.

Was wir wollen, ist eine wiederverwendbare Klasse, mit der wir dieses Objekt einmal erstellen und für die Lebensdauer unserer Anwendung wiederverwenden können:

public class GitHubServiceGenerator {

    private static final String BASE_URL = "https://api.github.com/";

    private static Retrofit.Builder builder
      = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create());

    private static Retrofit retrofit = builder.build();

    private static OkHttpClient.Builder httpClient
      = new OkHttpClient.Builder();

    public static  S createService(Class serviceClass) {
        return retrofit.create(serviceClass);
    }
}

Die gesamte Logik zum Erstellen des Retrofit-Objekts wird jetzt in dieseGitHubServiceGenerator-Klasse verschoben. Dies macht es zu einer nachhaltigen Client-Klasse, die verhindert, dass sich der Code wiederholt.

Hier ist ein einfaches Beispiel für die Verwendung:

UserService service
  = GitHubServiceGenerator.createService(UserService.class);

Wenn wir zum Beispiel einRepositoryService, erstellen würden, könnten wir diese Klasse wiederverwenden und die Erstellung vereinfachen.

In the next section werden wir es erweitern und Authentifizierungsfunktionen hinzufügen.

6. Authentifizierung

Die meisten APIs verfügen über eine Authentifizierung, um den Zugriff darauf zu sichern.

Unter Berücksichtigung unserer vorherigen Generatorklasse werden wir eine Erstellungsdienstmethode hinzufügen, die ein JWT-Token mit dem HeaderAuthorizationverwendet:

public static  S createService(Class serviceClass, final String token ) {
   if ( token != null ) {
       httpClient.interceptors().clear();
       httpClient.addInterceptor( chain -> {
           Request original = chain.request();
           Request request = original.newBuilder()
             .header("Authorization", token)
             .build();
           return chain.proceed(request);
       });
       builder.client(httpClient.build());
       retrofit = builder.build();
   }
   return retrofit.create(serviceClass);
}

Um unserer Anfrage einen Header hinzuzufügen, müssen wir die Interceptor-Funktionen vonOkHttp verwenden. Dazu verwenden wir unseren zuvor definierten Builder und rekonstruieren das Retrofit-Objekt.

Beachten Sie, dass dies ein einfaches Auth-Beispiel ist, aber mit der Verwendung von Interceptors können wir jede Authentifizierung wie OAuth, Benutzer / Passwort usw. verwenden.

7. Protokollierung

In diesem Abschnitt werden wir unsereGitHubServiceGenerator für Protokollierungsfunktionen weiter erweitern, die für Debugging-Zwecke in jedem Projekt sehr wichtig sind.

Wir werden unser bisheriges Wissen über Interzeptoren nutzen, aber wir benötigen eine zusätzliche Abhängigkeit, nämlichHttpLoggingInterceptor von OkHttp. Fügen wir sie zu unserenpom.xml hinzu:


    com.squareup.okhttp3
    logging-interceptor
    3.9.0

Erweitern wir nun unsere KlasseGitHubServiceGenerator:

public class GitHubServiceGenerator {

    private static final String BASE_URL = "https://api.github.com/";

    private static Retrofit.Builder builder
      = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create());

    private static Retrofit retrofit = builder.build();

    private static OkHttpClient.Builder httpClient
      = new OkHttpClient.Builder();

    private static HttpLoggingInterceptor logging
      = new HttpLoggingInterceptor()
        .setLevel(HttpLoggingInterceptor.Level.BASIC);

    public static  S createService(Class serviceClass) {
        if (!httpClient.interceptors().contains(logging)) {
            httpClient.addInterceptor(logging);
            builder.client(httpClient.build());
            retrofit = builder.build();
        }
        return retrofit.create(serviceClass);
    }

    public static  S createService(Class serviceClass, final String token) {
        if (token != null) {
            httpClient.interceptors().clear();
            httpClient.addInterceptor( chain -> {
                Request original = chain.request();
                Request.Builder builder1 = original.newBuilder()
                  .header("Authorization", token);
                Request request = builder1.build();
                return chain.proceed(request);
            });
            builder.client(httpClient.build());
            retrofit = builder.build();
        }
        return retrofit.create(serviceClass);
    }
}

Dies ist die endgültige Form unserer Klasse. Wir können sehen, wie wir dieHttpLoggingInterceptor hinzugefügt haben, und wir legen sie für die Basisprotokollierung fest, die die Zeit protokolliert, die benötigt wurde, um die Anforderung, den Endpunkt und den Status für jede Klasse zu erstellen Anfrage usw.

Es ist wichtig, einen Blick darauf zu werfen, wie wir prüfen, ob der Abfangjäger vorhanden ist, damit wir ihn nicht versehentlich zweimal hinzufügen.

8. Fazit

In diesem umfangreichen Handbuch haben wir uns mit der exzellenten Retrofit-Bibliothek befasst und uns auf die Sync / Async-API sowie einige bewährte Methoden für Modellierung, Authentifizierung und Protokollierung konzentriert.

Die Bibliothek kann auf sehr komplexe und nützliche Weise verwendet werden. Für einen erweiterten Anwendungsfall mit RxJava werfen Sie bitte einen Blick aufthis tutorial.

Und wie immer kann der Quellcodeover on GitHub gefunden werden.