1. Pengenalan
Dalam artikel ini, kami akan menunjukkan asas-asas untuk menghantar pelbagai jenis permintaan HTTP, menerima dan menafsirkan respons HTTP , dan cara mengkonfigurasi Pelanggan dengan OkHttp.
Juga, kita akan membahas kes penggunaan yang lebih maju untuk mengkonfigurasi klien dengan header tersuai, timeout, cache respons, dll.
2. Gambaran Keseluruhan OkHttp
OkHttp adalah klien HTTP & HTTP / 2 yang cekap untuk aplikasi Android dan Java.
Ia dilengkapi dengan fitur canggih seperti penyatuan sambungan (jika HTTP / 2 tidak tersedia), pemampatan GZIP telus, dan cache respons untuk mengelakkan rangkaian sepenuhnya untuk permintaan berulang.
Ia juga dapat pulih dari masalah sambungan biasa dan, jika kegagalan sambungan, jika perkhidmatan mempunyai banyak alamat IP, ia dapat mencuba semula permintaan ke alamat alternatif.
Pada tahap tinggi, pelanggan dirancang untuk menyekat panggilan segerak segerak dan tidak sekatan.
OkHttp menyokong Android 2.3 ke atas. Untuk Java, syarat minimum adalah 1.7.
Selepas tinjauan ringkas ini, mari kita lihat beberapa contoh penggunaan.
3. Ketergantungan Maven
Mari tambahkan perpustakaan sebagai pergantungan ke pom.xml terlebih dahulu :
com.squareup.okhttp3 okhttp 3.4.2
Untuk melihat kebergantungan terkini perpustakaan ini, lihat halaman di Maven Central.
4. DAPATKAN SEGERA Dengan OkHttp
Untuk menghantar permintaan GET segerak, kita perlu membina objek Request berdasarkan URL dan membuat Panggilan . Selepas pelaksanaannya, kami mendapat kembali contoh Respons :
@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. DAPATKAN ASGAR Dengan OkHttp
Sekarang, untuk membuat GET yang tidak segerak, kita perlu membuat Panggilan . A Callback membolehkan kita untuk membaca respons apabila ia dibaca. Ini berlaku setelah tajuk respons siap.
Membaca badan tindak balas mungkin masih menyekat. OkHttp pada masa ini tidak menawarkan API asinkron untuk menerima badan respons dalam beberapa bahagian:
@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 Pertanyaan
Akhirnya, untuk menambahkan parameter pertanyaan pada permintaan GET kami, kami dapat memanfaatkan HttpUrl.Builder .
Setelah URL dibina, kami dapat meneruskannya ke objek Permintaan kami :
@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 membina RequestBody untuk menghantar parameter "nama pengguna" dan "kata laluan" :
@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 Pantas untuk Mengirim Permintaan dengan OkHttp mempunyai lebih banyak contoh permintaan POST dengan OkHttp.
8. Memuat naik fail
8.1. Muat naik Fail
Dalam contoh ini, kita akan melihat cara memuat naik Fail . Kami akan memuat naik fail "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 Muat Naik Fail
Akhirnya, mari kita lihat bagaimana untuk mendapatkan kemajuan muat naik Fail . Kami akan memperluas RequestBody untuk mendapatkan keterlihatan dalam proses muat naik.
Pertama, inilah kaedah muat naik:
@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 antara muka ProgressListener yang membolehkan kita memerhatikan kemajuan muat naik:
public interface ProgressListener { void onRequestProgress(long bytesWritten, long contentLength); }
Berikut adalah ProgressRequestWrapper yang merupakan versi lanjutan 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(); } }
Akhirnya, inilah CountingSink yang merupakan versi lanjutan 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 bahawa:
- Semasa memperluas ForwardingSink ke "CountingSink", kami mengesampingkan kaedah write () untuk menghitung bait yang ditulis (dipindahkan)
- Semasa memperluas RequestBody ke " ProgressRequestWrapper ", kami mengganti kaedah writeTo () untuk menggunakan "ForwardingSink" kami
9. Menetapkan Header Tersuai
9.1. Menetapkan Header pada Permintaan
Untuk menetapkan header tersuai pada Permintaan, kita dapat menggunakan panggilan addHeader yang mudah :
@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. Menetapkan Header Lalai
Dalam contoh ini, kita akan melihat cara mengkonfigurasi header lalai pada Klien itu sendiri, dan bukannya menetapkannya pada setiap permintaan.
Sebagai contoh, jika kita ingin menetapkan jenis konten "application / json" untuk setiap permintaan kita harus menetapkan pencegat untuk klien kita. Inilah kaedahnya:
@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 bahawa pemintas menambah tajuk pada permintaan asal.
10. Jangan Ikut Pengalihan
Dalam contoh ini, kita akan melihat cara mengkonfigurasi OkHttpClient agar berhenti mengikuti pengalihan.
Secara lalai, jika permintaan GET dijawab dengan HTTP 301 Dipindahkan Secara Tetap pengalihan diikuti secara automatik. Dalam beberapa kes penggunaan, itu mungkin baik-baik saja, tetapi tentunya ada kes penggunaan yang tidak diingini.
Untuk mencapai tingkah laku ini, semasa kita membina klien kita, kita perlu menetapkan followRedirects ke false .
Perhatikan bahawa respons akan mengembalikan kod status HTTP 301 :
@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)); }
Sekiranya kita menghidupkan pengalihan dengan parameter benar (atau menghapusnya sepenuhnya), klien akan mengikuti pengalihan dan ujian akan gagal kerana kod pengembalian akan menjadi HTTP 200.
11. Waktu tamat
Gunakan waktu tunggu untuk gagal membuat panggilan apabila rakan sebaya tidak dapat dihubungi. Kegagalan rangkaian boleh disebabkan oleh masalah penyambungan pelanggan, masalah ketersediaan pelayan, atau apa-apa di antara mereka. OkHttp menyokong masa tamat sambung, baca, dan tulis.
Dalam contoh ini, kami membina pelanggan kami dengan readTimeout 1 saat, sementara URL diserahkan dengan kelewatan 2 saat:
@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)); }
Perhatikan kerana ujian akan gagal kerana waktu tunggu pelanggan lebih rendah daripada masa respons sumber.
12. Membatalkan Panggilan
Gunakan Call.cancel () untuk segera menghentikan panggilan yang sedang berlangsung. Sekiranya utas sedang menulis permintaan atau membaca respons, IOException akan dilemparkan.
Gunakan ini untuk memulihara rangkaian apabila panggilan tidak lagi diperlukan; contohnya semasa pengguna anda menjauh dari aplikasi:
@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. Caching Respons
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, kod contoh boleh didapati di projek GitHub.