מדריך ל- OkHttp

1. הקדמה

במאמר זה נציג את היסודות של שליחת סוגים שונים של בקשות HTTP, קבלה ופרשנות של תגובות HTTP, וכיצד להגדיר לקוח עם OkHttp.

כמו כן, נעבור למקרי שימוש מתקדמים יותר של קביעת תצורה של לקוח עם כותרות מותאמות אישית, פסק זמן, אחסון במטמון וכו '.

2. OkHttp סקירה כללית

OkHttp הוא לקוח HTTP ו- HTTP / 2 יעיל ליישומי Android ו- Java.

הוא מגיע עם תכונות מתקדמות כגון איגום חיבורים (אם HTTP / 2 אינו זמין), דחיסת GZIP שקופה ושמירת מטמון כדי למנוע את הרשת לחלוטין לבקשות חוזרות.

הוא גם מסוגל להתאושש מבעיות חיבור נפוצות, ובשגיאת חיבור, אם לשירות יש מספר כתובות IP, הוא יכול לנסות שוב את הבקשה לכתובות חלופיות.

ברמה גבוהה, הלקוח מיועד לחסימת שיחות סינכרוניות סינכרוניות ולא חסימות.

OkHttp תומך ב- Android 2.3 ומעלה. עבור Java, הדרישה המינימלית היא 1.7.

לאחר סקירה קצרה זו, בואו נראה כמה דוגמאות לשימוש.

3. תלות של Maven

ראשית נוסיף את הספרייה כתלות ב- pom.xml:

 com.squareup.okhttp3 okhttp 3.4.2 

כדי לראות את התלות האחרונה בספרייה זו, עיין בעמוד במרכז Maven.

4. GET סינכרוני עם OkHttp

כדי לשלוח בקשת GET סינכרונית עלינו לבנות בַּקָשָׁה אובייקט המבוסס על א כתובת אתר ולעשות א שִׂיחָה. לאחר ביצועו אנו מקבלים חזרה מופע של תְגוּבָה:

@ מבחן ציבורי בטל כאשר GetRequest_thenCorrect () זורק IOException {בקשת בקשה = בקשה חדשה.בנאי (). אורל (BASE_URL + "/ תאריך"). בניין (); שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); assertThat (response.code (), equalTo (200)); }

5. אסינכרוני GET עם OkHttp

עכשיו, כדי ליצור GET אסינכרוני עלינו להשלים את a שִׂיחָה. א התקשר חזרה מאפשר לנו לקרוא את התגובה כאשר היא ניתנת לקריאה. זה קורה לאחר שכותרות התגובה מוכנות.

קריאת גוף התגובה עשויה עדיין לחסום. OkHttp אינו מציע כרגע ממשקי API אסינכרוניים לקבלת גוף תגובה בחלקים:

@ מבחן ציבורי בטל כאשר AsynchronousGetRequest_thenCorrect () {בקשת בקשה = בקשה חדשה.בנאי (). אורל (BASE_URL + "/ תאריך"). בניין (); שיחת שיחה = client.newCall (בקשה); call.enqueue (Callback חדש () {public void onResponse (שיחת שיחה, תגובה תגובה) זורק IOException {// ...} בטל ציבורי onFailure (שיחת שיחה, IOException e) {fail ();}}); }

6. קבל עם פרמטרים של שאילתה

לבסוף, כדי להוסיף פרמטרים של שאילתות לבקשת ה- GET שלנו אנו יכולים לנצל את ה- HttpUrl.Builder.

לאחר בניית כתובת ה- URL נוכל להעביר אותה אל שלנו בַּקָשָׁה לְהִתְנַגֵד:

@Test ציבורי בטל כאשר GetRequestWithQueryParameter_thenCorrect () זורק IOException {HttpUrl.Builder urlBuilder = HttpUrl.parse (BASE_URL + "/ ex / bars"). NewBuilder (); urlBuilder.addQueryParameter ("id", "1"); מחרוזת url = urlBuilder.build (). ToString (); בקשת בקשה = Request.Builder חדש () .url (url) .build (); שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); assertThat (response.code (), equalTo (200)); }

7. בקשת POST

בואו נסתכל על בקשת POST פשוטה בה אנו בונים RequestBody כדי לשלוח את הפרמטרים "שם משתמש" ו "סיסמה":

@ מבחן ציבורי בטל כאשר SendPostRequest_thenCorrect () זורק IOException {RequestBody formBody = FormBody.Builder חדש () .add ("שם משתמש", "מבחן"). להוסיף ("סיסמה", "מבחן"). בניין (); בקשת בקשה = Request.Builder חדש () .url (BASE_URL + "/ משתמשים") .post (formBody) .build (); שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); assertThat (response.code (), equalTo (200)); }

במאמר שלנו מדריך מהיר לפרסום בקשות עם OkHttp יש דוגמאות נוספות לבקשות POST עם OkHttp.

8. העלאת קבצים

8.1. לעלות קובץ

בדוגמה זו נראה כיצד להעלות a קוֹבֶץ. אנו מעלים את "test.ext ” קובץ באמצעות MultipartBody.Builder:

@Test ציבורי בטל כאשר UploadFile_thenCorrect () זורק IOException {RequestBody requestBody = חדש MultipartBody.Builder () .setType (MultipartBody.FORM) .addFormDataPart ("קובץ", "file.txt", RequestBody.create (MediaType.parse ("יישום /" יישום) octet-stream "), קובץ חדש (" src / test / resources / test.txt "))) .build (); בקשת בקשה = Request.Builder חדש () .url (BASE_URL + "/ משתמשים / העלאה") .post (requestBody) .build (); שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); assertThat (response.code (), equalTo (200)); }

8.2. קבל התקדמות העלאת קבצים

לבסוף, בואו נראה כיצד ניתן להתקדם של א קוֹבֶץ העלאה. נאריך RequestBody כדי להשיג נראות לתהליך ההעלאה.

ראשית, הנה שיטת ההעלאה:

@Test ציבורי בטל כאשר GetUploadFileProgress_thenCorrect () זורק IOException {RequestBody requestBody = חדש MultipartBody.Builder () .setType (MultipartBody.FORM) .addFormDataPart ("קובץ", "file.txt", RequestBody.create (MediaType.parse) octet-stream "), קובץ חדש (" src / test / resources / test.txt "))) .build (); ProgressRequestWrapper.ProgressListener מאזין = (bytesWritten, contentLength) -> {float אחוז = 100f * bytesWritten / contentLength; assertFalse (Float.compare (אחוז, 100)> 0); }; ProgressRequestWrapper countingBody = ProgressRequestWrapper חדש (requestBody, מאזין); בקשת בקשה = Request.Builder חדש () .url (BASE_URL + "/ משתמשים / העלאה") .post (countingBody) .build (); שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); assertThat (response.code (), equalTo (200)); } 

הנה הממשק ProgressListener המאפשר לנו לצפות בהתקדמות ההעלאה:

ממשק ציבורי ProgressListener {void onRequestProgress (בתים ארוכים כתוב, תוכן ארוך אורך); }

הנה ה ProgressRequestWrapper שהיא הגרסה המורחבת של RequestBody:

מחלקה ציבורית ProgressRequestWrapper מרחיב את RequestBody {@Override public void writeTo (BufferedSink sink) זורק IOException {BufferedSink bufferedSink; countingSink = חדש CountingSink (כיור); bufferedSink = Okio.buffer (countingSink); delegate.writeTo (bufferedSink); bufferedSink.flush (); }}

לבסוף, הנה CountingSink שהיא הגרסה המורחבת של העברהכִּיוֹר :

מחלקה מוגנת CountingSink מרחיבה ForwardingSink {בתים ארוכים פרטייםWritten = 0; CountingSink ציבורי (נציג כיור) {סופר (נציג); } @Override כתיבת חלל ציבורי (מקור חוצץ, byteCount ארוך) זורק IOException {super.write (מקור, byteCount); bytesWritten + = byteCount; listener.onRequestProgress (bytesWritten, contentLength ()); }}

ציין זאת:

  • בעת הארכה העברת כיור ל "CountingSink", אנו עוקפים את שיטת ה- write () כדי לספור את הבתים הכתובים (שהועברו)
  • בעת הארכה RequestBody ל "ProgressRequestWrapper ", אנו עוקפים את שיטת writeTo () לשימוש שלנו "ForwardingSink"

9. הגדרת כותרת מותאמת אישית

9.1. הגדרת כותרת על בקשה

להגדרת כותרת מותאמת אישית על בַּקָשָׁה אנחנו יכולים להשתמש בפשטות addHeader שִׂיחָה:

@Test ציבורי בטל כאשר SetHeader_thenCorrect () זורק IOException {בקשת בקשה = בקשה חדשה.בנאי () .url (SAMPLE_URL) .addHeader ("סוג תוכן", "יישום / json"). שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); תגובה.סגור (); }

9.2. הגדרת כותרת ברירת מחדל

בדוגמה זו נראה כיצד להגדיר כותרת ברירת מחדל בלקוח עצמו במקום להגדיר אותה בכל בקשה ובקשה.

לדוגמא, אם אנו רוצים להגדיר סוג תוכן "יישום / json" לכל בקשה עלינו לקבוע מיירט ללקוח שלנו. הנה השיטה:

@Test ציבורי בטל כאשר SetDefaultHeader_thenCorrect () זורק IOException {OkHttpClient לקוח = חדש OkHttpClient.Builder () .addInterceptor (DefaultContentTypeInterceptor חדש ("יישום / json")) .build (); בקשת בקשה = Request.Builder חדש () .url (SAMPLE_URL) .build (); שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); תגובה.סגור (); }

והנה ה DefaultContentTypeInterceptor שהיא הגרסה המורחבת של מיירט:

מחלקה ציבורית DefaultContentTypeInterceptor מיישם מיירט {יירוט תגובה ציבורית (שרשרת Interceptor.Chain) זורק IOException {בקש originalRequest = chain.request (); בקש requestWithUserAgent = originalRequest .newBuilder () .header ("Content-Type", contentType) .build (); return chain.proceed (requestWithUserAgent); }}

שים לב שהמיירט מוסיף את הכותרת לבקשה המקורית.

10. אל תעקוב אחר הפניות מחדש

בדוגמה זו נראה כיצד להגדיר את התצורה של OkHttpClient להפסיק לעקוב אחר הפניות מחדש.

כברירת מחדל, אם בקשת GET נענתה באמצעות HTTP 301 הועבר לצמיתות ההפניה מתבצעת באופן אוטומטי. במקרים מסוימים של שימוש, זה עשוי להיות בסדר גמור, אך בהחלט ישנם מקרי שימוש שבהם זה לא רצוי.

כדי להשיג התנהגות זו, כאשר אנו בונים את הלקוח שלנו, עלינו להגדיר followRedirects ל שֶׁקֶר.

שים לב שהתגובה תחזיר תשובה HTTP 301 קוד מצב:

@ מבחן ציבורי בטל כאשר SetFollowRedirects_thenNotRedirected () זורק IOException {לקוח OkHttpClient = OkHttpClient חדש (). NewBuilder () .followRedirects (שקר) .build (); בקשת בקשה = Request.Builder חדש () .url ("// t.co/I5YYd9tddw") .build (); שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); assertThat (response.code (), equalTo (301)); } 

אם נפעיל את ההפניה מחדש עם א נָכוֹן פרמטר (או להסיר אותו לחלוטין), הלקוח יבצע את ההפניה מחדש והבדיקה תיכשל מכיוון שקוד ההחזרה יהיה HTTP 200.

11. פסק זמן

השתמש בפסקי זמן כדי להיכשל בשיחה כאשר עמיתו אינו ניתן להשגה. כשלים ברשת יכולים לנבוע מבעיות קישוריות ללקוחות, בעיות בזמינות שרתים או כל דבר שביניהם. OkHttp תומך בהפסקת חיבור, קריאה וכתיבה.

בדוגמה זו בנינו את הלקוח שלנו עם הפסקת קריאה של שניות אחת, בעוד שכתובת האתר מוגשת עם 2 שניות עיכוב:

@Test ציבורי בטל כאשר SetRequestTimeout_thenFail () זורק IOException {OkHttpClient לקוח = OkHttpClient.Builder חדש () .readTimeout (1, TimeUnit.SECONDS) .build (); בקשת בקשה = Request.Builder חדש () .url (BASE_URL + "/ עיכוב / 2") .build (); שיחת שיחה = client.newCall (בקשה); תגובת תגובה = call.execute (); assertThat (response.code (), equalTo (200)); }

שים לב כי הבדיקה תיכשל מכיוון שפסק הזמן הקצוב ללקוח נמוך מזמן תגובת המשאבים.

12. ביטול שיחה

להשתמש Call.cancel () להפסיק מיד שיחה מתמשכת. אם שרשור כותב כרגע בקשה או קורא תגובה, IOException ייזרק.

השתמש בזה כדי לשמור על הרשת כששיחה כבר לא נחוצה; לדוגמא כאשר המשתמש שלך מנווט הרחק מיישום:

@Test (צפוי = IOException.class) בטל ציבורי כאשרCancelRequest_thenCorrect () זורק IOException {ScheduledExecutorService executor = Executors.newScheduledThreadPool (1); בקשת בקשה = Request.Builder חדש () .url (BASE_URL + "/ עיכוב / 2") .build (); שניות int = 1; startnanos ארוך = System.nanoTime (); שיחת שיחה = client.newCall (בקשה); executor.schedule (() -> {logger.debug ("ביטול שיחה:" + (System.nanoTime () - startNanos) / 1e9f); call.cancel (); logger.debug ("ביטול שיחה:" + (System .nanoTime () - startNanos) / 1e9f);}, שניות, TimeUnit.SECONDS); logger.debug ("ביצוע שיחה:" + (System.nanoTime () - startNanos) / 1e9f); תגובת תגובה = call.execute (); logger.debug (השיחה הייתה צפויה להיכשל, אך הושלמה: "+ (System.nanoTime () - startNanos) / 1e9f, תגובה);}

13. אחסון במטמון

ליצור מטמון, נצטרך ספריית מטמון שנוכל לקרוא ולכתוב אליה, והגבלה על גודל המטמון.

הלקוח ישתמש בזה במטמון התגובה:

@Test הציבור בטל כאשר SetResponseCache_thenCorrect () זורק IOException {int cacheSize = 10 * 1024 * 1024; קובץ cacheDirectory = קובץ חדש ("src / test / resources / cache"); מטמון מטמון = מטמון חדש (cacheDirectory, cacheSize); לקוח OkHttpClient = OkHttpClient.Builder חדש (). מטמון (מטמון) .build (); בקשת בקשה = Request.Builder חדש () .url ("// publicobject.com/helloworld.txt") .build (); תגובה תגובה 1 = client.newCall (בקשה). Execute (); logResponse (תגובה 1); תגובה response2 = client.newCall (בקשה). Execute (); logResponse (תגובה 2); }

לאחר השקת הבדיקה, התגובה מהשיחה הראשונה לא תושמר במטמון. קריאה לשיטה תגובה cache יחזור ריק, תוך קריאה לשיטה רשת תגובה יחזיר את התגובה מהרשת.

כמו כן, תיקיית המטמון תתמלא בקבצי המטמון.

ביצוע השיחה השני ייצור אפקט הפוך, מכיוון שהתגובה כבר נשמרה. פירוש הדבר שקריאה ל רשת תגובה יחזור ריק תוך כדי שיחה ל תגובה cache יחזיר את התגובה מהמטמון.

כדי למנוע מתגובה להשתמש במטמון, השתמש CacheControl.FORCE_NETWORK. כדי למנוע את השימוש ברשת, השתמש CacheControl.FORCE_CACHE.

הזהיר אותך: אם אתה משתמש FORCE_CACHE והתגובה דורשת את הרשת, OkHttp תחזיר תשובת בקשה 504 שאינה מסתפקת.

14. מסקנה

במאמר זה ראינו כמה דוגמאות כיצד להשתמש ב- OkHttp כלקוח HTTP & HTTP / 2.

כמו תמיד, ניתן למצוא את קוד הדוגמה בפרויקט GitHub.


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