Eine Anleitung zu OkHttp

1. Einleitung

In diesem Artikel zeigen wir die Grundlagen der verschiedenen Arten von HTTP - Anfragen Senden, Empfangen und Interpretieren von HTTP - Antworten , und wie ein Client mit OkHttp konfigurieren.

Außerdem werden wir uns mit fortgeschritteneren Anwendungsfällen zum Konfigurieren eines Clients mit benutzerdefinierten Headern, Zeitüberschreitungen, Antwort-Caching usw. befassen.

2. OkHttp-Übersicht

OkHttp ist ein effizienter HTTP- und HTTP / 2-Client für Android- und Java-Anwendungen.

Es verfügt über erweiterte Funktionen wie Verbindungspooling (wenn HTTP / 2 nicht verfügbar ist), transparente GZIP-Komprimierung und Antwort-Caching, um das Netzwerk bei wiederholten Anforderungen vollständig zu vermeiden.

Es kann auch häufig auftretende Verbindungsprobleme beheben und bei einem Verbindungsfehler, wenn ein Dienst mehrere IP-Adressen hat, die Anforderung an alternative Adressen wiederholen.

Auf hoher Ebene ist der Client so konzipiert, dass er sowohl synchrone als auch nicht blockierende asynchrone Anrufe blockiert.

OkHttp unterstützt Android 2.3 und höher. Für Java beträgt die Mindestanforderung 1,7.

Nach dieser kurzen Übersicht sehen wir uns einige Anwendungsbeispiele an.

3. Maven-Abhängigkeit

Fügen wir zunächst die Bibliothek als Abhängigkeit in die Datei pom.xml ein :

 com.squareup.okhttp3 okhttp 3.4.2 

Informationen zur neuesten Abhängigkeit dieser Bibliothek finden Sie auf der Seite zu Maven Central.

4. Synchrones GET mit OkHttp

Um eine synchrone GET-Anfrage zu senden, müssen wir ein Request- Objekt basierend auf einer URL erstellen und einen Anruf tätigen . Nach seiner Ausführung erhalten wir eine Instanz von Response zurück :

@Test public void whenGetRequest_thenCorrect() throws IOException { Request request = new Request.Builder() .url(BASE_URL + "/date") .build(); Call call = client.newCall(request); Response response = call.execute(); assertThat(response.code(), equalTo(200)); }

5. Asynchrones GET mit OkHttp

Um ein asynchrones GET zu erstellen, müssen wir einen Anruf in die Warteschlange stellen . Ein Rückruf ermöglicht es uns, die Antwort zu lesen, wenn sie lesbar ist. Dies geschieht, nachdem die Antwortheader fertig sind.

Das Lesen des Antwortkörpers kann immer noch blockieren. OkHttp bietet derzeit keine asynchronen APIs an, um einen Antworttext in Teilen zu erhalten:

@Test public void whenAsynchronousGetRequest_thenCorrect() { Request request = new Request.Builder() .url(BASE_URL + "/date") .build(); Call call = client.newCall(request); call.enqueue(new Callback() { public void onResponse(Call call, Response response) throws IOException { // ... } public void onFailure(Call call, IOException e) { fail(); } }); }

6. GET With Query Parameters

Um unserer GET-Anfrage Abfrageparameter hinzuzufügen, können wir den HttpUrl.Builder nutzen .

Nachdem die URL erstellt wurde, können wir sie an unser Request- Objekt übergeben:

@Test public void whenGetRequestWithQueryParameter_thenCorrect() throws IOException { HttpUrl.Builder urlBuilder = HttpUrl.parse(BASE_URL + "/ex/bars").newBuilder(); urlBuilder.addQueryParameter("id", "1"); String url = urlBuilder.build().toString(); Request request = new Request.Builder() .url(url) .build(); Call call = client.newCall(request); Response response = call.execute(); assertThat(response.code(), equalTo(200)); }

7. POST-Anfrage

Schauen wir uns eine einfache POST-Anfrage an, bei der wir einen RequestBody erstellen , um die Parameter "Benutzername" und "Passwort" zu senden :

@Test public void whenSendPostRequest_thenCorrect() throws IOException { RequestBody formBody = new FormBody.Builder() .add("username", "test") .add("password", "test") .build(); Request request = new Request.Builder() .url(BASE_URL + "/users") .post(formBody) .build(); Call call = client.newCall(request); Response response = call.execute(); assertThat(response.code(), equalTo(200)); }

In unserem Artikel Eine Kurzanleitung zum Posten von Anfragen mit OkHttp finden Sie weitere Beispiele für POST-Anfragen mit OkHttp.

8. Datei hochladen

8.1. Eine Datei hochladen

In diesem Beispiel sehen wir, wie Sie eine Datei hochladen . Wir werden die Datei " test.ext" mit MultipartBody.Builder hochladen :

@Test public void whenUploadFile_thenCorrect() throws IOException { RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", "file.txt", RequestBody.create(MediaType.parse("application/octet-stream"), new File("src/test/resources/test.txt"))) .build(); Request request = new Request.Builder() .url(BASE_URL + "/users/upload") .post(requestBody) .build(); Call call = client.newCall(request); Response response = call.execute(); assertThat(response.code(), equalTo(200)); }

8.2. Fortschritt beim Hochladen von Dateien abrufen

Lassen Sie uns abschließend sehen, wie Sie den Fortschritt eines Datei- Uploads ermitteln. Wir werden RequestBody erweitern , um Einblick in den Upload-Prozess zu erhalten.

Hier ist zunächst die Upload-Methode:

@Test public void whenGetUploadFileProgress_thenCorrect() throws IOException { RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("file", "file.txt", RequestBody.create(MediaType.parse("application/octet-stream"), new File("src/test/resources/test.txt"))) .build(); ProgressRequestWrapper.ProgressListener listener = (bytesWritten, contentLength) -> { float percentage = 100f * bytesWritten / contentLength; assertFalse(Float.compare(percentage, 100) > 0); }; ProgressRequestWrapper countingBody = new ProgressRequestWrapper(requestBody, listener); Request request = new Request.Builder() .url(BASE_URL + "/users/upload") .post(countingBody) .build(); Call call = client.newCall(request); Response response = call.execute(); assertThat(response.code(), equalTo(200)); } 

Hier ist die Schnittstelle ProgressListener , mit der wir den Upload-Fortschritt beobachten können:

public interface ProgressListener { void onRequestProgress(long bytesWritten, long contentLength); }

Hier ist der ProgressRequestWrapper , die erweiterte Version von RequestBody :

public class ProgressRequestWrapper extends RequestBody { @Override public void writeTo(BufferedSink sink) throws IOException { BufferedSink bufferedSink; countingSink = new CountingSink(sink); bufferedSink = Okio.buffer(countingSink); delegate.writeTo(bufferedSink); bufferedSink.flush(); } }

Schließlich ist hier der CountingSink , die erweiterte Version von Forwarding Sink :

protected class CountingSink extends ForwardingSink { private long bytesWritten = 0; public CountingSink(Sink delegate) { super(delegate); } @Override public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); bytesWritten += byteCount; listener.onRequestProgress(bytesWritten, contentLength()); } }

Beachten Sie, dass:

  • Wenn Sie ForwardingSink auf "CountingSink" erweitern, überschreiben wir die write () -Methode, um die geschriebenen (übertragenen) Bytes zu zählen
  • Wenn Sie RequestBody auf " ProgressRequestWrapper " erweitern, überschreiben wir die writeTo () -Methode, um unseren "ForwardingSink" zu verwenden.

9. Festlegen eines benutzerdefinierten Headers

9.1. Setting a Header on a Request

To set any custom header on a Request we can use a simple addHeader call:

@Test public void whenSetHeader_thenCorrect() throws IOException { Request request = new Request.Builder() .url(SAMPLE_URL) .addHeader("Content-Type", "application/json") .build(); Call call = client.newCall(request); Response response = call.execute(); response.close(); }

9.2. Setting a Default Header

In this example, we will see how to configure a default header on the Client itself, instead of setting it on each and every request.

For example, if we want to set a content type “application/json” for every request we need to set an interceptor for our client. Here is the method:

@Test public void whenSetDefaultHeader_thenCorrect() throws IOException { OkHttpClient client = new OkHttpClient.Builder() .addInterceptor( new DefaultContentTypeInterceptor("application/json")) .build(); Request request = new Request.Builder() .url(SAMPLE_URL) .build(); Call call = client.newCall(request); Response response = call.execute(); response.close(); }

And here is the DefaultContentTypeInterceptor which is the extended version of Interceptor:

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

Note that the interceptor adds the header to the original request.

10. Do Not Follow Redirects

In this example, we'll see how to configure the OkHttpClient to stop following redirects.

By default, if a GET request is answered with an HTTP 301 Moved Permanently the redirect is automatically followed. In some use cases, that may be perfectly fine, but there are certainly use cases where that’s not desired.

To achieve this behavior, when we build our client, we need to set followRedirects to false.

Note that the response will return an HTTP 301 status code:

@Test public void whenSetFollowRedirects_thenNotRedirected() throws IOException { OkHttpClient client = new OkHttpClient().newBuilder() .followRedirects(false) .build(); Request request = new Request.Builder() .url("//t.co/I5YYd9tddw") .build(); Call call = client.newCall(request); Response response = call.execute(); assertThat(response.code(), equalTo(301)); } 

If we turn on the redirect with a true parameter (or remove it completely), the client will follow the redirection and the test will fail as the return code will be an HTTP 200.

11. Timeouts

Use timeouts to fail a call when its peer is unreachable. Network failures can be due to client connectivity problems, server availability problems, or anything between. OkHttp supports connect, read, and write timeouts.

In this example, we built our client with a readTimeout of 1 seconds, while the URL is served with 2 seconds of delay:

@Test public void whenSetRequestTimeout_thenFail() throws IOException { OkHttpClient client = new OkHttpClient.Builder() .readTimeout(1, TimeUnit.SECONDS) .build(); Request request = new Request.Builder() .url(BASE_URL + "/delay/2") .build(); Call call = client.newCall(request); Response response = call.execute(); assertThat(response.code(), equalTo(200)); }

Note as the test will fail as the client timeout is lower than the resource response time.

12. Canceling a Call

Use Call.cancel() to stop an ongoing call immediately. If a thread is currently writing a request or reading a response, an IOException will be thrown.

Use this to conserve the network when a call is no longer necessary; for example when your user navigates away from an application:

@Test(expected = IOException.class) public void whenCancelRequest_thenCorrect() throws IOException { ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); Request request = new Request.Builder() .url(BASE_URL + "/delay/2") .build(); int seconds = 1; long startNanos = System.nanoTime(); Call call = client.newCall(request); executor.schedule(() -> { logger.debug("Canceling call: " + (System.nanoTime() - startNanos) / 1e9f); call.cancel(); logger.debug("Canceled call: " + (System.nanoTime() - startNanos) / 1e9f); }, seconds, TimeUnit.SECONDS); logger.debug("Executing call: " + (System.nanoTime() - startNanos) / 1e9f); Response response = call.execute(); logger.debug(Call was expected to fail, but completed: " + (System.nanoTime() - startNanos) / 1e9f, response); }

13. Response Caching

To create a Cache, we'll need a cache directory that we can read and write to, and a limit on the cache's size.

The client will use it to cache the response:

@Test public void whenSetResponseCache_thenCorrect() throws IOException { int cacheSize = 10 * 1024 * 1024; File cacheDirectory = new File("src/test/resources/cache"); Cache cache = new Cache(cacheDirectory, cacheSize); OkHttpClient client = new OkHttpClient.Builder() .cache(cache) .build(); Request request = new Request.Builder() .url("//publicobject.com/helloworld.txt") .build(); Response response1 = client.newCall(request).execute(); logResponse(response1); Response response2 = client.newCall(request).execute(); logResponse(response2); }

After launching the test, the response from the first call will not have been cached. A call to the method cacheResponse will return null, while a call to the method networkResponse will return the response from the network.

Also, the cache folder will be filled with the cache files.

The second call execution will produce the opposite effect, as the response will have already been cached. This means that a call to networkResponse will return null while a call to cacheResponse will return the response from the cache.

To prevent a response from using the cache, use CacheControl.FORCE_NETWORK. To prevent it from using the network, use CacheControl.FORCE_CACHE.

Be warned: if you use FORCE_CACHE and the response requires the network, OkHttp will return a 504 Unsatisfiable Request response.

14. Conclusion

In this article, we have seen several examples of how to use OkHttp as an HTTP & HTTP/2 client.

Der Beispielcode befindet sich wie immer im GitHub-Projekt.