okhttp的基礎(chǔ)教程(磨礪營(yíng)馬劍威Android)

這篇文章主要總結(jié)Android著名網(wǎng)絡(luò)框架-okhttp的基礎(chǔ)使用。

okhttp是什么

okhttp是Android端的一個(gè)Http客戶端,其基礎(chǔ)功能相當(dāng)于Android自帶的HttpURLConnection和Apache HTTP Client,但他卻比自帶的2個(gè)Http客戶端優(yōu)越很多,一者是寫法簡(jiǎn)單,二者okhttp處理很多網(wǎng)絡(luò)復(fù)雜問(wèn)題,如會(huì)從很多常用的連接問(wèn)題中自動(dòng)恢復(fù)。如果您的服務(wù)器配置了多個(gè)IP地址,當(dāng)?shù)谝粋€(gè)IP連接失敗的時(shí)候,OkHttp會(huì)自動(dòng)嘗試下一個(gè)IP。OkHttp還處理了代理服務(wù)器問(wèn)題和SSL握手失敗等等很多問(wèn)題。關(guān)于第二者,這篇文章不討論。

okhttp的導(dǎo)入

Gradle導(dǎo)入

compile'com.squareup.okhttp3:okhttp:3.2.0'

compile'com.squareup.okio:okio:1.6.0'

okhttp基礎(chǔ)使用

這里我們主要介紹簡(jiǎn)單的使用,介紹內(nèi)容如下

1.get請(qǐng)求

2.post請(qǐng)求,參數(shù)是鍵值對(duì)

3.post請(qǐng)求,多種類型的body

4.文件下載

5.加入Gson

get請(qǐng)求

get請(qǐng)求分為同步get和異步get,兩者的區(qū)別主要get的方式是工作在另一個(gè)線程還是工作在本線程。請(qǐng)求的方式大同小異。

首先定義一個(gè)OkHttpClient對(duì)象,如下

privateOkHttpClient client

=newOkHttpClient();

然后構(gòu)建一個(gè)Request,構(gòu)建方式如下:

Requestrequest = newRequest.Builder().

url("http://www.baidu.com").

build();

這個(gè)是最簡(jiǎn)單的request的構(gòu)建方式,當(dāng)然我們可以構(gòu)建的很復(fù)雜。

Requestrequest = newRequest.Builder().

url("http://www.baidu.com").

addHeader("User-Agent","android").

header("Content-Type","text/html;

charset=utf-8").

build();

通過(guò)addHeader和header方法為請(qǐng)求增加請(qǐng)求頭部,注意使用header(name, value)可以設(shè)置唯一的name、value。如果已經(jīng)有值,舊的將被移除,然后添加新的。使用addHeader(name, value)可以添加多值(添加,不移除已有的)。

同步的get方法,通過(guò)client.newCall(request).execute()方法得到請(qǐng)求的response.

Responseresponse = okHttpClient.newCall(request).execute();

OkHttp封裝了很多處理response的方法,比如response.headers()的得到headers.

Headers headers = response.headers();

for(inti=0;i< headers.size();i++) {

System.out.println(headers.name(i) +

": " + headers.value(i)); }

結(jié)果如下:

Date:Mon,18Apr201605:23:43GMT

Content-Type:text/html; charset=utf-8

Transfer-Encoding:chunked

Connection:Keep-Alive

Vary:Accept-Encoding

Set-Cookie:BAIDUID=A323EC9BF678C0EB78E20741FD71211B:FG=1; expires=Thu,31-Dec-3723:55:55GMT; max-age=2147483647; path=/; domain=.baidu.com

Set-Cookie:BIDUPSID=A323EC9BF678C0EB78E20741FD71211B; expires=Thu,31-Dec-3723:55:55GMT; max-age=2147483647;path=/; domain=.baidu.com

Set-Cookie:PSTM=1460957023; expires=Thu,31-Dec-3723:55:55GMT; max-age=2147483647; path=/; domain=.baidu.com

Set-Cookie:BDSVRTM=0; path=/

Set-Cookie:BD_HOME=0; path=/

Set-Cookie:H_PS_PSSID=1434_19672_18281_19690_17948_18205_19558_15952_12257;path=/; domain=.baidu.com

P3P:CP=" OTI DSP COR IVA OUR IND COM "

Cache-Control:private

Cxy_all:baidu+2db7793e0e32b9f6c20be8f623e1ae43

Expires:Mon,18Apr201605:22:55GMT

X-Powered-By:HPHP

Server:BWS/1.1

X-UA-Compatible:IE=Edge,chrome=1

BDPAGETYPE:1

BDQID:0xfacc6fc10004ca96

BDUSERID:0

OkHttp-Sent-Millis:1460957021226

OkHttp-Received-Millis:1460957021430

響應(yīng)報(bào)文的實(shí)體可以通過(guò)response.body().string()獲取;如果希望獲得返回的二進(jìn)制字節(jié)數(shù)組,則調(diào)用response.body().bytes();如果你想拿到返回的inputStream,則調(diào)用response.body().byteStream()。

異步的get請(qǐng)求得到的response方法是通過(guò)如下方法

okHttpClient.newCall(request).enqueue(newCallback() {

@Override

publicvoidonFailure(Call

call, IOException e){

}

@Override

publicvoidonResponse(Call

call, Response response)throwsIOException{

}

});

在onResponse方法中,執(zhí)行請(qǐng)求成功的代碼,onFailure方法中,執(zhí)行請(qǐng)求失敗的代碼,下面給一個(gè)完整的異步get的栗子

importandroid.os.Bundle;

importandroid.os.Handler;

importandroid.support.v7.app.AppCompatActivity;

importandroid.text.method.ScrollingMovementMethod;

importandroid.widget.TextView;

importjava.io.IOException;

importokhttp3.Call;

importokhttp3.Callback;

importokhttp3.Headers;

importokhttp3.OkHttpClient;

importokhttp3.Request;

importokhttp3.Response;

publicclassMainActivityextendsAppCompatActivity{

privateOkHttpClient

okHttpClient =newOkHttpClient();

publicTextView show;

publicHandler handler =newHandler();

@Override

protectedvoidonCreate(Bundle

savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

show = (TextView) findViewById(R.id.show);

show.setMovementMethod(ScrollingMovementMethod.getInstance());

Request request =newRequest.Builder().

url("http://www.baidu.com").

addHeader("User-Agent","android").

header("Content-Type","text/html;

charset=utf-8").

get().

build();

okHttpClient.newCall(request).enqueue(newCallback() {

@Override

publicvoidonFailure(Call call, IOException e){

}

@Override

publicvoidonResponse(Call call,finalResponse response)throwsIOException{

finalHeaders headers = response.headers();

finalString str =response.body().string();

handler.post(newRunnable() {

@Override

publicvoidrun(){

for(inti =0; i

show.append(headers.name(i) +": "+ headers.value(i));

show.append(str);

}

}

});

}

});

}

}

其實(shí)按照官網(wǎng)說(shuō)的,回調(diào)是發(fā)生在response的headers準(zhǔn)備好就發(fā)生的,所以有時(shí)候請(qǐng)求response的實(shí)體部分會(huì)發(fā)生阻塞。

post請(qǐng)求——鍵值對(duì)為參數(shù)。

post請(qǐng)求和get請(qǐng)求除了在構(gòu)建request上面不同以外,在處理response上面都是一樣的,所以下面我們只討論一下post構(gòu)建request,當(dāng)然post也是支持同步post和異步post的,可以參考get方法。

在構(gòu)建post的request時(shí)候,首先用FormBody.Builder去構(gòu)建request的body部分,栗子如下,當(dāng)然這是OKHttp3的方法.

FormBody.Builder builder =newFormBody.Builder();

for(inti =0; i < key.size() ;i ++){

builder.add(key.get(i),value.get(i));

}

RequestBody body = builder.build();

builder中add的是要加入的參數(shù)鍵值對(duì)。得到要構(gòu)造的body后用

Request request =newRequest.Builder().url(url).post(body).build();

獲得請(qǐng)求的request,后面的操作就和get方法是一樣的,這里可以參考異步get的栗子,構(gòu)建一個(gè)post的request.下面的寫法原封不變。

post請(qǐng)求--多種類型的body

上文已經(jīng)說(shuō)了post和get的用法主要在構(gòu)建不同的request上面,所以接下來(lái)我們主要討論的也是如何構(gòu)建request.

參考上面,我們首先要?jiǎng)?chuàng)建一個(gè)requestBody,我們可以用下面的方式去構(gòu)建,當(dāng)然這也是okhttp3的方法

MultipartBody.Builder builder =newMultipartBody.Builder().setType(MultipartBody.FORM);

已表單上傳的形式去提交post。我們看一下builder的方法

/** Add a part to the body. */

publicBuilderaddPart(RequestBody body){

returnaddPart(Part.create(body));

}

/** Add a part to the body. */

publicBuilderaddPart(Headers headers, RequestBody body){

returnaddPart(Part.create(headers, body));

}

/** Add a form data part to the body. */

publicBuilderaddFormDataPart(String name, Stringvalue){

returnaddPart(Part.createFormData(name,value));

}

/** Add a form data part to the body. */

publicBuilderaddFormDataPart(String name, String filename, RequestBody body){

returnaddPart(Part.createFormData(name, filename, body));

}

從這里我們可以看出可以直接用public Builder addFormDataPart(String name, String filename,

RequestBody body)上傳一個(gè)File,最后一個(gè)參數(shù)是請(qǐng)求的實(shí)體,可以通過(guò)RequestBody.create(final MediaType contentType, final File

file)獲得,而MediaType則可以通過(guò)下面方法獲得

//調(diào)用judgeType方法

privatestaticfinal MediaType

MEDIA_TYPE = MediaType.parse(judgeType(fileName);

//judge方法如下

privateStringjudgeType(Stringpath) {

FileNameMap fileNameMap = URLConnection.getFileNameMap();

StringcontentTypeFor = fileNameMap.getContentTypeFor(path);

if(contentTypeFor ==null) {

contentTypeFor ="application/octet-stream";

}

returncontentTypeFor;

}

由于我后臺(tái)能力比較渣,這里用一個(gè)官網(wǎng)的例子來(lái)實(shí)現(xiàn)一遍我剛才討論的方法。

MultipartBody.Builder builder =newMultipartBody.Builder()

.setType(MultipartBody.FORM)

.addFormDataPart("image","logo-square.png",

RequestBody.create(MEDIA_TYPE_PNG,newFile("website/static/logo-square.png")));

RequestBody requestBody = builder.build();

Request request =newRequest.Builder()

.header("Authorization","Client-ID

"+"9199fdef135c122")

.url("https://api.imgur.com/3/image")

.post(requestBody)

.build();

當(dāng)然除了這個(gè)方法以外,調(diào)用如下方法也是可以的,我們可以利用name和filename自己構(gòu)造Header傳上去。

publicBuilderaddPart(Headers headers, RequestBody body){

returnaddPart(Part.create(headers, body))

栗子如下:

builder.addPart(Headers.of("Content-Disposition","form-data; name=\"" +

name + "\"; filename=\"" + fileName + "\""),fileBody);

后面的寫法和上面類似,這樣我們就實(shí)現(xiàn)了文件上傳的寫法。

文件下載

剛才我們上面已經(jīng)說(shuō)了,希望獲得返回的二進(jìn)制字節(jié)數(shù)組,則調(diào)用response.body().bytes();如果你想拿到返回的inputStream,則調(diào)用response.body().byteStream()。換句話說(shuō),文件的下載可以簡(jiǎn)單的通過(guò)get請(qǐng)求,得到相應(yīng)的response,在把他的實(shí)體轉(zhuǎn)換成二進(jìn)制流寫入文件,就是實(shí)現(xiàn)了文件的下載。主要的寫法就是文件的讀寫,跟OKHttp關(guān)系不大,當(dāng)然我們也可以用okio來(lái)實(shí)現(xiàn)文件的讀寫,這里水平有限就不介紹了。下面給一個(gè)簡(jiǎn)單的例子。

privatevoid_download(finalString url,finalString destFileDir,finalResultCallback callback){

finalRequest request =newRequest.Builder().url(url).build();

finalCall call = okHttpClient.newCall(request);

call.enqueue(newCallback() {

@Override

publicvoidonFailure(Call call, IOException e){

}

@Override

publicvoidonResponse(Call call, Response response)throwsIOException{

InputStream is =null;

byte[] buf =newbyte[2048];

intlen =0;

FileOutputStream fos =null;

try{

is =response.body().byteStream();

File file =newFile(destFileDir,getFileName(url));

fos =newFileOutputStream(file);

while((len = is.read(buf)) != -1){

fos.write(buf,0,len);

}

fos.flush();

//....省略后續(xù)對(duì)已經(jīng)保存的文件的操作

}catch(IOException e) {

e.printStackTrace();

}finally{

try{

if(is !=null) is.close();

}catch(IOException e) {

}

try

{

if(fos !=null) fos.close();

}catch(IOException e)

{

}

}

}

});

}

加入Gson

接下來(lái),我們討論一個(gè)很實(shí)際的問(wèn)題,Android的網(wǎng)絡(luò)請(qǐng)求一般不會(huì)去請(qǐng)求一個(gè)網(wǎng)站的Html,更多的是請(qǐng)求后臺(tái)接口的Json文件,所以我們用Gson來(lái)處理json的解析。這一部分和前面就不同了,前面多數(shù)講的是如何構(gòu)建不同的request來(lái)得到response,而對(duì)響應(yīng)的結(jié)果,處理都是一致的。但這里主要的寫法就是用Gson去處理response,而request的構(gòu)建則根據(jù)上面介紹的方法去構(gòu)建,無(wú)需改變。

Gson的導(dǎo)入

compile'com.google.code.gson:gson:2.6.2'

比如我們后臺(tái)給出的api是這樣一個(gè)json文件

{

"status":0,

"intro":"你好",

"shopName":"byhieg",

"message":"查詢成功",

}

則我們可以簡(jiǎn)單的構(gòu)建這樣的一個(gè)Test.java文件,如下所示:

publicclassTest{

/**

* status : 0

* intro : byhieg

* shopName : byhige

* message :查詢成功

*/

privateintstatus;

privateString intro;

privateString shopName;

privateString message;

publicintgetStatus(){

returnstatus;

}

publicvoidsetStatus(intstatus){

this.status = status;

}

publicStringgetIntro(){

returnintro;

}

publicvoidsetIntro(String

intro){

this.intro = intro;

}

publicStringgetShopName(){

returnshopName;

}

publicvoidsetShopName(String

shopName){

this.shopName = shopName;

}

publicStringgetMessage(){

returnmessage;

}

publicvoidsetMessage(String

message){

this.message = message;

}

}

在獲得到response之后,用如下代碼把Json文件解析成result對(duì)象。然后調(diào)用result對(duì)象的get方法就可以得到j(luò)son文件中的intro的值和shopname的值,以及status和message.這里就不多介紹了

Test result =newGson().fromJson(response.body().string,Test.class);

本文簡(jiǎn)單介紹了okhttp的使用,更多內(nèi)容關(guān)注微信公眾號(hào)mjw-java或訪問(wèn)www.moliying.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容