Every network request is a request. Request is the encapsulation of URL, method, header and body. It is also the encapsulation of request line, request header and entity content in Http protocol
public final class Request { private final HttpUrl url; private final String method; private final Headers headers; private final RequestBody body; private final Object tag; private volatile CacheControl cacheControl; // Lazily initialized.
1.HttpUrl
HttpUrl is mainly used to regulate common url connection and parse the components of url
Let's use the following example to illustrate the use of httpUrl
https://www.google.com/search?q=maplejaw
① parse url string with parse:
1. HttpUrl url = HttpUrl.parse("https://www.google.com/search?q=maplejaw");
(2) common through the constructor mode:
1. HttpUrl url = new HttpUrl.Builder() 2. .scheme("https") 3. .host("www.google.com") 4. .addPathSegment("search") 5. .addQueryParameter("q", "maplejaw") 6. .build();
2.Headers
Headers are used to configure request headers. You must be familiar with the configuration of request headers, such as' content type ',' user agent 'and' cache control '. ` ` ` `
`There are also two ways to create Headers. As follows:
(1)of() creation: the array passed in must be an even pair, otherwise an exception will be thrown. `
1. Headers.of("name1","value1","name2","value2",.....);
You can also use its overloaded method of (map map map) to create
(2) builder mode creation:``
1. Headers mHeaders=new Headers.Builder() 2. .set("name1","value1")//set means name1 is unique, which will overwrite the existing 3. .add("name2","value2")//add does not overwrite the existing header. There can be multiple headers 4. .build();
Let's take a look at the internal part of the header. It's very simple not to paste the source code. The internal part of the Headers is to save the header private final String[] namesAndValues through an array. You may have such a question, why not use the array instead of the Map? Because the Map Key is unique, and the header requirements are not unique
In addition, is the array easy to access data? It's convenient. Let's look at three ways
Finally, the toString method is transformed into String to facilitate writing the request header,
1. @Override 2. public String toString() { 3. StringBuilder result = new StringBuilder(); 4. , size = size(); i < size; i++) { 5. result.append(name(i)).append(": ").append(value(i)).append("\n"); 6. } 7. return result.toString(); 8. } 10. /** Returns the field at {@code position} or null if that is out of range. */ 11. public String name(int index) { 12. ; 13. || nameIndex >= namesAndValues.length) { 14. return null; 15. } 16. return namesAndValues[nameIndex]; 17. } 19. /** Returns the value at {@code index} or null if that is out of range. */ 20. public String value(int index) { 21. + ; 22. || valueIndex >= namesAndValues.length) { 23. return null; 24. } 25. return namesAndValues[valueIndex]; 26. }
3.RequestBody
Requestbody is the content of the request entity. Let's see how to build a requestbody
(1) create the request. Create() method
1. public static final MediaType TEXT = MediaType.parse("text/plain; charset=utf-8"); 2. public static final MediaType STREAM = MediaType.parse("application/octet-stream"); 3. public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); 5. //Build string request body 6. RequestBody body1 = RequestBody.create(TEXT, string); 7. //Build byte request body 8. RequestBody body2 = RequestBody.create(STREAM, byte); 9. //Build file request body 10. RequestBody body3 = RequestBody.create(STREAM, file); 11. //post upload json 12. RequestBody body4 = RequestBody.create(JSON, json);//json is of type String 14. //Set the request body to the request method 15. Request request = new Request.Builder() 16. .url(url) 17. .post(xx)// xx represents one of body1, body2, body3, body4 18. .build();
(2) build the form request body and submit the key value pair (OkHttp3 does not have the FormEncodingBuilder class, instead of the more powerful FormBody:)
1. //Build form RequestBody 2. RequestBody formBody=new FormBody.Builder() 3. .add("name","maplejaw") 4. .add(") 5. ... 6. .build();
(3) build request body of partitioned form: (OkHttp3 cancels MultipartBuilder and replaces MultipartBody.Builder()
You can add forms, binary data such as files, etc.)
1. public static final MediaType STREAM = MediaType.parse("application/octet-stream"); 2. //Build form RequestBody 3. RequestBody multipartBody=new MultipartBody.Builder() 4. .setType(MultipartBody.FORM)//Indicates multipart / form data type 5. .addFormDataPart(") //Add form data 6. .addFormDataPart("avatar","111.jpg",RequestBody.create(STREAM,file)) //Add a file, where avatar is the form name and 111.jpg is the file name. 7. .addPart(..)//This method is used to add RequestBody,Headers and custom parts. Generally speaking, the above is enough 8. .build();
Knowing the creation of RequestBody, let's take a look at the source code
RequestBody is to request entity content. If there is no entity content in a Get request, post submission is available. In addition, only post submission is available when the form is uploaded when the browser communicates with the server. Therefore, RequestBody encapsulates the entity content corresponding to the browser form when it is uploaded. It is unclear what the entity content is Communication details of Http protocol of Android
There are three ways to create RequestBody in OkHttp3
① mode 1:
1. public static RequestBody create(MediaType contentType, String content) { 2. Charset charset = Util.UTF_8; 3. if (contentType != null) { 4. charset = contentType.charset();//Creation method of MediaType for ContentType in request header: public static final MediaType TEXT= 5. //MediaType.parse("text/plain; charset=utf-8") 6. if (charset == null) { 7. charset = Util.UTF_8;<span style="font-family:Microsoft YaHei;">//If charset is not specified in contentType, UTF-8 is used by default</span> 8. contentType = MediaType.parse(contentType + "; charset=utf-8"); 9. } 10. } 11. byte[] bytes = content.getBytes(charset); 12. return create(contentType, bytes); 13. }
② method 2: FormBody form creation, let's take a look
FormBody is used for common post form upload key value pairs. Let's first look at the creation method, and then look at the source code
1. RequestBody formBody=new FormBody.Builder() 2. .add("name","maplejaw") 3. .add(") 4. ... 5. .build();
FormBody source code
1. public final class FormBody extends RequestBody { 2. private static final MediaType CONTENT_TYPE = 3. MediaType.parse("application/x-www-form-urlencoded"); 5. private final List<String> encodedNames; 6. private final List<String> encodedValues; 8. private FormBody(List<String> encodedNames, List<String> encodedValues) { 9. this.encodedNames = Util.immutableList(encodedNames); 10. this.encodedValues = Util.immutableList(encodedValues); 11. } 13. /** The number of key-value pairs in this form-encoded body. */ 14. public int size() { 15. return encodedNames.size(); 16. } 18. public String encodedName(int index) { 19. return encodedNames.get(index); 20. } 22. public String name(int index) { 23. return percentDecode(encodedName(index), true); 24. } 26. public String encodedValue(int index) { 27. return encodedValues.get(index); 28. } 30. public String value(int index) { 31. return percentDecode(encodedValue(index), true); 32. } 34. @Override public MediaType contentType() { 35. return CONTENT_TYPE; 36. } 38. @Override public long contentLength() { 39. return writeOrCountBytes(null, true); 40. } 42. @Override public void writeTo(BufferedSink sink) throws IOException { 43. writeOrCountBytes(sink, false); 44. } 46. /** 47. * Either writes this request to {@code sink} or measures its content length. We have one method 48. * do double-duty to make sure the counting and content are consistent, particularly when it comes 49. * to awkward operations like measuring the encoded length of header strings, or the 50. * length-in-digits of an encoded integer. 51. */ 52. private long writeOrCountBytes(BufferedSink sink, boolean countBytes) { 53. long byteCount = 0L; 55. Buffer buffer; 56. if (countBytes) { 57. buffer = new Buffer(); 58. } else { 59. buffer = sink.buffer(); 60. } 62. , size = encodedNames.size(); i < size; i++) { 63. ) buffer.writeByte('&'); 64. buffer.writeUtf8(encodedNames.get(i)); 65. buffer.writeByte('='); 66. buffer.writeUtf8(encodedValues.get(i)); 67. } 69. if (countBytes) { 70. byteCount = buffer.size(); 71. buffer.clear(); 72. } 74. return byteCount; 75. } 77. public static final class Builder { 78. private final List<String> names = new ArrayList<>(); 79. private final List<String> values = new ArrayList<>(); 81. public Builder add(String name, String value) { 82. names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, false, false, true, true)); 83. values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, false, false, true, true)); 84. return this; 85. } 87. public Builder addEncoded(String name, String value) { 88. names.add(HttpUrl.canonicalize(name, FORM_ENCODE_SET, true, false, true, true)); 89. values.add(HttpUrl.canonicalize(value, FORM_ENCODE_SET, true, false, true, true)); 90. return this; 91. } 93. public FormBody build() { 94. return new FormBody(names, values); 95. } 96. } 97. }
Let's look at the method 'writeOrCountBytes'. Use writeOrCountBytes to calculate the request body size and write the request body to BufferedSink.
As for the BufferSink and buffer classes, these two classes are the ones in Okio. Buffer is equivalent to a cache area and BufferedSink is equivalent to OutputStream. It extends
The function of OutputStream and the complete source code of Okio will be blogged later
③ mode 3: MultipartBody block form creation
`MultipartBody can add forms, files and other binary data. Let's look at several important methods`
1. public static Part createFormData(String name, String filename, RequestBody body) { 2. if (name == null) { 3. throw new NullPointerException("name == null"); 4. } 5. StringBuilder disposition = new StringBuilder("form-data; name="); 6. appendQuotedString(disposition, name); 8. if (filename != null) { 9. disposition.append("; filename="); 10. appendQuotedString(disposition, filename); 11. } 13. return create(Headers.of("Content-Disposition", disposition.toString()), body); 14. }
Let's take a look at this method. Whether we are addPart or addFormDataPart finally comes to this method, which is encapsulated into a Part object, that is, the entity content
The value of the content disposition of and the binary stream or key value pair of the file
MultipartBody and FormBody are basically the same. The main difference lies in the 'writeOrCountBytes method. The block form is mainly to sum up the size of each block to find the size of the request body. If one of the blocks has no specified size, it will return - 1. So if there are files in the block form, the size cannot be calculated by default, unless you specify contentLength for the RequestBody of the file. `
1. private long writeOrCountBytes(BufferedSink sink, boolean countBytes) throws IOException { 2. long byteCount = 0L; 4. Buffer byteCountBuffer = null; 5. if (countBytes) { 6. //If it's a calculation of size, it's new 7. sink = byteCountBuffer = new Buffer(); 8. } 9. //Loop block 10. , partCount = parts.size(); p < partCount; p++) { 11. Part part = parts.get(p); 12. //Get the header of each block 13. Headers headers = part.headers; 14. //Get the request body of each block 15. RequestBody body = part.body; 17. //Write -- xxxxxxxxxx boundary 18. sink.write(DASHDASH); 19. sink.write(boundary); 20. sink.write(CRLF); 22. //Block header 23. if (headers != null) { 24. , headerCount = headers.size(); h < headerCount; h++) { 25. sink.writeUtf8(headers.name(h)) 26. .write(COLONSPACE) 27. .writeUtf8(headers.value(h)) 28. .write(CRLF); 29. } 30. } 32. //Content type of write block 33. MediaType contentType = body.contentType(); 34. if (contentType != null) { 35. sink.writeUtf8("Content-Type: ") 36. .writeUtf8(contentType.toString()) 37. .write(CRLF); 38. } 40. //Write block size 41. long contentLength = body.contentLength(); 42. ) { 43. sink.writeUtf8("Content-Length: ") 44. .writeDecimalLong(contentLength) 45. .write(CRLF); 46. } else if (countBytes) { 47. // We can't measure the body's size without the sizes of its components. 48. //If a block does not have this size, return - 1 49. byteCountBuffer.clear(); 50. return -1L; 51. } 53. sink.write(CRLF); 55. //If it is the calculated size, it will be accumulated, otherwise, it will be written to BufferedSink 56. if (countBytes) { 57. byteCount += contentLength; 58. } else { 59. body.writeTo(sink); 60. } 62. sink.write(CRLF); 63. } 65. //Write -- XXXXXXXX -- end boundary 66. sink.write(DASHDASH); 67. sink.write(boundary); 68. sink.write(DASHDASH); 69. sink.write(CRLF); 71. if (countBytes) { 72. byteCount += byteCountBuffer.size(); 73. byteCountBuffer.clear(); 74. } 76. return byteCount; 77. }
4.CacheControl
( 1) Cache-Control:
Cache control specifies the caching mechanism that requests and responses follow. Setting cache control in a request or response message does not modify the cache processing in another message processing. There are several cache instructions at request:
- Public: all content will be cached (both client and proxy can be cached).
- Private: content is only cached in private cache (only client can cache, proxy server can't cache)
- No cache: request or response messages cannot be cached
- No store: do not use or store cache
- Max age: the cached content will be invalid after the specified time (seconds). This option is only available in HTTP 1.1. If it is used with last modified, the priority is higher
- After xxx seconds, the browser resends the request to the server. Within the specified time (seconds), the client will directly return to the cache without initiating a network request. If it expires, it will automatically initiate a network request
- Min fresh: indicates that the client can receive a response whose response time is less than the current time plus the specified time.
- Max stale: indicates that the client can receive response messages beyond the timeout period. If you specify a value for the max stale message, the client can receive a response message that exceeds the value specified for the timeout period.
(2) CacheControl class
① common functions
1. final CacheControl.Builder builder = new CacheControl.Builder(); 2. builder.noCache();//Do not use cache, all go online 3. builder.noStore();//Do not use or store cache 4. builder.onlyIfCached();//Cache only 5. builder.noTransform();//No transcoding 6. builder.maxAge(, TimeUnit.MILLISECONDS);//Indicates that the client can receive responses with a lifetime of no more than the specified time. 7. builder.maxStale(, TimeUnit.SECONDS);//Indicates that the client can receive response messages that exceed the timeout period 8. builder.minFresh(, TimeUnit.SECONDS);//Indicates that the client can receive a response with a response time less than the current time plus the specified time. 9. CacheControl cache = builder.build();//cacheControl
② two constants of CacheControl:
1. public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();//Do not use cache 2. public static final CacheControl FORCE_CACHE = new Builder() 3. .onlyIfCached() 4. .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS) 5. .build();//Cache only
③ how to use when requesting:
1. final CacheControl.Builder builder = new CacheControl.Builder(); 2. builder.maxAge(, TimeUnit.MILLISECONDS); 3. CacheControl cache = builder.build(); 4. final Request request = new Request.Builder().cacheControl(cache).url(requestUrl).build(); 5. final Call call = mOkHttpClient.newCall(request);// 6. call.enqueue(new Callback() { 7. @Override 8. public void onFailure(Call call, IOException e) { 9. failedCallBack("Access failed", callBack); 10. Log.e(TAG, e.toString()); 11. } 13. @Override 14. public void onResponse(Call call, Response response) throws IOException { 15. if (response.isSuccessful()) { 16. String string = response.body().string(); 17. Log.e(TAG, "response ----->" + string); 18. successCallBack((T) string, callBack); 19. } else { 20. failedCallBack("Server error", callBack); 21. } 22. } 23. }); 24. return call; 25. } catch (Exception e) { 26. Log.e(TAG, e.toString()); 27. }
If the cache does not expire, it will directly return to the cache instead of initiating a network request. If the cache expires, it will automatically initiate a network request. Note: if you use FORCE_CACHE and the network's response requirements, OkHttp will return a 504 prompt, which tells you that the request response cannot be met. Therefore, we add a judgment to use it without a network
1. //Determine whether the network is connected 2. boolean connected = NetworkUtil.isConnected(context); 3. if (!connected) { 4. request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build(); 5. }
Original link https://www.bbsmax.com/A/MAzArw9OJ9/