Android的HTTP請求方式
大多數網路連線的Android應用程式都將使用HTTP傳送和接收資料
對於Http協議的工作原理,一句話概括的話,就是客戶端向伺服器發出一條HTTP請求,伺服器收到之後會返回一些資料給客戶端,然後客戶端再對這些資料進行解析和處理就可以了。
HTTP協議的主要特點:
支援客戶/伺服器模式
簡單快速:客戶向服務端請求服務時,只需傳送請求方式和路徑。
靈活:允許傳輸任意型別的資料物件。由Content-Type加以標記。
無連線:每次響應一個請求,響應完成以後就斷開連線。
無狀態:伺服器不儲存瀏覽器的任何資訊。每次提交的請求之間沒有關聯。非持續性和持續性:
HTTP1.0預設非持續性;HTTP1.1預設持續性
持續性: 瀏覽器和伺服器建立TCP連線後,可以請求多個物件
非持續性: 瀏覽器和伺服器建立TCP連線後,只能請求一個物件
POST和GET的區別
Post一般用於更新或者新增資源資訊 Get一般用於查詢操作,而且應該是安全和冪等的
Post更加安全 Get會把請求的資訊放到URL的後面
Post傳輸量一般無大小限制 Get不能大於2KB
Post執行效率低 Get執行效率略高
為什麼POST效率低,Get效率高?
Get將引數拼成URL,放到header訊息頭裡傳遞
Post直接以鍵值對的形式放到訊息體中傳遞。
但兩者的效率差距很小很小
不過由於HttpClient存在的API數量過多,擴充套件困難等缺陷,在開發中也不會推薦使用這種方式。因此在Android M(6.0版本)系統中,HttpClient的功能被完全移除了,標誌著此功能被正式棄用,所以今天就介紹一下現在官方建議使用HttpURLConnection的用法.
其實Android4.4的原始碼中HttpURLConnection已經替換成了OkHttp,這個出色的網路通訊庫可以完全替代原生的HttpURLConnection,OKHttp是由著名的Square公司開發的,這個公司在開源事業中貢獻良多,除了這裡說到的OkHttp之外,還有平時常用的像Picasso,Retrofit等著名的開源專案
OKHttp的GitHub主頁地址
HttpURLConnection是適用於大多數應用程式的通用輕量級的HTTP客戶端,這個階段發展較為穩重緩慢,但是它的重點API能使我們能夠輕鬆地改進。
在FroyoAndroid2.2版本之前,HttpURLConnection有一些令人沮喪的錯誤。特別是呼叫close()可讀的InputStream可能會中斷連線池.
通過禁用連線池來解決此問題
private void disableConnectionReuseIfNecessary(){
//HTTP connection reuse which was buggy pre-froyo
if(Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO){
System.setProperty("http.keepAlive","false");
}
}
^(●゚∀゚○)ノ好吧,接下來言歸正傳,這裡從入門開始講起
入門
首先需要獲取到HttpURLConnection的例項,一般只需要new 出一個URL物件,並傳入目標的網路地址,然後呼叫一下openConnection()方法即可,
URL url = new URL("http://blog.csdn.net/checkiming");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
在得到了HttpURLConnection的例項之後,可以設定一下HTTP請求所使用的方法。常用的兩個方法為: POST和GET .GET表示希望從伺服器那裡獲取到資料,而POST則表示希望提交資料給伺服器。
connection.setRequestMethod("GET")
/connection.setRequestMethod("POST")
接下來就可以進行一些自由的定製了,比如設定連線超時、讀取超時的毫秒數,以及伺服器希望得到的一些訊息頭等,這部分是可以根據自己的需求情況寫的,示例:
connection.setConnectTimeout(6000);
connection.setReadTimeout(6000);
之後再呼叫getInputStream()方法就可以獲取到伺服器返回的輸入流了,剩下的任務就是對輸入流進行讀取,如下所示:
InputStream in = connection.getInputStream();
最後可以呼叫disconnect()這個方法將這個HTTP連線關閉掉,如下顯示
connection.disconnect();
即使Google在大部分Android版本推薦建議使用HttpURLConnection ,但在開源盛行的今天,我們完全可以使用一些出色的網路通訊庫來替代原生的HttpURLConnection ,而OkHttp無疑是最出色的一個。
Okhttp介紹
OkHttp是一個高效的Http客戶端,有如下的特點:
支援HTTP2/SPDY黑科技
socket自動選擇最好路線,並支援自動重連
擁有自動維護的socket連線池,減少握手次數
擁有佇列執行緒池,輕鬆寫併發
擁有Interceptors輕鬆處理請求與響應(比如透明GZIP壓縮,LOGGING)
基於Headers的快取策略
之前也有說到,OKhttp是一個相對成熟的解決方案,在Android4.4的原始碼中已經將HttpURLConnection替換成了OKhttp,所以我們更有理由去相信OKhttp的強大之處.
OkHttp 處理了很多網路疑難雜症:會從很多常用的連線問題中自動恢復。如果您的伺服器配置了多個IP地址,當第一個IP連線失敗的時候,OkHttp會自動嘗試下一個IP。OkHttp還處理了代理伺服器問題和SSL握手失敗問題。
使用 OkHttp 無需重寫您程式中的網路程式碼。OkHttp實現了幾乎和java.net.HttpURLConnection一樣的API。如果你用了 Apache HttpClient,則OkHttp也提供了一個對應的okhttp-apache 模組。
這裡看到一處關於OKhttp的使用問題
注:在國內使用OkHttp會因為這個問題導致部分酷派手機使用者無法聯網,所以對於大眾app來說,需要等待這個bug修復後再使用。或者嘗試使用OkHttp的老版本。
截止到目前,OkHttp一直沒有修復,並把修復計劃延遲到了OkHttp2.3中。不是所有裝置都能重現,僅少量裝置會出現這個問題。(如果問題這麼明顯,OkHttp早就修復了)
注意如果使用jar需要匯入以下兩個包
地址
get引數
// 01. 定義okhttp
OkHttpClient okHttpClient_get = new OkHttpClient();
// 02.請求體
Request request = new Request.Builder()
.get()//get請求方式
.url("http://10.0.3.2:8080/WebServiceTest/servlet/ServcieTest?name=sy")//網址
.build();
Response response = okHttpClient_get.newCall(request).execute();
if (response.isSuccessful()) {
// 列印資料
System.out.println(response.body().string());
} else {
throw new IOException("Unexpected code " + response);
}
- enqueue是非同步方法
post請求引數
// 定義okhttp
OkHttpClient okHttpClient_post_kv = new OkHttpClient();
// 定義請求體
// 執行okhttp
RequestBody body = new FormBody.Builder()
.add("name", "sy")//新增引數體
.add("age", "18")
.build();
Request request = new Request.Builder()
.post(body) //請求引數
.url("http://10.0.3.2:8080/WebServiceTest/servlet/ServcieTest")
.build();
Response response = okHttpClient_post_kv.newCall(request).execute();
System.out.println(response.body().string());
- enqueue是非同步方法
post請求json
OkHttpClient okHttpClient_post_json = new OkHttpClient();
String json = "{\n" + " "age": "18",\n" + " "name": "sy"\n" + "}";
RequestBody body =
RequestBody.create(MediaType.parse("application/json;charset=utf-8"), json);
Request request = new Request.Builder()
.post(body)
.url("http://10.0.3.2:8080/WebServiceTest/servlet/ServcieTest")
.build();
Response response = okHttpClient_post_json.newCall(request).execute();
System.out.println(response.body().string());
上傳圖片
OkHttpClient okHttpClient_upload = new OkHttpClient();
File file = new File(Environment.getExternalStorageDirectory() + "/download", "file.txt");
RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), file);
Request request = new Request.Builder()
.post(body)
.url("http://10.0.3.2:8080/WebServiceTest/servlet/ServcieTest")
.build();
Response response = okHttpClient_upload.newCall(request).execute();
System.out.println(response.body().string());
下載圖片
OkHttpClient okHttpClient_down = new OkHttpClient();
Request request =
new Request.Builder()
.get()
.url("http://10.0.3.2:8080/WebServiceTest/p22.jpg")
.build();
okHttpClient_down.newCall(request).enqueue(MainActivity.this);
/**
* 超時錯誤,伺服器無響應
*
* @param call
* @param e
*/
@Override
public void onFailure(Call call, IOException e)
{
}
/**
* 伺服器響應
*
* @param call
* @param response
* @throws IOException
*/
@Override
public void onResponse(Call call, Response response)
throws IOException
{
InputStream inputStream = response.body().byteStream();
final BitmapDrawable bitmapDrawable = new BitmapDrawable(inputStream);
runOnUiThread(new Runnable()
{
@Override
public void run()
{
mIv_main_load_image.setImageDrawable(bitmapDrawable);
}
});
}
常用api記錄
OkHttpClient client=new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) //設定連線超時
.readTimeout(60, TimeUnit.SECONDS) //設定讀超時
.writeTimeout(60,TimeUnit.SECONDS) //設定寫超時
.retryOnConnectionFailure(true) //是否自動重連
.build(); //構建OkHttpClient物件
client.dispatcher().executorService().shutdown(); //清除並關閉執行緒池
client.connectionPool().evictAll(); //清除並關閉連線池
client.cache().close(); //清除cache
用之前的OkHttpClient物件建立一個新的OkHttpClient物件 公用執行緒池
```
OkHttpClient eagerClient = client.newBuilder()
.readTimeout(500, TimeUnit.MILLISECONDS)
.build();
Request request = new Request.Builder()
.url(“https://api.github.com/repos/square/okhttpissues“)
//設定訪問url
.get() //類似的有post、delete、patch、head、put等方法,對應不同的網路請求方法
.header(“User-Agent”, “OkHttpHeaders.java”) //設定header
.addHeader(“Accept”, “application/json; q=0.5”) //新增header
.removeHeader(“User-Agent”) //移除header
.headers(new Headers.Builder().add(“User-Agent”, “OkHttp Headers.java”).build())//移除原有所有header,並設定新header
.addHeader(“Accept”, “application/vnd.github.v3+json”)
.build(); //構建request
RequestBody還有兩個子類:FormBody和MultipartBody。
RequestBody formBody = new FormBody.Builder() //提交表單鍵值對
.add(“platform”, “android”) //新增鍵值對
.add(“name”, “XXX”)
.add(“subject”, “Hello”)
.addEncoded(URLEncoder.encode(“詳細”,”GBK”), //新增已編碼的鍵值對
URLEncoder.encode(“無”,”GBK”))
.add(“描述”,”你好”) //其實會自動編碼,但是無法控制編碼格式
.build();
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MediaType.parse("image/png"), new File("website/static/logo.png")))
.addFormDataPart("discription","beautiful")
.build();
Call物件
Call call=client.newCall(request); //獲取Call物件
Response response=call.execute(); //同步執行網路請求,不要在主執行緒執行
call.enqueue(new Callback()); //非同步執行網路請求
call.cancel(); //取消請求
call.isCanceled(); //查詢是否取消
call.isExecuted(); //查詢是否被執行過
ResponseBody
body.contentLength(); //body的長度
String content=body.string(); //以字串形式解碼body
byte[] byteContent=body.bytes(); //以位元組陣列形式解碼body
InputStreamReader reader=body.charStream(); //將body以字元流的形式解碼
InputStream inputStream=body.byteStream(); //將body以位元組流的形式解碼