<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      安卓筆記俠

      專注安卓開發(fā)

      導(dǎo)航

      OkHttp3源碼詳解(一) Request類

       

      每一次網(wǎng)絡(luò)請(qǐng)求都是一個(gè)Request,Request是對(duì)url,method,header,body的封裝,也是對(duì)Http協(xié)議中請(qǐng)求行,請(qǐng)求頭,實(shí)體內(nèi)容的封裝

      1 public final class Request {
      2   private final HttpUrl url;
      3   private final String method;
      4   private final Headers headers;
      5   private final RequestBody body;
      6   private final Object tag;
      7 
      8   private volatile CacheControl cacheControl; // Lazily initialized.

      1.HttpUrl

      HttpUrl主要用來(lái)規(guī)范普通的url連接,并且解析url的組成部分

      現(xiàn)通過下面的例子來(lái)示例httpUrl的使用
      https://www.google.com/search?q=maplejaw
      ①使用parse解析url字符串:

      HttpUrl url = HttpUrl.parse("https://www.google.com/search?q=maplejaw");

      ②通過構(gòu)造者模式來(lái)常見:

      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用于配置請(qǐng)求頭,對(duì)于請(qǐng)求頭配置大家一定不陌生吧,比如Content-Type,User-Agent和Cache-Control等等。

      創(chuàng)建Headers也有兩種方式。如下:
      (1)of()創(chuàng)建:傳入的數(shù)組必須是偶數(shù)對(duì),否則會(huì)拋出異常。

      Headers.of("name1","value1","name2","value2",.....);

      還可以使用它的重載方法of(Map<String,String> map)方法來(lái)創(chuàng)建

      (2)構(gòu)建者模式創(chuàng)建:

      Headers mHeaders=new Headers.Builder()
                  .set("name1","value1")//set表示name1是唯一的,會(huì)覆蓋掉已經(jīng)存在的
                  .add("name2","value2")//add不會(huì)覆蓋已經(jīng)存在的頭,可以存在多個(gè)
                  .build();

      我們來(lái)看一下Header的內(nèi)部,源碼就不粘貼了很簡(jiǎn)單,Headers內(nèi)部是通過一個(gè)數(shù)組來(lái)保存header private final String[] namesAndValues;大家可能會(huì)有這樣的疑問,為什么不用Map而用數(shù)組呢?因?yàn)镸ap的Key是唯一的,而header要求不唯一

      另外,數(shù)組方便取數(shù)據(jù)嗎?很方便,我們來(lái)看著三個(gè)方法

      最后通過toString方法轉(zhuǎn)變成String,方便寫入請(qǐng)求頭,

       1 @Override 
       2 public String toString() {
       3     StringBuilder result = new StringBuilder();
       4     for (int i = 0, size = size(); i < size; i++) {
       5       result.append(name(i)).append(": ").append(value(i)).append("\n");
       6     }
       7     return result.toString();
       8 }
       9 
      10 /** Returns the field at {@code position} or null if that is out of range. */
      11 public String name(int index) {
      12     int nameIndex = index * 2;
      13     if (nameIndex < 0 || nameIndex >= namesAndValues.length) {
      14       return null;
      15     }
      16     return namesAndValues[nameIndex];
      17 }
      18  
      19 /** Returns the value at {@code index} or null if that is out of range. */
      20 public String value(int index) {
      21     int valueIndex = index * 2 + 1;
      22     if (valueIndex < 0 || valueIndex >= namesAndValues.length) {
      23       return null;
      24     }
      25     return namesAndValues[valueIndex];
      26   }

       

      3.RequestBody

       requestBody也就是請(qǐng)求實(shí)體內(nèi)容,我們先來(lái)看一下如何來(lái)構(gòu)建一個(gè)RequestBody

      (1)Request.create()方法創(chuàng)建

       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");
       4 
       5 //構(gòu)建字符串請(qǐng)求體
       6 RequestBody body1 = RequestBody.create(TEXT, string);
       7 //構(gòu)建字節(jié)請(qǐng)求體
       8 RequestBody body2 = RequestBody.create(STREAM, byte);
       9 //構(gòu)建文件請(qǐng)求體
      10 RequestBody body3 = RequestBody.create(STREAM, file);
      11 //post上傳json
      12 RequestBody body4 = RequestBody.create(JSON, json);//json為String類型的
      13 
      14 //將請(qǐng)求體設(shè)置給請(qǐng)求方法內(nèi)
      15 Request request = new Request.Builder()
      16 .url(url)
      17 .post(xx)// xx表示body1,body2,body3,body4中的某一個(gè)
      18 .build();
      19  
      View Code

      (2)構(gòu)建表單請(qǐng)求體,提交鍵值對(duì)(OkHttp3沒有FormEncodingBuilder這個(gè)類,替代它的是功能更加強(qiáng)大的FormBody:)

       //構(gòu)建表單RequestBody
      RequestBody formBody=new FormBody.Builder()
                       .add("name","maplejaw")
                       .add("age","18")
                        ...     
                       .build();

       (3)構(gòu)建分塊表單請(qǐng)求體:(OkHttp3取消了MultipartBuilder,取而代之的是MultipartBody.Builder()
                        既可以添加表單,又可以也可以添加文件等二進(jìn)制數(shù)據(jù))

       1 public static final MediaType STREAM = MediaType.parse("application/octet-stream");
       2  //構(gòu)建表單RequestBody
       3 RequestBody multipartBody=new MultipartBody.Builder()
       4                 .setType(MultipartBody.FORM)//指明為 multipart/form-data 類型
       5                 .addFormDataPart("age","20") //添加表單數(shù)據(jù)
       6                 .addFormDataPart("avatar","111.jpg",RequestBody.create(STREAM,file)) //添加文件,其中avatar為表單名,111.jpg為文件名。
       7                 .addPart(..)//該方法用于添加RequestBody,Headers和添加自定義Part,一般來(lái)說(shuō)以上已經(jīng)夠用
       8                 .build();
       9  
      10  

      知道了RequestBody的創(chuàng)建,我們來(lái)看一下源碼
      RequestBody也就是請(qǐng)求實(shí)體內(nèi)容,對(duì)于一個(gè)Get請(qǐng)求時(shí)沒有實(shí)體內(nèi)容的,Post提交才有,而且瀏覽器與服務(wù)器通信時(shí)基本上只有表單上傳才會(huì)用到POST提交,所以RequestBody其實(shí)也就是封裝了瀏覽器表單上傳時(shí)對(duì)應(yīng)的實(shí)體內(nèi)容,對(duì)于實(shí)體內(nèi)容是什么樣還不清楚的可以去看一下我的一篇博客Android的Http協(xié)議的通信詳解

      OkHttp3中RequestBody有三種創(chuàng)建方式

       ①方式一:

       public static RequestBody create(MediaType contentType, String content) {
          Charset charset = Util.UTF_8;
          if (contentType != null) {
            charset = contentType.charset();//MediaType的為請(qǐng)求頭中的ContentType創(chuàng)建方式:public static final MediaType TEXT = 
                                            //MediaType.parse("text/plain; charset=utf-8")
            if (charset == null) {
              charset = Util.UTF_8;<span style="font-family:Microsoft YaHei;">//如果contentType中沒有指定charset,默認(rèn)使用UTF-8</span>
              contentType = MediaType.parse(contentType + "; charset=utf-8");
            }
          }
          byte[] bytes = content.getBytes(charset);
          return create(contentType, bytes);
        }
      View Code

      ②方式二:FormBody表單創(chuàng)建,我們來(lái)看一下

      FormBody用于普通post表單上傳鍵值對(duì),我們先來(lái)看一下創(chuàng)建的方法,再看源碼

      1  RequestBody formBody=new FormBody.Builder()
      2                  .add("name","maplejaw")
      3                  .add("age","18")
      4                   ...     
      5                  .build();

      FormBody源碼

       1 public final class FormBody extends RequestBody {
       2   private static final MediaType CONTENT_TYPE =
       3       MediaType.parse("application/x-www-form-urlencoded");
       4 
       5   private final List<String> encodedNames;
       6   private final List<String> encodedValues;
       7 
       8   private FormBody(List<String> encodedNames, List<String> encodedValues) {
       9     this.encodedNames = Util.immutableList(encodedNames);
      10     this.encodedValues = Util.immutableList(encodedValues);
      11   }
      12 
      13   /** The number of key-value pairs in this form-encoded body. */
      14   public int size() {
      15     return encodedNames.size();
      16   }
      17 
      18   public String encodedName(int index) {
      19     return encodedNames.get(index);
      20   }
      21 
      22   public String name(int index) {
      23     return percentDecode(encodedName(index), true);
      24   }
      25 
      26   public String encodedValue(int index) {
      27     return encodedValues.get(index);
      28   }
      29 
      30   public String value(int index) {
      31     return percentDecode(encodedValue(index), true);
      32   }
      33 
      34   @Override public MediaType contentType() {
      35     return CONTENT_TYPE;
      36   }
      37 
      38   @Override public long contentLength() {
      39     return writeOrCountBytes(null, true);
      40   }
      41 
      42   @Override public void writeTo(BufferedSink sink) throws IOException {
      43     writeOrCountBytes(sink, false);
      44   }
      45 
      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;
      54 
      55     Buffer buffer;
      56     if (countBytes) {
      57       buffer = new Buffer();
      58     } else {
      59       buffer = sink.buffer();
      60     }
      61 
      62     for (int i = 0, size = encodedNames.size(); i < size; i++) {
      63       if (i > 0) buffer.writeByte('&');
      64       buffer.writeUtf8(encodedNames.get(i));
      65       buffer.writeByte('=');
      66       buffer.writeUtf8(encodedValues.get(i));
      67     }
      68 
      69     if (countBytes) {
      70       byteCount = buffer.size();
      71       buffer.clear();
      72     }
      73 
      74     return byteCount;
      75   }
      76 
      77   public static final class Builder {
      78     private final List<String> names = new ArrayList<>();
      79     private final List<String> values = new ArrayList<>();
      80 
      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     }
      86 
      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     }
      92 
      93     public FormBody build() {
      94       return new FormBody(names, values);
      95     }
      96   }
      97 }
      View Code

      我們主要來(lái)看一下方法writeOrCountBytes,通過writeOrCountBytes來(lái)計(jì)算請(qǐng)求體大小和將請(qǐng)求體寫入BufferedSink。

      至于BufferSink和Buffer類,這兩個(gè)類是Okio中的類,Buffer相當(dāng)于一個(gè)緩存區(qū),BufferedSink相當(dāng)于OutputStream,它擴(kuò)展了

      OutputStream的功能,Okio的完整源碼我后續(xù)也會(huì)寫博客

      ③方式三:MultipartBody分塊表單創(chuàng)建

      MultipartBody, 既可以添加表單,又可以也可以添加文件等二進(jìn)制數(shù)據(jù),我們就看幾個(gè)重要的方法

       public static Part createFormData(String name, String filename, RequestBody body) {
            if (name == null) {
              throw new NullPointerException("name == null");
            }
            StringBuilder disposition = new StringBuilder("form-data; name=");
            appendQuotedString(disposition, name);
       
            if (filename != null) {
              disposition.append("; filename=");
              appendQuotedString(disposition, filename);
            }
       
            return create(Headers.of("Content-Disposition", disposition.toString()), body);
          }
      View Code

      我們來(lái)看這個(gè)方法,我們是addPart還是addFormDataPart最終都走到了這個(gè)方法,封裝成一個(gè)Part對(duì)象,也就是實(shí)體內(nèi)容中

      的Content-Disposition跟文件二進(jìn)制流或者鍵值對(duì)的值

      MultipartBody和FormBody大體上相同,主要區(qū)別在于writeOrCountBytes方法,分塊表單主要是將每個(gè)塊的大小進(jìn)行累加來(lái)求出請(qǐng)求體大小,如果其中有一個(gè)塊沒有指定大小,就會(huì)返回-1。所以分塊表單中如果包含文件,默認(rèn)是無(wú)法計(jì)算出大小的,除非你自己給文件的RequestBody指定contentLength。

        private long writeOrCountBytes(BufferedSink sink, boolean countBytes) throws IOException {
          long byteCount = 0L;
       
          Buffer byteCountBuffer = null;
          if (countBytes) {
            //如果是計(jì)算大小的話,就new個(gè)
            sink = byteCountBuffer = new Buffer();
          }
          //循環(huán)塊
          for (int p = 0, partCount = parts.size(); p < partCount; p++) {
            Part part = parts.get(p);
            //獲取每個(gè)塊的頭
            Headers headers = part.headers;
            //獲取每個(gè)塊的請(qǐng)求體
            RequestBody body = part.body;
       
            //寫 --xxxxxxxxxx 邊界     
            sink.write(DASHDASH);
            sink.write(boundary);
            sink.write(CRLF);
       
            //寫塊的頭
            if (headers != null) {
              for (int h = 0, headerCount = headers.size(); h < headerCount; h++) {
                sink.writeUtf8(headers.name(h))
                    .write(COLONSPACE)
                    .writeUtf8(headers.value(h))
                    .write(CRLF);
              }
            }
       
            //寫塊的Content_Type
            MediaType contentType = body.contentType();
            if (contentType != null) {
              sink.writeUtf8("Content-Type: ")
                  .writeUtf8(contentType.toString())
                  .write(CRLF);
            }
       
            //寫塊的大小
            long contentLength = body.contentLength();
            if (contentLength != -1) {
              sink.writeUtf8("Content-Length: ")
                  .writeDecimalLong(contentLength)
                  .write(CRLF);
            } else if (countBytes) {
              // We can't measure the body's size without the sizes of its components.
              //如果有個(gè)塊沒有這名大小,就返回-1.
              byteCountBuffer.clear();
              return -1L;
            }
       
            sink.write(CRLF);
       
            //如果是計(jì)算大小就累加,否則寫入BufferedSink
            if (countBytes) {
              byteCount += contentLength;
            } else {
              body.writeTo(sink);
            }
       
            sink.write(CRLF);
          }
       
       //寫 --xxxxxxxxxx-- 結(jié)束邊界
          sink.write(DASHDASH);
          sink.write(boundary);
          sink.write(DASHDASH);
          sink.write(CRLF);
       
       
          if (countBytes) {
            byteCount += byteCountBuffer.size();
            byteCountBuffer.clear();
          }
       
          return byteCount;
        }
      View Code

      4.CacheControl

      ( 1)  Cache-Control:

      Cache-Control指定請(qǐng)求和響應(yīng)遵循的緩存機(jī)制。在請(qǐng)求消息或響應(yīng)消息中設(shè)置Cache-Control并不會(huì)修改另一個(gè)消息處理過程中的緩存處理過程。請(qǐng)求時(shí)的緩存指令有下幾種:

      1. Public:所有內(nèi)容都將被緩存(客戶端和代理服務(wù)器都可緩存)。
      2. Private:內(nèi)容只緩存到私有緩存中(僅客戶端可以緩存,代理服務(wù)器不可緩存)
      3. no-cache:請(qǐng)求或者響應(yīng)消息不能緩存
      4. no-store:不使用緩存,也不存儲(chǔ)緩存
      5. max-age:緩存的內(nèi)容將在指定時(shí)間(秒)后失效, 這個(gè)選項(xiàng)只在HTTP 1.1可用, 并如果和Last-Modified一起使用時(shí), 優(yōu)先級(jí)較高
      6. 在 xxx 秒后,瀏覽器重新發(fā)送請(qǐng)求到服務(wù)器,指定時(shí)間(秒)內(nèi),客戶端會(huì)直接返回cache而不會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求,若過期會(huì)自動(dòng)發(fā)起網(wǎng)絡(luò)請(qǐng)求
      7. min-fresh:指示客戶端可以接收響應(yīng)時(shí)間小于當(dāng)前時(shí)間加上指定時(shí)間的響應(yīng)。
      8. max-stale:指示客戶端可以接收超出超時(shí)期間的響應(yīng)消息。如果指定max-stale消息的值,那么客戶機(jī)可以接收超出超時(shí)期指定值之內(nèi)的響應(yīng)消息。

      (2)CacheControl類

      ①常用的函數(shù)

       final CacheControl.Builder builder = new CacheControl.Builder();
                  builder.noCache();//不使用緩存,全部走網(wǎng)絡(luò)
                  builder.noStore();//不使用緩存,也不存儲(chǔ)緩存
                  builder.onlyIfCached();//只使用緩存
                  builder.noTransform();//禁止轉(zhuǎn)碼
                  builder.maxAge(10, TimeUnit.MILLISECONDS);//指示客戶機(jī)可以接收生存期不大于指定時(shí)間的響應(yīng)。
                  builder.maxStale(10, TimeUnit.SECONDS);//指示客戶機(jī)可以接收超出超時(shí)期間的響應(yīng)消息
                  builder.minFresh(10, TimeUnit.SECONDS);//指示客戶機(jī)可以接收響應(yīng)時(shí)間小于當(dāng)前時(shí)間加上指定時(shí)間的響應(yīng)。
                  CacheControl cache = builder.build();//cacheControl
      View Code

      ②CacheControl的兩個(gè)常量:

       public static final CacheControl FORCE_NETWORK = new Builder().noCache().build();//不使用緩存
        public static final CacheControl FORCE_CACHE = new Builder()
            .onlyIfCached()
            .maxStale(Integer.MAX_VALUE, TimeUnit.SECONDS)
            .build();//只使用緩存
      View Code

      ③請(qǐng)求時(shí)如何使用:

      final CacheControl.Builder builder = new CacheControl.Builder();
                  builder.maxAge(10, TimeUnit.MILLISECONDS);
                  CacheControl cache = builder.build();
                  final Request request = new Request.Builder().cacheControl(cache).url(requestUrl).build();
                  final Call call = mOkHttpClient.newCall(request);//
                  call.enqueue(new Callback() {
                      @Override
                      public void onFailure(Call call, IOException e) {
                          failedCallBack("訪問失敗", callBack);
                          Log.e(TAG, e.toString());
                      }
       
                      @Override
                      public void onResponse(Call call, Response response) throws IOException {
                          if (response.isSuccessful()) {
                              String string = response.body().string();
                              Log.e(TAG, "response ----->" + string);
                              successCallBack((T) string, callBack);
                          } else {
                              failedCallBack("服務(wù)器錯(cuò)誤", callBack);
                          }
                      }
                  });
                  return call;
              } catch (Exception e) {
                  Log.e(TAG, e.toString());
              }
      View Code

         以上如果Cache沒有過期會(huì)直接返回cache而不會(huì)去發(fā)起網(wǎng)絡(luò)請(qǐng)求,若過期自動(dòng)發(fā)起網(wǎng)絡(luò)請(qǐng)求,注意:如果您使用FORCE_CACHE和網(wǎng)絡(luò)的響應(yīng)需求,OkHttp則會(huì)返回一個(gè)504提示,告訴你不可滿足請(qǐng)求響應(yīng),所以我們加一個(gè)判斷在沒有網(wǎng)絡(luò)的情況下使用

           //判斷網(wǎng)絡(luò)是否連接
              boolean connected = NetworkUtil.isConnected(context);
               if (!connected) {
                   request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
                }

       

      posted on 2016-10-30 23:40  安卓筆記俠  閱讀(1240)  評(píng)論(0)    收藏  舉報(bào)

      主站蜘蛛池模板: 北岛玲中文字幕人妻系列| 性欧美三级在线观看| 美日韩精品一区二区三区| 国语精品自产拍在线观看网站| 中文国产成人精品久久不卡| 国产无遮挡又黄又爽不要vip软件| 国产国产久热这里只有精品| 日韩蜜桃AV无码中文字幕不卡高清一区二区| 国产精品视频一区二区三区无码| 日韩精品一区二区三区视频| 国内精品大秀视频日韩精品| 国产精品亚洲а∨天堂2021 | 精品精品亚洲高清a毛片| 开心一区二区三区激情| 国产睡熟迷奷系列网站| 国内精品自产拍在线播放| 国产女同一区二区在线| 1区2区3区4区产品不卡码网站| 污污内射在线观看一区二区少妇| 国产成人一区二区三区视频免费| 亚洲人妻一区二区精品| 精品国产片一区二区三区| 日韩在线视频网| 国产国语一级毛片| 白嫩少妇无套内谢视频| 精品久久久久久亚洲综合网| 久久综合开心激情五月天| 亚洲AV高清一区二区三区尤物| gogo无码大胆啪啪艺术| 国产无码高清视频不卡| 成人免费在线播放av| 天天色综网| 毛片内射久久久一区| 亚洲综合一区二区三区视频| 亚洲中文字幕无码一区日日添| 男人的天堂av一二三区| 亚洲成在人线在线播放无码| 淮安市| 日韩精品久久久肉伦网站| 午夜国产精品福利一二| 亚洲国产成人精品无色码|