Panduan untuk OkHttp

1. Perkenalan

Dalam artikel ini, kami akan menunjukkan dasar-dasar pengiriman berbagai jenis permintaan HTTP, menerima dan menafsirkan tanggapan HTTP , dan cara mengkonfigurasi Klien dengan OkHttp.

Selain itu, kita akan membahas kasus penggunaan lanjutan dalam mengonfigurasi klien dengan header khusus, batas waktu, cache respons, dll.

2. Ikhtisar OkHttp

OkHttp adalah klien HTTP & HTTP / 2 yang efisien untuk aplikasi Android dan Java.

Muncul dengan fitur-fitur canggih seperti penggabungan koneksi (jika HTTP / 2 tidak tersedia), kompresi GZIP transparan, dan cache respons untuk menghindari jaringan sepenuhnya untuk permintaan berulang.

Itu juga dapat memulihkan dari masalah koneksi umum dan, pada kegagalan koneksi, jika layanan memiliki beberapa alamat IP, itu dapat mencoba kembali permintaan ke alamat alternatif.

Pada tingkat tinggi, klien dirancang untuk memblokir panggilan sinkron dan non-pemblokiran panggilan asinkron.

OkHttp mendukung Android 2.3 dan di atasnya. Untuk Java, persyaratan minimumnya adalah 1.7.

Setelah gambaran singkat ini, mari kita lihat beberapa contoh penggunaan.

3. Ketergantungan Maven

Mari pertama-tama tambahkan pustaka sebagai dependensi ke pom.xml :

 com.squareup.okhttp3 okhttp 3.4.2 

Untuk melihat dependensi terbaru dari library ini, lihat halaman di Maven Central.

4. GET Sinkron Dengan OkHttp

Untuk mengirim permintaan GET sinkron, kita perlu membuat objek Permintaan berdasarkan URL dan melakukan Panggilan . Setelah dieksekusi, kita mendapatkan kembali sebuah contoh dari Respon :

@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. GET Asynchronous Dengan OkHttp

Sekarang, untuk membuat GET asinkron kita perlu mengantrekan Panggilan . Sebuah Callback memungkinkan kita untuk membaca respon ketika dibaca. Ini terjadi setelah header respons siap.

Membaca isi respons mungkin masih menghalangi. OkHttp saat ini tidak menawarkan API asinkron apa pun untuk menerima isi respons di beberapa bagian:

@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. DAPATKAN Dengan Parameter Query

Terakhir, untuk menambahkan parameter kueri ke permintaan GET kita, kita dapat memanfaatkan HttpUrl.Builder .

Setelah URL dibuat, kita dapat meneruskannya ke objek Request kita :

@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. Permintaan POST

Mari kita lihat permintaan POST sederhana di mana kita membuat RequestBody untuk mengirim parameter "username" dan "password" :

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

Artikel kami Panduan Cepat untuk Mengirim Permintaan dengan OkHttp memiliki lebih banyak contoh permintaan POST dengan OkHttp.

8. Mengupload File

8.1. Unggah File

Dalam contoh ini, kita akan melihat cara mengupload File . Kami akan mengupload file " test.ext" menggunakan MultipartBody.Builder :

@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. Dapatkan Kemajuan Unggahan File

Terakhir, mari kita lihat bagaimana cara mendapatkan kemajuan unggahan File . Kami akan memperluas RequestBody untuk mendapatkan visibilitas ke dalam proses pengunggahan.

Pertama, inilah metode unggah:

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

Berikut adalah antarmuka ProgressListener yang memungkinkan kita mengamati kemajuan unggahan:

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

Berikut adalah ProgressRequestWrapper yang merupakan versi lanjutan dari 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(); } }

Terakhir, inilah CountingSink yang merupakan versi lanjutan dari 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()); } }

Perhatikan bahwa:

  • Saat memperluas ForwardingSink ke "CountingSink", kami mengganti metode write () untuk menghitung byte tertulis (ditransfer)
  • Saat memperluas RequestBody ke " ProgressRequestWrapper ", kami mengganti metode writeTo () untuk menggunakan "ForwardingSink" kami

9. Mengatur Header Kustom

9.1. Mengatur Header pada Permintaan

Untuk mengatur tajuk khusus apa pun pada Permintaan kita dapat menggunakan panggilan addHeader sederhana :

@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. Menyetel Header Default

Dalam contoh ini, kita akan melihat cara mengkonfigurasi header default pada Klien itu sendiri, alih-alih mengaturnya pada setiap permintaan.

Misalnya, jika kita ingin menyetel tipe konten "application / json" untuk setiap permintaan, kita perlu menyetel pencegat untuk klien kita. Berikut caranya:

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

Dan inilah DefaultContentTypeInterceptor yang merupakan versi lanjutan dari 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); } }

Perhatikan bahwa interseptor menambahkan header ke permintaan asli.

10. Jangan Ikuti Pengalihan

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.

Seperti biasa, kode contoh dapat ditemukan di proyek GitHub.