Introduzione al retrofit

1. Panoramica

Retrofit è un client HTTP indipendente dai tipi per Android e Java, sviluppato da Square (Dagger, Okhttp).

In questo articolo spiegheremo come utilizzare Retrofit, con un focus sulle sue caratteristiche più interessanti. Più in particolare, discuteremo l'API sincrona e asincrona, come usarla con l'autenticazione, la registrazione e alcune buone pratiche di modellazione.

2. Impostazione dell'esempio

Inizieremo aggiungendo la libreria Retrofit e il convertitore Gson:

 com.squareup.retrofit2 retrofit 2.3.0   com.squareup.retrofit2 converter-gson 2.3.0 

Per le ultime versioni, dai un'occhiata a Retrofit e converter-gson nel repository Maven Central.

3. Modellazione API

Retrofit modella gli endpoint REST come interfacce Java, rendendoli molto semplici da comprendere e utilizzare.

Modelleremo l'API utente da GitHub; questo ha un endpoint GET che restituisce questo in formato JSON:

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

Retrofit funziona modellando su un URL di base e facendo in modo che le interfacce restituiscano le entità dall'endpoint REST.

Per semplicità, prenderemo una piccola parte del JSON modellando la nostra classe User che prenderà i valori quando li avremo ricevuti:

public class User { private String login; private long id; private String url; // ... // standard getters an setters }

Possiamo vedere che stiamo prendendo solo un sottoinsieme di proprietà per questo esempio. Retrofit non si lamenterà delle proprietà mancanti - poiché mappa solo ciò di cui abbiamo bisogno , non si lamenterà nemmeno se dovessimo aggiungere proprietà che non sono nel JSON.

Ora possiamo passare alla modellazione dell'interfaccia e spiegare alcune delle annotazioni Retrofit:

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); }
    

I metadati forniti con le annotazioni sono sufficienti per lo strumento per generare implementazioni funzionanti.

L' annotazione @GET indica al client quale metodo HTTP utilizzare e su quale risorsa, quindi, ad esempio, fornendo un URL di base di "//api.github.com" invierà la richiesta a "//api.github.com / utenti ".

La "/" iniziale sul nostro URL relativo indica a Retrofit che si tratta di un percorso assoluto sull'host.

Un'altra cosa da notare è che utilizziamo parametri @Query completamente opzionali , che possono essere passati come nulli se non ne abbiamo bisogno, lo strumento si occuperà di ignorare questi parametri se non hanno valori.

E, ultimo ma non meno importante, @Path ci consente di specificare un parametro di percorso che verrà inserito al posto del markup che abbiamo usato nel percorso.

4. API sincrona / asincrona

Per costruire una chiamata di richiesta HTTP, dobbiamo prima costruire il nostro oggetto Retrofit:

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

Retrofit fornisce un comodo builder per costruire il nostro oggetto richiesto. Ha bisogno dell'URL di base che verrà utilizzato per ogni chiamata di servizio e di una fabbrica di convertitori, che si occupa dell'analisi dei dati che stiamo inviando e anche delle risposte che otteniamo.

In questo esempio, utilizzeremo GsonConverterFactory , che mapperà i nostri dati JSON alla classe User definita in precedenza.

È importante notare che le diverse fabbriche hanno scopi diversi, quindi tieni presente che possiamo anche utilizzare le fabbriche per XML, proto-buffer o persino crearne una per un protocollo personalizzato. Per un elenco delle fabbriche già implementate, possiamo dare un'occhiata qui.

L'ultima dipendenza è OKHttpClient , che è un client HTTP e HTTP / 2 per applicazioni Android e Java. Questo si occuperà della connessione al server e dell'invio e del recupero delle informazioni. Potremmo anche aggiungere intestazioni e intercettori per ogni chiamata, che vedremo nella nostra sezione di autenticazione.

Ora che abbiamo il nostro oggetto Retrofit, possiamo costruire la nostra chiamata di servizio, diamo un'occhiata a come farlo in modo sincrono:

UserService service = retrofit.create(UserService.class); Call callSync = service.getUser("eugenp"); try { Response response = callSync.execute(); User user = response.body(); } catch (Exception ex) { ... }

Qui possiamo vedere come Retrofit si occupa della costruzione della nostra interfaccia di servizio iniettando il codice necessario per effettuare la richiesta, in base alle nostre annotazioni precedenti.

Successivamente, otteniamo un oggetto Call che è quello utilizzato per eseguire la richiesta all'API GitHub. Il metodo più importante qui è execute , che viene utilizzato per eseguire una chiamata in modo sincrono e bloccherà il thread corrente durante il trasferimento dei dati.

Dopo che la chiamata è stata eseguita con successo, possiamo recuperare il corpo della risposta - già su un oggetto utente - grazie al nostro GsonConverterFactory .

Effettuare una chiamata sincrona è molto semplice, ma di solito utilizziamo una richiesta asincrona non bloccante:

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); } });

Ora invece del metodo execute, utilizziamo il metodo enqueue , che accetta un'interfaccia Callback come parametro per gestire il successo o il fallimento della richiesta. Nota che questo verrà eseguito in un thread separato.

Dopo che la chiamata è terminata correttamente, possiamo recuperare il corpo nello stesso modo in cui abbiamo fatto in precedenza.

5. Creazione di una classe ServiceGenerator riutilizzabile

Ora che abbiamo visto come costruire il nostro oggetto Retrofit e come utilizzare un'API, possiamo vedere che non vogliamo continuare a scrivere il builder più e più volte.

Quello che vogliamo è una classe riutilizzabile che ci consenta di creare questo oggetto una volta e di riutilizzarlo per tutta la durata della nostra applicazione:

public class GitHubServiceGenerator { private static final String BASE_URL = "//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); } }

All the logic of creating the Retrofit object is now moved to this GitHubServiceGenerator class, this makes it a sustainable client class which stops the code from repeating.

Here's a simple example of how to use it :

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

Now if we, for example, were to create a RepositoryService, we could reuse this class and simplify the creation.

In the next section, we're going to extend it and add authentication capabilities.

6. Authentication

Most APIs have some authentication to secure access to it.

Taking into account our previous generator class, we're going to add a create service method, that takes a JWT token with the Authorization header :

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); }

To add a header to our request, we need to use the interceptor capabilities of OkHttp; we do this by using our previously define builder and by reconstructing the Retrofit object.

Note that this a simple auth example, but with the use of interceptors we can use any authentication such as OAuth, user/password, etc.

7. Logging

In this section, we're going to further extend our GitHubServiceGenerator for logging capabilities, which are very important for debugging purposes in every project.

We're going to use our previous knowledge of interceptors, but we need an additional dependency, which is the HttpLoggingInterceptor from OkHttp, let us add it to our pom.xml:

 com.squareup.okhttp3 logging-interceptor 3.9.0 

Ora estendiamo la nostra classe GitHubServiceGenerator :

public class GitHubServiceGenerator { private static final String BASE_URL = "//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); } }

Questa è la forma finale della nostra classe, possiamo vedere come abbiamo aggiunto HttpLoggingInterceptor e lo abbiamo impostato per la registrazione di base, che registrerà il tempo impiegato per effettuare la richiesta, l'endpoint, lo stato di ogni richiesta, ecc.

È importante dare un'occhiata a come controlliamo se l'interceptor esiste, quindi non lo aggiungiamo accidentalmente due volte.

8. Conclusione

In questa guida completa, abbiamo esaminato l'eccellente libreria Retrofit concentrandoci sulla sua API Sync / Async, alcune best practice di modellazione, autenticazione e registrazione.

La libreria può essere utilizzata in modi molto complessi e utili; per un caso d'uso avanzato con RxJava, dai un'occhiata a questo tutorial.

E, come sempre, il codice sorgente può essere trovato su GitHub.