En guide til OkHttp

1. Introduktion

I denne artikel viser vi det grundlæggende i afsendelse af forskellige typer HTTP-anmodninger, modtagelse og fortolkning af HTTP-svar, og hvordan man konfigurerer en klient med OkHttp.

Vi går også ind i mere avancerede brugssager til konfiguration af en klient med brugerdefinerede overskrifter, timeouts, caching af svar osv.

2. OkHttp-oversigt

OkHttp er en effektiv HTTP & HTTP / 2-klient til Android- og Java-applikationer.

Den leveres med avancerede funktioner såsom pooling af forbindelse (hvis HTTP / 2 ikke er tilgængelig), gennemsigtig GZIP-komprimering og caching af svar for at undgå netværket helt til gentagne anmodninger.

Det er også i stand til at komme sig efter almindelige forbindelsesproblemer, og hvis en tjeneste har flere IP-adresser, kan den prøve en anmodning om alternative adresser, hvis en tjeneste har flere IP-adresser.

På et højt niveau er klienten designet til både at blokere synkrone og ikke-blokerende asynkrone opkald.

OkHttp understøtter Android 2.3 og nyere. For Java er minimumskravet 1,7.

Lad os se nogle brugseksempler efter denne korte oversigt.

3. Maven-afhængighed

Lad os først tilføje biblioteket som en afhængighed af pom.xml:

 com.squareup.okhttp3 okhttp 3.4.2 

For at se den seneste afhængighed af dette bibliotek, se siden på Maven Central.

4. Synkron FÅ MED OkHttp

For at sende en synkron GET-anmodning skal vi oprette en Anmodning objekt baseret på en URL og lav en Opkald. Efter udførelsen får vi tilbage en forekomst af Respons:

@Test offentlig ugyldig nårGetRequest_thenCorrect () kaster IOException {Request request = new Request.Builder () .url (BASE_URL + "/ date") .build (); Ring til opkald = client.newCall (anmodning); Svarrespons = call.execute (); assertThat (respons.code (), equalTo (200)); }

5. Asynkron FÅ MED OkHttp

For at lave en asynkron GET er vi nødt til at anskaffe en Opkald. EN Ring tilbage giver os mulighed for at læse svaret, når det er læsbart. Dette sker, når svaroverskrifterne er klar.

Læsning af responsorganet kan stadig blokere. OkHttp tilbyder i øjeblikket ingen asynkrone API'er til at modtage en reaktionsdel i dele:

@Test offentlig ugyldig nårAsynchronousGetRequest_thenCorrect () {Request request = new Request.Builder () .url (BASE_URL + "/ date") .build (); Ring til opkald = client.newCall (anmodning); call.enqueue (new Callback () {public void onResponse (Call call, Response response) throw IOException {// ...} public void onFailure (Call call, IOException e) {fail ();}}); }

6. FÅ MED Forespørgselsparametre

Endelig, for at tilføje forespørgselsparametre til vores GET-anmodning kan vi drage fordel af HttpUrl.Builder.

Når URL'en er bygget, kan vi videregive den til vores Anmodning objekt:

@Test offentlig ugyldig nårGetRequestWithQueryParameter_thenCorrect () kaster IOException {HttpUrl.Builder urlBuilder = HttpUrl.parse (BASE_URL + "/ ex / barer"). NewBuilder (); urlBuilder.addQueryParameter ("id", "1"); String url = urlBuilder.build (). ToString (); Anmodningsanmodning = ny Request.Builder () .url (url) .build (); Ring til opkald = client.newCall (anmodning); Svarrespons = call.execute (); assertThat (respons.code (), equalTo (200)); }

7. POST-anmodning

Lad os se på en simpel POST-anmodning, hvor vi bygger en RequestBody for at sende parametrene “Brugernavn” og "adgangskode":

@Test offentlig ugyldig nårSendPostRequest_thenCorrect () kaster IOException {RequestBody formBody = ny FormBody.Builder () .add ("brugernavn", "test") .add ("password", "test") .build (); Anmodningsanmodning = ny Request.Builder () .url (BASE_URL + "/ brugere") .post (formBody) .build (); Ring til opkald = client.newCall (anmodning); Svarrespons = call.execute (); assertThat (respons.code (), equalTo (200)); }

Vores artikel En hurtig guide til indlægsanmodninger med OkHttp har flere eksempler på POST-anmodninger med OkHttp.

8. Filupload

8.1. Upload en fil

I dette eksempel ser vi, hvordan man uploader en Fil. Vi uploader "test.ext ” fil ved hjælp af MultipartBody.Builder:

@Test offentlig ugyldigt nårUploadFile_thenCorrect () kaster IOException {RequestBody requestBody = ny MultipartBody.Builder () .setType (MultipartBody.FORM) .addFormDataPart ("fil", "file.txt", RequestBody.create (MediaType.parse ("applikation /" applikation / "applikation /" octet-stream "), ny fil (" src / test / resources / test.txt "))) .build (); Anmodningsanmodning = ny Request.Builder () .url (BASE_URL + "/ brugere / upload") .post (requestBody) .build (); Ring til opkald = client.newCall (anmodning); Svarrespons = call.execute (); assertThat (respons.code (), equalTo (200)); }

8.2. Få status for filupload

Lad os endelig se, hvordan man får fremskridt i en Fil upload. Vi strækker os ud RequestBody for at få synlighed i uploadprocessen.

Først er her uploadmetoden:

@Test offentlig ugyldig nårGetUploadFileProgress_thenCorrect () kaster IOException {RequestBody requestBody = ny MultipartBody.Builder () .setType (MultipartBody.FORM) .addFormDataPart ("fil", "file.txt", RequestBody.create (MediaType / parse) octet-stream "), ny fil (" src / test / resources / test.txt "))) .build (); ProgressRequestWrapper.ProgressListener lytter = (bytesWritten, contentLength) -> {float-procent = 100f * bytesWritten / contentLength; assertFalse (Float.compare (procent, 100)> 0); }; ProgressRequestWrapper countingBody = ny ProgressRequestWrapper (requestBody, lytter); Anmodningsanmodning = ny Request.Builder () .url (BASE_URL + "/ brugere / upload") .post (countingBody) .build (); Ring til opkald = client.newCall (anmodning); Svarrespons = call.execute (); assertThat (respons.code (), equalTo (200)); } 

Her er grænsefladen ProgressListener der gør det muligt for os at observere uploadforløbet:

offentlig grænseflade ProgressListener {ugyldig påRequestProgress (lange byteSkrevet, langt indholdLængde); }

Her er ProgressRequestWrapper som er den udvidede version af RequestBody:

offentlig klasse ProgressRequestWrapper udvider RequestBody {@Override public void writeTo (BufferedSink sink) kaster IOException {BufferedSink bufferedSink; countingSink = ny CountingSink (vask); bufferedSink = Okio.buffer (countingSink); delegat.writeTo (bufferedSink); bufferedSink.flush (); }}

Endelig er her CountingSink som er den udvidede version af VideresendelseHåndvask :

beskyttet klasse CountingSink udvider ForwardingSink {private long bytesWritten = 0; public CountingSink (Sink delegate) {super (delegeret); } @ Override public void write (Buffer source, long byteCount) kaster IOException {super.write (source, byteCount); bytesWritten + = byteCount; listener.onRequestProgress (bytesWritten, contentLength ()); }}

Noter det:

  • Ved udvidelse VideresendelseSink til “CountingSink”, vi tilsidesætter skrivemetoden () for at tælle de skrevne (overførte) byte
  • Ved udvidelse RequestBody til "ProgressRequestWrapper “, Vi tilsidesætter writeTo () -metoden for at bruge vores “ForwardingSink”

9. Indstilling af en brugerdefineret header

9.1. Indstilling af en header på en anmodning

For at indstille enhver brugerdefineret header på en Anmodning vi kan bruge et simpelt addHeader opkald:

@Test offentlig ugyldig nårSetHeader_thenCorrect () kaster IOException {Request request = new Request.Builder () .url (SAMPLE_URL) .addHeader ("Content-Type", "application / json") .build (); Ring til opkald = client.newCall (anmodning); Svarrespons = call.execute (); respons.close (); }

9.2. Indstilling af en standard overskrift

I dette eksempel vil vi se, hvordan man konfigurerer en standardoverskrift på selve klienten, i stedet for at indstille den på hver enkelt anmodning.

For eksempel, hvis vi vil indstille en indholdstype “Ansøgning / json” for hver anmodning er vi nødt til at indstille en interceptor til vores klient. Her er metoden:

@Test offentlig ugyldig når SetDefaultHeader_thenCorrect () kaster IOException {OkHttpClient-klient = ny OkHttpClient.Builder () .addInterceptor (ny DefaultContentTypeInterceptor ("applikation / json")) .build (); Anmodningsanmodning = ny Request.Builder () .url (SAMPLE_URL) .build (); Ring til opkald = client.newCall (anmodning); Svarrespons = call.execute (); respons.close (); }

Og her er DefaultContentTypeInterceptor som er den udvidede version af Interceptor:

offentlig klasse DefaultContentTypeInterceptor implementerer Interceptor {public Response intercept (Interceptor.Chain chain) kaster IOException {Request originalRequest = chain.request (); Request requestWithUserAgent = originalRequest .newBuilder () .header ("Content-Type", contentType) .build (); return chain.proceed (requestWithUserAgent); }}

Bemærk, at interceptor føjer overskriften til den oprindelige anmodning.

10. Følg ikke omdirigeringer

I dette eksempel ser vi, hvordan du konfigurerer OkHttpClient for at stoppe med at følge omdirigeringer.

Som standard, hvis en GET-anmodning besvares med en HTTP 301 Flyttet permanent omdirigering følges automatisk. I nogle brugssager kan det være helt fint, men der er bestemt brugstilfælde, hvor det ikke ønskes.

For at opnå denne adfærd, når vi bygger vores klient, er vi nødt til at indstille followRedirects til falsk.

Bemærk, at svaret returnerer et HTTP 301 statuskode:

@Test offentlig ugyldig nårSetFollowRedirects_thenNotRedirected () kaster IOException {OkHttpClient-klient = ny OkHttpClient (). NewBuilder () .followRedirects (falsk) .build (); Anmodningsanmodning = ny Request.Builder () .url ("// t.co/I5YYd9tddw") .build (); Ring til opkald = client.newCall (anmodning); Svarrespons = call.execute (); assertThat (respons.code (), equalTo (301)); } 

Hvis vi tænder omdirigering med en rigtigt parameter (eller fjerne den fuldstændigt), vil klienten følge omdirigering, og testen mislykkes, da returkoden vil være en HTTP 200.

11. Timeouts

Brug timeouts til at mislykkes med et opkald, når dets peer ikke kan nås. Netværksfejl kan skyldes klientforbindelsesproblemer, servertilgængelighedsproblemer eller noget derimellem. OkHttp understøtter tilslutning, læsning og skrivning af timeouts.

I dette eksempel byggede vi vores klient med en readTimeout på 1 sekund, mens URL-adressen serveres med 2 sekunders forsinkelse:

@Test offentlig ugyldig nårSetRequestTimeout_thenFail () kaster IOException {OkHttpClient-klient = ny OkHttpClient.Builder () .readTimeout (1, TimeUnit.SECONDS) .build (); Anmodningsanmodning = ny Request.Builder () .url (BASE_URL + "/ forsinkelse / 2") .build (); Call call = client.newCall (anmodning); Svarrespons = call.execute (); assertThat (respons.code (), equalTo (200)); }

Bemærk, da testen mislykkes, da klientens timeout er lavere end ressourcens responstid.

12. Annullering af et opkald

Brug Call.cancel () for at stoppe et igangværende opkald med det samme. Hvis en tråd i øjeblikket skriver en anmodning eller læser et svar, en IOUndtagelse vil blive kastet.

Brug dette til at bevare netværket, når et opkald ikke længere er nødvendigt; for eksempel når din bruger navigerer væk fra en applikation:

@Test (forventet = IOException.class) offentlig ugyldig nårCancelRequest_thenCorrect () kaster IOException {ScheduledExecutorService eksekutor = Executors.newScheduledThreadPool (1); Anmodningsanmodning = ny Request.Builder () .url (BASE_URL + "/ forsinkelse / 2") .build (); int sekunder = 1; lang startNanos = System.nanoTime (); Call call = client.newCall (anmodning); executor.schedule (() -> {logger.debug ("Annullerer opkald:" + (System.nanoTime () - startNanos) / 1e9f); call.cancel (); logger.debug ("Annulleret opkald:" + (System .nanoTime () - startNanos) / 1e9f);}, sekunder, TimeUnit.SECONDS); logger.debug ("Udfører opkald:" + (System.nanoTime () - startNanos) / 1e9f); Svarrespons = call.execute (); logger.debug (Call forventes at mislykkes, men afsluttet: "+ (System.nanoTime () - startNanos) / 1e9f, svar);}

13. Svar caching

At oprette en Cache, vi har brug for et cache-bibliotek, som vi kan læse og skrive til, og en grænse for cache-størrelsen.

Klienten bruger den til at cache svaret:

@Test offentligt ugyldigt nårSetResponseCache_thenCorrect () kaster IOException {int cacheSize = 10 * 1024 * 1024; Fil cacheDirectory = ny fil ("src / test / ressourcer / cache"); Cache cache = ny cache (cacheDirectory, cacheSize); OkHttpClient-klient = ny OkHttpClient.Builder () .cache (cache) .build (); Anmodningsanmodning = ny Request.Builder () .url ("// publicobject.com/helloworld.txt") .build (); Svar respons1 = client.newCall (anmodning) .execute (); logResponse (respons1); Svar respons2 = client.newCall (anmodning) .execute (); logResponse (respons2); }

Efter start af testen er svaret fra det første opkald ikke cachelagret. Et kald til metoden cacheResponse kommer tilbage nul, mens et opkald til metoden netværkssvar vil returnere svaret fra netværket.

Cache-mappen udfyldes også med cache-filerne.

Den anden opkaldsudførelse vil give den modsatte effekt, da svaret allerede er cachelagret. Dette betyder, at et opkald til netværkssvar kommer tilbage nul mens et opkald til cacheResponse returnerer svaret fra cachen.

Brug for at forhindre et svar i at bruge cachen CacheControl.FORCE_NETWORK. Brug for at forhindre det i at bruge netværket CacheControl.FORCE_CACHE.

Vær advaret: hvis du bruger FORCE_CACHE og svaret kræver netværket, OkHttp vil returnere et 504 utilfredsstillende anmodning svar.

14. Konklusion

I denne artikel har vi set flere eksempler på, hvordan man bruger OkHttp som en HTTP & HTTP / 2-klient.

Som altid kan eksempelkoden findes i GitHub-projektet.


$config[zx-auto] not found$config[zx-overlay] not found