現在基本上所有的網路框架都採用Okhttp、rxjava、retrofit三者一起寫的。因為最近沒有什麼事情,就抽空總結一下這方面的知識:因為這些東西連在一期講的話,很多同學會覺得懵逼,所以這裡我準備先講一下每一個東西的用法,然後在講解一下怎麼聯合使用。
最近看了 《X特遣隊/自殺小隊》 覺得不錯。以一種混子的心態生活,其實挺輕鬆的!所以一張圖片鎮樓!習慣的我可以發給你!
本文知識點
- OkHttp的使用
- OkHttp上傳檔案
- OkHttp的一些高階用法
1. OkHttp的簡單使用
在這裡先來個重要的說明:網路許可權一定要加,一定要加!!!
其實關於OkHttp的使用只要記住一個順序就可以
- 建立OkHttpClient物件
- 建立請求Request內容
- 傳送請求
- 建立請求的回撥
基本上記住上面的步驟就可以實現簡單的請求了!
1.1 簡單的GET請求
既然上面都提到了相應的步驟,我們就按照上面的步驟寫一下就可以了!!!
1.1.1 建立OkHttpClient物件
OkHttpClient httpClient = new OkHttpClient();
複製程式碼
建立一個物件而已,沒有什麼好說的!!!
1.1.2 建立請求Request內容
Request request = new Request.Builder()
.method("GET", null)
.url("https://www.baidu.com/")
.build();
複製程式碼
這裡簡單說一下,method是設定相應的請求方式的;url是設定相應的請求地址的!其次Request是一個構建者的構建模式。剩下的沒有什麼好說的。,如果新手,不用管那麼多為什麼,實現效果才是重要的!!!
1.1.3 傳送請求
Call call = httpClient.newCall(request);
複製程式碼
這裡其實就是讓httpClient知道自己要請求什麼而已
1.1.4 建立請求返回的內容
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "請求失敗的原因:" + e);
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
Headers headers = response.headers();
Set<String> names = headers.names();
for (String name : names) {
Log.e(TAG, "請求的header" + name);
String value = headers.get(name);
Log.e(TAG, "值為: " + value + "\n----------------------------------");
}
final String date = response.body().string();
mHandler.post(new Runnable() {
@Override
public void run() {
mTvShow.setText(date);
}
});
}
});
複製程式碼
這裡要說明的就多了:
-
當你的看到FATAL EXCEPTION: OkHttp Dispatcher這個異常的時候,恭喜你,你踩到第一個坑了!這個主要是因為
response.body().string()
只能呼叫一次,如果你在程式碼中呼叫了兩次,那麼就會出現上面的異常; -
當你非同步請求的時候,是不能在子執行緒修改UI的,所以這裡我用了一個Handler去操作相應的內容
-
如果你想看相應的一些內容的話,那麼看那個for迴圈那裡,你列印一下,就能看到如下的內容,如果不怎麼理解的話,找你們後臺人員請教一下!一定要虛心哦。
Accept-Ranges →bytes Cache-Control →no-cache Connection →Keep-Alive Content-Length →227 Content-Type →text/html Date →Wed, 05 Sep 2018 03:41:58 GMT Etag →"5b7b7f40-e3" Last-Modified →Tue, 21 Aug 2018 02:56:00 GMT Pragma →no-cache Server →BWS/1.1 Set-Cookie →BD_NOT_HTTPS=1; path=/; Max-Age=300 Strict-Transport-Security →max-age=0 X-Ua-Compatible →IE=Edge,chrome=1 複製程式碼
-
如果失敗的話,那麼就會在onFailure中把異常反饋給你!!!
給你貼下整體程式碼吧!
/*1.建立OkHttpClient物件*/
OkHttpClient httpClient = new OkHttpClient();
/*2.建立請求Request內容*/
Request request = new Request.Builder()
.method("GET", null)
.url("https://www.baidu.com/")
.build();
/*3.傳送請求*/
Call call = httpClient.newCall(request);
/*4.建立請求的回撥*/
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "請求失敗的原因:" + e);
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
Headers headers = response.headers();
Set<String> names = headers.names();
for (String name : names) {
Log.e(TAG, "請求的header" + name);
String value = headers.get(name);
Log.e(TAG, "值為: " + value + "\n----------------------------------");
}
final String date = response.body().string();
mHandler.post(new Runnable() {
@Override
public void run() {
mTvShow.setText(date);
}
});
}
});
複製程式碼
以上步驟就能正常請求相應的資料了,如果還沒有資料的話,好好看看程式碼!
1.2 簡單的POST請求
關於POST請求的話,基本上就是比GET請求多一步設定表單的方法,也就是一個FormBody物件的設定,以key、value的方式設定表單而已,所以這裡教你怎麼寫,然後我貼一下程式碼就那麼滴了,誰讓我那麼懶呢!!!
表單的寫法是這樣的:
FormBody formBody = new FormBody.Builder()
.add("key", "value")
.build();
複製程式碼
其實add方法可以被呼叫多次,新增相應的key和value;
整體的程式碼是這樣的!!!
/*1.建立OkHttpClient物件*/
OkHttpClient httpClient = new OkHttpClient();
/*2.建立相應的表單內容*/
FormBody formBody = new FormBody.Builder()
.add("key", "value")
.build();
/*3.建立請求Request內容*/
Request request = new Request.Builder()
.url("https://www.baidu.com/")
.post(formBody)
.build();
/*4.傳送請求*/
Call call = httpClient.newCall(request);
/*5.建立請求的回撥*/
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "請求失敗的原因:" + e);
}
@Override
public void onResponse(Call call, final Response response) throws IOException {
Headers headers = response.headers();
Set<String> names = headers.names();
for (String name : names) {
Log.e(TAG, "請求的header" + name);
String value = headers.get(name);
Log.e(TAG, "值為: " + value + "\n----------------------------------");
}
final String date = response.body().string();
mHandler.post(new Runnable() {
@Override
public void run() {
mTvShow.setText(date);
}
});
}
});
}
複製程式碼
POST和GET請求只是請求的方式不同,POST比較安全,所有內容都依靠表單傳遞!
2. OkHttp3進行檔案上傳
在這裡先來個重要的說明:去寫SD卡的許可權一定要加,一定要加!!!
說到檔案上傳,一般的網路請求都帶有檔案上傳的功能,其實OkHttp3也可以上傳檔案,具體操作步驟如下:
- 建立OkHttpClient物件
- 建立請求Request內容和所有所需引數(這裡和其他的請求不同的地方)
- 獲取檔案
- 設定上傳檔案的型別
- 獲取請求體
- 建立請求
- 建立請求的回撥
因為其他的內容都差不多,只有關於表單的內容不通,所以這裡著重講一下關於這個表單的問題。
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "張三")
.addFormDataPart("image", "zhangsan.jpg", RequestBody.create(MediaType.parse("application/octet-stream"), new File(Environment.getExternalStorageDirectory().getParent() + "/0/123.png")))
.build();
複製程式碼
一般這種上傳檔案,基本上都是傳遞相應的使用者圖片,修改圖片什麼的!因為伺服器要根據你上傳的這張圖片進行相應圖片的替換。回來說上面那個配置:
- 上面第一個"title"那個引數的話,應該是一組key、value形式,基本上是根據伺服器定的引數為準,可以有多組!
- 第二個"image"那個引數的話,基本上就是這個形式的key、圖片名稱、圖片位置。這樣就能鎖定一張圖片了,這樣就構建出一個相應的RequestBody物件了。
整體程式碼是這樣的:
/*1.建立OkHttpClient物件*/
OkHttpClient httpClient = new OkHttpClient();
/*2.建立相應的表單內容*/
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("title", "張三")
.addFormDataPart("image", "zhangsan.jpg", RequestBody.create(MediaType.parse("application/octet-stream"), new File(Environment.getExternalStorageDirectory().getParent() + "/0/123.png")))
.build();
/*3.建立請求Request內容*/
Request request = new Request.Builder()
.header("key", "value")
.url("https://www.baidu.com/")
.post(requestBody)
.build();
/*4.傳送請求*/
Call call = httpClient.newCall(request);
/*5.建立請求的回撥*/
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e(TAG, "onFailure: " + e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.e(TAG, "onResponse: " + response.body().string());
}
});
複製程式碼
對了忘說了一點,圖片是以流的形式進行傳遞的。所以上面"application/octet-stream"配置的是這種格式,如果是其他的格式呢?給大家一份對照表:參照一下就OK了。
引數 | 說明 |
---|---|
text/html | HTML格式 |
text/plain | 純文字格式 |
text/xml | XML格式 |
image/gif | gif圖片格式 |
image/jpeg | jpg圖片格式 |
image/png | png圖片格式 |
application/xhtml+xml | XHTML格式 |
application/xml | XML資料格式 |
application/atom+xml | Atom XML聚合格式 |
application/json | JSON資料格式 |
application/pdf | pdf格式 |
application/msword | Word文件格式 |
application/octet-stream | 二進位制流資料 |
基本上你把上面的程式碼改吧改吧就能上傳檔案了!!!就醬紫簡單。。。
3. OkHttp的高階配置
3.1 OkHttp一些基本引數的配置
配置請求時間和連線超時的時間等等
OkHttpClient httpClient = new OkHttpClient.Builder()
//設定相應的連線池
.connectionPool(new ConnectionPool())
//連線超時
.connectTimeout(15, TimeUnit.SECONDS)
//寫入超時
.writeTimeout(15, TimeUnit.SECONDS)
//讀取超時
.readTimeout(20, TimeUnit.SECONDS)
.build();
複製程式碼
3.2 OkHttp攔截器的一些簡單理解
往往在專案中,都會有一些關於公共請求引數的一些問題,這裡就會用到相應的OkHttp攔截器!什麼是攔截器呢?簡單點說就和埋點差不多。在請求的時候,會走每一個攔截器!想新增什麼就新增什麼,這裡我們通過幾個例項講解一下你就能大概理解了!
3.2.1 日誌攔截器
先看下程式碼,然後我在做一下相應的解釋:
public class LogInterceptor implements Interceptor {
private static final String TAG = LogInterceptor.class.getSimpleName();
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
/*這樣就能在請求之前列印相應的內容了*/
Log.e("url", String.format("Sending request %s on %s %n %s", request.url(), chain.connection(), request.headers()));
/*其實下面這個chain.proceed(request)這個方法,代表請求前和請求後*/
return chain.proceed(request);
}
}
複製程式碼
這裡就是直接列印了一個相應的LOG,可以獲取到一些請求的引數,這裡說明一下:
- 當你的請求為GET請求的時候只能列印一個Url地址,也就是
request.url()
的值了,飲後後面的headers獲取到的內容為空,因為GET請求沒有相應的表單資訊; chain.connection()
當你使用除了日誌攔截器的時候,就會返回空chain.proceed(request)
代表請求響應的結果,所以說明你也是可以修改返回結果的!!!
3.2.2 重定向一個網址連結的攔截器
這個說來就有意思了,當你請求攔截器的時候,正常應該返回百度返回的內容,但是如果你修改了連結的地址會怎麼樣呢?當然就會返回你修改之後的返回地址了。。。我們看看怎麼實現的
public class ResetInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = new Request.Builder()
.method("GET", null)
.url("https://fanyi.baidu.com/translate?aldtype=16047&query=%E8%BF%9B%E5%BA%A6%0D%0A&keyfrom=baidu&smartresult=dict&lang=auto2zh#zh/en/%E9%87%8D%E7%BD%AE")
.build();
return chain.proceed(newRequest);
}
}
複製程式碼
對,你沒有看錯,就這麼赤裸裸的換了一個url地址,其實Request request = chain.request();
這個方法,返回的Request就是在建立的時候,建立的Request,所以,這裡你直接,通過攔截器,直接建立一個新的,直接返回就可以了,就沒有之前的Request什麼事情了!!!其實就相當於你把之前的內容重新寫了一遍!就醬紫了。。。
3.2.3 新增相應的公共請求引數
其實這個的實現和上面的差不多,也就是替換相應的Request的內容!但是這裡你要考慮一個問題,就是GET請求和POST請求的處理方式應該是不同的,多以這裡要分情況去處理。否則不能達到你想要的效果的!所以這裡我們分開說。先說明一下,GET請求是在Url後面拼接相應的引數,而POST請求是在form表單中新增相應的引數,所以方式一定是不一樣的!!!
1. GET請求新增公共請求引數
先來一段程式碼體驗一下:
HttpUrl build = originalRequest.url().newBuilder()
.addQueryParameter("key1", "value1")
.addQueryParameter("key2", "value2")
.addQueryParameter("key3", "value3")
.addQueryParameter("key4", "value4")
.addQueryParameter("key5", "value5")
.build();
Request request = originalRequest.newBuilder().url(build).build();
複製程式碼
這樣就可以新增相應的公共請求引數了,其實開始的時候,我以為newBuilder()是建立一個新的內容呢?其實它是拿到之前的內容,然後把下面的內容新增進去。所以這裡其他的內容是不會收到影響的!!!
其實GET請求就是在URL後面追加上相應的引數。
2. POST請求新增公共請求引數
還是先來一點程式碼體驗一下:
Request requestBuilder = originalRequest.newBuilder()
.addHeader("key1", "value1")
.addHeader("key2", "value2")
.addHeader("key3", "value3")
.addHeader("key4", "value4")
.addHeader("key5", "value5")
.build();
複製程式碼
和上面的類似,只是寫法不同而已!因為POST請求新增的是相應的header。
整體的程式碼如下:
public class PublicInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if ("GET".equals(request.method())) {
//GET請求的處理
HttpUrl build = request.url().newBuilder()
.addQueryParameter("key1", "value1")
.addQueryParameter("key2", "value2")
.addQueryParameter("key3", "value3")
.addQueryParameter("key4", "value4")
.addQueryParameter("key5", "value5")
.build();
request = request.newBuilder().url(build).build();
} else if ("POST".equals(request.method())) {
request = request.newBuilder()
.addHeader("key1", "value1")
.addHeader("key2", "value2")
.addHeader("key3", "value3")
.addHeader("key4", "value4")
.addHeader("key5", "value5")
.build();
}
return chain.proceed(request);
}
}
複製程式碼
最後在把相應的Interceptor新增到OkHttp就好了。
2018年10月15日補充:
在POST請求中,請求引數應該新增到body中,所以上面程式碼是有問題的!
替換成下面這樣:
if (originalRequest.body() instanceof FormBody) {
// 構造新的請求表單
FormBody.Builder builder = new FormBody.Builder();
FormBody body = (FormBody) originalRequest.body();
//將以前的引數新增
for (int i = 0; i < body.size(); i++) {
builder.add(body.encodedName(i), body.encodedValue(i));
}
//追加新的引數
builder.add("key1", "value1");
builder.add("key2", "value2");
builder.add("key3", "value3");
builder.add("key4", "value4");
builder.add("key5", "value5");
//構造新的請求體
originalRequest = originalRequest.newBuilder().post(builder.build()).build();
}
複製程式碼
對於以上的錯誤深表歉意,因為沒有弄清楚http中的一下內容,還請見諒!!!
基本上使用的時候就這麼多問題,可能有些講解不到的,如果有什麼不到位的,及時補充!!!有問題留言,我看到了一定會回覆你的!!!