轉載請标明出處:http://blog.csdn.net/donkor_/article/details/53589316
前言:
OkHttp是Square開發的第三方庫,用于發送和接收基于HTTP的網絡請求。它建立在Okio庫之上,通過建立共享記憶體池,它嘗試通過标準Java I / O庫更高效地讀取和寫入資料。它還是Retrofit庫的底層庫,為使用基于REST的API提供類型安全性。Square公司是不是看着很 眼紅 ( 眼熟 ),是的沒錯,這家公司在開源的道路上做足了貢獻,造福了無數程式員。除了OkHttp外,還有Picasso、Retrofit、otto等著名的開源項目。目前OkHttp最新版本是3.x,支援Android 2.3+,是以以下所講内容都是基于OkHttp3.x。
OkHttp項目開源位址 :https://github.com/square/okhttp
基本使用:
●配置與導入
在AndroidManifest.xml檔案中打開了聯網的權限:
在Android Studio 中配置gradle:
●發送和接收網絡請求
執行個體化一個OkHttpClient并建立一個Request對象。
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
如果有任何需要添加的查詢參數,OkHttp提供的HttpUrl類可以用來構造URL:
HttpUrl.Builder urlBuilder = HttpUrl.parse("http://blog.csdn.net/donkor_").newBuilder();
//addQueryParameter 添加查詢參數
urlBuilder.addQueryParameter("name", "donkor");
urlBuilder.addQueryParameter("blog", "okhttp3");
urlBuilder.addQueryParameter("number", "8");
String url = urlBuilder.build().toString();
Request request = new Request.Builder()
.url(url)
.build();
●同步Get
因為Android不允許主線程上的網絡調用,是以隻能在單獨的線程或背景服務上進行同步調用。
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
//newCall方法會得到一個Call對象,表示一個新的網絡請求
//execute方法是同步方法,會阻塞目前線程,并傳回Response對象
okhttp3.Response response = client.newCall(request).execute();
String data=response.body().string();
if (response.isSuccessful()) {
Log.e("asd","okHttp is request success");
} else {
Log.e("asd", "okHttp is request error");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
※ 注:通過Response對象的body()方法可以得到響應體ResponseBody對象,調用其string()方法可以很友善地将響應體中的資料轉換為字元串,該方法會将所有的資料放入到記憶體之中,是以如果資料超過1M,最好不要調用string()方法以避免占用過多記憶體,這種情況下可以考慮将資料當做Stream流處理。
●異步Get
//enqueue方法調用異步請求網絡,該方法接收一個okhttp3.Callback對象,
// 且不會阻塞目前線程,會新開一個工作線程,讓實際的網絡請求在工作線程中執行。
//當異步請求成功後,會回調Callback對象的onResponse方法,在該方法中可以擷取Response對象。
// 當異步請求失敗或者調用了Call對象的cancel方法時,會回調Callback對象的onFailure方法。
// onResponse和onFailure這兩個方法都是在工作線程中執行的。
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("asd", "okHttp is request erro");
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.e("asd", "okHttp is request success");
String data=response.body().string();
//在主線程中進行UI修改操作
runOnUiThread(new Runnable() {
@Override
public void run() {
//do something
}
});
}
});
●Post方式發送String
使用HTTP POST送出請求到服務。這個例子送出了一個markdown文檔到web服務,以HTML方式渲染markdown。因為請求體會放置在記憶體中,是以應該避免用該API發送超過1M的資料。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
String postBody = ""
+ "Releases\n"
+ "--------\n"
+ "\n"
+ " * _1.0_ May 6, 2013\n"
+ " * _1.1_ June 15, 2013\n"
+ " * _1.2_ August 11, 2013\n";
//post方法接收一個RequestBody對象
//create方法第一個參數都是MediaType類型,create方法的第二個參數可以是String、File、byte[]或okio.ByteString
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody))
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
●POST方式發送Stream流
這裡我們将請求主體作為流。 請求體的内容由流寫入産生。 此示例直接流入Okio緩沖接收器。 您的程式可能更喜歡OutputStream,您可以從BufferedSink.outputStream()擷取。
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
RequestBody requestBody = new RequestBody() {
//重寫contentType()方法,傳回markdown類型的MediaType
@Override public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}
//重寫writeTo()方法,該方法會傳入一個Okia的BufferedSink類型的對象,
//可以通過BufferedSink的各種write方法向其寫入各種類型的資料,
//此例中用其writeUtf8方法向其中寫入UTF-8的文本資料。
//也可以通過它的outputStream()方法,得到輸出流OutputStream,
//進而通過OutputSteram向BufferedSink寫入資料。
@Override public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("Numbers\n");
sink.writeUtf8("-------\n");
for (int i = ; i <= ; i++) {
sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
}
}
private String factor(int n) {
for (int i = ; i < n; i++) {
int x = n / i;
if (x * i == n) return factor(x) + " × " + i;
}
return Integer.toString(n);
}
};
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
●POST方式發送檔案File
上傳檔案在實際開發中也經常用到,這裡比較簡單,直接看代碼:
public static final MediaType MEDIA_TYPE_MARKDOWN
= MediaType.parse("text/x-markdown; charset=utf-8");
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
//File("/你的檔案路徑/名稱")
File file = new File("README.md");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
.post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
●POST方式Form表單中的鍵值對
使用FormBody.Builder來建構類似于HTML 标簽的請求體。鍵值對将使用一種HTML相容形式的URL編碼來進行編碼。
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
RequestBody formBody = new FormBody.Builder()
.add("search", "Jurassic Park")
.build();
Request request = new Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(formBody)
.build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
System.out.println(response.body().string());
}
●響應緩存
要想緩存響應,就需要配置一個可以讀寫的緩存目錄,以及緩存大小的限制。 并且緩存目錄應該是私有的,不受信任的應用程式不應該能夠讀取其内容!
一個緩存目錄同時被多個緩存通路是錯誤的。 大多數應用程式應該隻需調用一次OkHttpClient(),并配置它的緩存,之後隻需要調用這個執行個體即可。 否則,兩個緩存執行個體會互相幹擾,破壞響應緩存,并可能導緻應用程式崩潰。
private final OkHttpClient client;
public CacheResponse(File cacheDirectory) throws Exception {
//設定緩存上限為M
int cacheSize = * * ;
Cache cache = new Cache(cacheDirectory, cacheSize);
//new OkHttpClient隻執行個體化一次,避免多個緩存執行個體互相幹擾
client = new OkHttpClient.Builder()
.cache(cache)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
Response response1 = client.newCall(request).execute();
if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
String response1Body = response1.body().string();
System.out.println("Response 1 response: " + response1);
//對于同一個url位址,第一次獲得緩存資料 為null
System.out.println("Response 1 cache response: " + response1.cacheResponse());
//對于同一個url位址,第一次獲得請求資料 不為null
System.out.println("Response 1 network response: " + response1.networkResponse());
Response response2 = client.newCall(request).execute();
if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
String response2Body = response2.body().string();
System.out.println("Response 2 response: " + response2);
//對于同一個url位址,第二次獲得緩存資料 不為null
System.out.println("Response 2 cache response: " + response2.cacheResponse());
//對于同一個url位址,第二次獲得請求資料 為null
System.out.println("Response 2 network response: " + response2.networkResponse());
System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
}
※ 注:如果想讓某次請求禁用緩存,可以調用request.cacheControl(CacheControl.FORCE_NETWORK)方法,這樣即便緩存目錄有對應的緩存,也會忽略緩存,強制發送網絡請求,這對于擷取最新的響應結果很有用。如果想強制某次請求使用緩存的結果,可以調用request.cacheControl(CacheControl.FORCE_CACHE),這樣不會發送實際的網絡請求,而是讀取緩存,即便緩存資料過期了,也會強制使用該緩存作為響應資料,如果緩存不存在,那麼就傳回504 Unsatisfiable Request錯誤。
●取消請求Call
當請求不再需要的時候,我們應該中止請求,比如退出目前的Activity了,那麼在Activity中發出的請求應該被中止。可以通過調用Call的cancel方法立即中止請求,如果線程正在寫入Request或讀取Response,那麼會抛出IOException異常。使用這個api可以節約網絡資源。同步請求和異步請求都可以被取消。
private final ScheduledExecutorService executor = Executors.newScheduledThreadPool();
private final OkHttpClient client = new OkHttpClient();
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a second delay.伺服器端會有兩秒的延時
.build();
final long startNanos = System.nanoTime();
final Call call = client.newCall(request);
// Schedule a job to cancel the call in second.
//用戶端送出請求秒之後,請求還未完成,這時候通過cancel方法中止了Call,請求中斷,并觸發IOException異常
executor.schedule(new Runnable() {
@Override public void run() {
System.out.printf("%.2f Canceling call.%n", (System.nanoTime() - startNanos) / f);
call.cancel();
System.out.printf("%.2f Canceled call.%n", (System.nanoTime() - startNanos) / f);
}
}, , TimeUnit.SECONDS);
try {
System.out.printf("%.2f Executing call.%n", (System.nanoTime() - startNanos) / f);
Response response = call.execute();
System.out.printf("%.2f Call was expected to fail, but completed: %s%n",
(System.nanoTime() - startNanos) / f, response);
} catch (IOException e) {
System.out.printf("%.2f Call failed as expected: %s%n",
(System.nanoTime() - startNanos) / f, e);
}
}
●設定逾時
沒有響應時使用逾時結束call。沒有響應的原因可能是客戶點連結問題、伺服器可用性問題或者這之間的其他東西。OkHttp支援連接配接,讀取和寫入逾時。
private final OkHttpClient client;
public ConfigureTimeouts() throws Exception {
//connectTimeout方法設定用戶端和伺服器建立連接配接的逾時時間
//writeTimeout方法設定用戶端上傳資料到伺服器的逾時時間
//readTimeout方法設定用戶端從伺服器下載下傳響應資料的逾時時間
client = new OkHttpClient.Builder()
.connectTimeout(, TimeUnit.SECONDS)
.writeTimeout(, TimeUnit.SECONDS)
.readTimeout(, TimeUnit.SECONDS)
.build();
}
public void run() throws Exception {
Request request = new Request.Builder()
.url("http://httpbin.org/delay/2") // This URL is served with a 2 second delay.
.build();
Response response = client.newCall(request).execute();
System.out.println("Response completed: " + response);
}
執行個體:Gson處理複雜JSON字元串,并擷取天氣情況:
1.在使用免費的天氣預報api,這裡用的是mob平台提供的,有需要的直接在下面連結注冊使用,這裡不再過多贅述。
http://api.mob.com/#/apiwiki/weather
2.mob平台擷取天氣狀況請求方式是get,這裡我們使用同步與異步get分别進行請求
3.請求成功,傳回的資料是json格式,是以我們使用Gson進行解析,并顯示在文本上
4.配置gradle與請求網絡權限
compile files('libs/gson-2.8.0.jar')
compile files('libs/okhttp-3.5.0.jar')
compile files('libs/okio-1.11.0.jar')
5.添加請求天氣預報的url位址,這裡我們以深圳為例
//url為請求位址 key=19d6b7c760314 key為注冊應用成功後獲得
private final String url = "http://apicloud.mob.com/v1/weather/query?key=19d6b7c760314&city=深圳";
6.初始化Gson與OkHttpClient
private Gson gson = new Gson();
private OkHttpClient client= new OkHttpClient();
7.根據json字元串的複雜程式,定義需要序列化的bean。下圖是我擷取得到天氣預報并列印成字元串的截圖
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0NXYFhGd192UvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2Lc1TP3hVe5ITY1lzRaZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jN3QDNwIDMwADNxITM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
8.根據上圖擷取的json字元串,要得到future最近四天的天氣,這裡我們定義三個類。
CommWeather.java
public class CommWeather {
private String retCode;
private List<Results> result;
public String getRetCode() {
return retCode;
}
public void setRetCode(String retCode) {
this.retCode = retCode;
}
public List<Results> getResult() {
return result;
}
public void setResult(List<Results> result) {
this.result = result;
}
}
Results.java
public class Results {
private String city;
private String sunrise;
private String sunset;
private List<Future> future;
public List<Future> getFuture() {
return future;
}
public void setFuture(List<Future> future) {
this.future = future;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getSunrise() {
return sunrise;
}
public void setSunrise(String sunrise) {
this.sunrise = sunrise;
}
public String getSunset() {
return sunset;
}
public void setSunset(String sunset) {
this.sunset = sunset;
}
}
Future.java
public class Future {
private String date;
private String dayTime;
private String night;
private String temperature;
private String wind;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getDayTime() {
return dayTime;
}
public void setDayTime(String dayTime) {
this.dayTime = dayTime;
}
public String getNight() {
return night;
}
public void setNight(String night) {
this.night = night;
}
public String getTemperature() {
return temperature;
}
public void setTemperature(String temperature) {
this.temperature = temperature;
}
public String getWind() {
return wind;
}
public void setWind(String wind) {
this.wind = wind;
}
}
9.使用okhttp同步/異步get擷取得到天氣預報的json資料,并進行解析,并顯示在UI上。這裡我們看下MainActivity中異步操作并解析的主要代碼:
private void getWeatherAsync() {
//CacheControl.FORCE_NETWORK 不進行緩存
Request request = new Request.Builder().url(url).cacheControl(CacheControl.FORCE_NETWORK).build();
//enqueue方法調用異步請求網絡,該方法接收一個okhttp3.Callback對象,
// 且不會阻塞目前線程,會新開一個工作線程,讓實際的網絡請求在工作線程中執行。
//當異步請求成功後,會回調Callback對象的onResponse方法,在該方法中可以擷取Response對象。
// 當異步請求失敗或者調用了Call對象的cancel方法時,會回調Callback對象的onFailure方法。
// onResponse和onFailure這兩個方法都是在工作線程中執行的。
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("asd", "okHttp is request error");
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.e("asd", "okHttp is request success");
//擷取伺服器傳回的json字元串
String responseString = response.body().string();
Log.e("asd", "responseString: " + responseString);
//使用Gson解析json字元串
CommWeather commWeather = gson.fromJson(responseString, CommWeather.class);
//retCode==200 請求成功
if (commWeather.getRetCode().equals("200")) {
List<Results> listResult = commWeather.getResult();
//根據mob的api文檔可以确定,result始終size為1
for (int i = ; i < listResult.size(); i++) {
final String city = listResult.get(i).getCity();
final String sunrise = listResult.get(i).getSunrise();
final String sunset = listResult.get(i).getSunset();
//在主線程中修改UI
runOnUiThread(new Runnable() {
@Override
public void run() {
tvCity.setTextColor(android.graphics.Color.RED);
tvCity.setText("Aysnc異步獲得的"+city + " 的日出時間: " + sunrise + " 和日落時間" + sunset);
}
});
//這裡我們隻需要擷取最近四天的天氣
final List<Future> listFuture = listResult.get(i).getFuture();
runOnUiThread(new Runnable() {
@Override
public void run() {
//修改UI操作
}
});
}
}
}
});
}
最後看下效果圖
CSDN下載下傳(内含最新jar包): http://download.csdn.net/detail/donkor_/9710663
About me
Email :[email protected]
Android開發交流QQ群 : 537891203