OKHttp源碼分析(三)之ResponseBody

一,概述

在使用OKHttp訪問網絡時,無論是同步請求還是異步請求,返回結果都是Response對象,所有的數據都封裝在這個對象中。
這個對象常用的方法有:

int code = response.code();//獲取響應碼
String message = response.message();//獲取響應消息
ResponseBody  body = response.body();//獲取響應體
InputStream inputStream = body.byteStream();//獲取輸入流
byte[] bytes = body.bytes();//獲取字節數組
String str = body.string();//獲取字符串數據

響應碼和響應消息很簡單,這裏不做介紹了,下面主要看response的body方法。

response的body方法返回ResponseBody對象,從ResponseBody對象中可以獲取到流,字節數組,字符串等類型的數據。下面重點講解ResponseBody對象是如何創建的,是怎麼從ResponseBody對象中獲取到不同數據的。

二,ResponseBody對象的創建

看源碼可知,ResponseBody類是一個抽象類,不能被實例化。一般使用它的子類RealResponseBody實例化對象。

RealResponseBody類的構造方法 源碼如下:

  public RealResponseBody(Headers headers, BufferedSource source) {
    this.headers = headers;
    this.source = source;
  }

由此可知,在創建RealResponseBody對象時,傳遞了BufferedSource 對象,BufferedSource 是okio庫中的輸入流,這裏就當作inputStream來使用。

注意:這個BufferedSource 對象很重要,它是網絡請求成功後返回的流對象,所有的數據都要從這個流中獲取。

在RealResponseBody類中字段source 是私有的,所以需要提供對外訪問的公共方法,如下:

  @Override public BufferedSource source() {
    return source;
  }

總結:在創建ResponseBody時,傳遞過來一個流對象。

三,從ResponseBody中獲取輸入流對象

從ResponseBody中獲取輸入流對象的代碼是:

InputStream inputStream = body.byteStream();//獲取輸入流

ResponseBody類的byteStream方法的原碼是:

  public final InputStream byteStream() {
    return source().inputStream();
  }

這個代碼很簡單,首先調用source方法返回BufferedSource 對象,BufferedSource 就是封裝的inputStream,所以可以從,BufferedSource對象中獲取inputStream對象。

三,從ResponseBody中獲取字節數組

從ResponseBody中獲取字節數組的代碼是:

byte[] bytes = body.bytes();//獲取字節數組

ResponseBody類的bytes方法的原碼是:

  public final byte[] bytes() throws IOException {
    long contentLength = contentLength();
    BufferedSource source = source();
    byte[] bytes;
    try {
      bytes = source.readByteArray();
    } finally {
      Util.closeQuietly(source);
    }
    return bytes;
  }

源碼中首先調用的是source方法,source方法返回BufferedSource 對象。然後調用BufferedSource 的readByteArray方法返回字節數組。

四,從ResponseBody中獲取字符串數據

從ResponseBody中獲取字符串數據的方法:

String str = body.string();//獲取字符串數據

ResponseBody類的string方法的原碼是:

  public final String string() throws IOException {
    return new String(bytes(), charset().name());
  }

這個方法比較簡單,先調用bytes方法得到字節數組,再將字節數組轉換爲字符串。

五,OKHttp中實現文件下載

分析ResponseBody類的原碼發現:OKHttp並沒有提供下載文件放方法。在httpURLconnection中下載文件時是先得到輸入流對象,然後從輸入流對象中讀取數據得到文件對象。在OKHttp中也能得到流對象,所以也可以自己實現文件下載,下載代碼如下:

try{
    InputStream  is = response.body().byteStream();//從服務器得到輸入流對象
    long sum = 0;
    File dir = new File(mDestFileDir);
    if (!dir.exists()){
        dir.mkdirs();
    }
    File file = new File(dir, mdestFileName);//根據目錄和文件名得到file對象
    FileOutputStream  fos = new FileOutputStream(file);
    byte[] buf = new byte[1024*8];
    int len = 0;
    while ((len = is.read(buf)) != -1){
        fos.write(buf, 0, len);
    }
    fos.flush();
    return file;

}

五,注意要點

從上面的分析可知,ResponseBody類的源碼是非常簡單的。本質就是從輸入流中獲取數據,但在初次使用時遇到了很多問題,現在總結如下:

  1. 在解析ResponseBody前ResponseBody中僅僅有流對象,調用string方法時纔開始解析流對象,所以這一步操作仍然需要與服務器有聯繫,仍然屬於訪問服務器的範疇,所以必須放在子線程中。只有得到String對象後才能跳轉到UI線程修改UI。
  2. 在解析ResponseBody前ResponseBody中僅僅有流對象,調用string方法就是從輸入流中讀取數據,這行代碼執行完畢表示讀取完畢。而此時再次調用string方法就得不到數據了。同理bytes方法也是從輸入流中讀取數據,所以調用string方法後再次調用bytes方法也得不到數據。
  3. 從ResponseBody的byteStream方法中得到inputstream對象時,並沒有從輸入流中讀取數據,此時仍然可以從string方法中獲取到數據。但是獲取string數據後,inputstream對象仍存在,但裏面已經沒有數據了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章