一,概述
在使用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類的源碼是非常簡單的。本質就是從輸入流中獲取數據,但在初次使用時遇到了很多問題,現在總結如下:
- 在解析ResponseBody前ResponseBody中僅僅有流對象,調用string方法時纔開始解析流對象,所以這一步操作仍然需要與服務器有聯繫,仍然屬於訪問服務器的範疇,所以必須放在子線程中。只有得到String對象後才能跳轉到UI線程修改UI。
- 在解析ResponseBody前ResponseBody中僅僅有流對象,調用string方法就是從輸入流中讀取數據,這行代碼執行完畢表示讀取完畢。而此時再次調用string方法就得不到數據了。同理bytes方法也是從輸入流中讀取數據,所以調用string方法後再次調用bytes方法也得不到數據。
- 從ResponseBody的byteStream方法中得到inputstream對象時,並沒有從輸入流中讀取數據,此時仍然可以從string方法中獲取到數據。但是獲取string數據後,inputstream對象仍存在,但裏面已經沒有數據了。