Android 網路請求詳解
我們知道大多數的 Android 應用程式都是通過和伺服器進行互動來獲取資料的。如果使用 HTTP 協議來傳送和接收網路資料,就免不了使用 HttpURLConnection 和 HttpClient,而 Android 中主要提供了上述兩種方式來進行 HTTP 操作。並且這兩種方式都支援 HTTPS 協議、以流的形式進行上傳和下載、配置超時時間、IPv6、以及連線池等功能。
但是 Googl e釋出 6.0 版本的時候宣告原生剔除 HttpClient,但是筆者認為 HttpClient 會提供相應的 jar 包做支援,畢竟 Google 對向下相容這方面一直都做的很好,相信在選擇網路功能的時候我們會選自己喜歡的方法。
HttpURLConnection
接著我們來看一下如何使用 HttpURLConnection 來處理簡單的網路請求。
// Get方式請求
public static void requestByGet() throws Exception {
String path = "10.128.7.34:3000/name=helloworld&password=android";
// 新建一個URL物件
URL url = new URL(path);
// 開啟一個HttpURLConnection連線
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
// 設定連線超時時間
urlConn.setConnectTimeout(6 * 1000);
// 開始連線
urlConn.connect();
// 判斷請求是否成功
if (urlConn.getResponseCode() == HTTP_200) {
// 獲取返回的資料
byte[] data = readStream(urlConn.getInputStream());
Log.i(TAG_GET, new String(data, "UTF-8"));
} else {
Log.i(TAG_GET, "Get方式請求失敗");
}
// 關閉連線
urlConn.disconnect();
}
```
// Post方式請求
public static void requestByPost() throws Throwable {
String path = "http://10.128.7.34:3000/login";
// 請求的引數轉換為byte陣列
String params = "name+ URLEncoder.encode("helloworld", "UTF-8")
+ "&password=" + URLEncoder.encode("android", "UTF-8");
byte[] postData = params.getBytes();
// 新建一個URL物件
URL url = new URL(path);
// 開啟一個HttpURLConnection連線
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
// 設定連線超時時間
urlConn.setConnectTimeout(5 * 1000);
// Post請求必須設定允許輸出
urlConn.setDoOutput(true);
// Post請求不能使用快取
urlConn.setUseCaches(false);
// 設定為Post請求
urlConn.setRequestMethod("POST");
urlConn.setInstanceFollowRedirects(true);
// 配置請求Content-Type
urlConn.setRequestProperty("Content-Type",
"application/x-www-form-urlencode");
// 開始連線
urlConn.connect();
// 傳送請求引數
DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream());
dos.write(postData);
dos.flush();
dos.close();
// 判斷請求是否成功
if (urlConn.getResponseCode() == HTTP_200) {
// 獲取返回的資料
byte[] data = readStream(urlConn.getInputStream());
Log.i(TAG_POST, new String(data, "UTF-8"));
} else {
Log.i(TAG_POST, "Post方式請求失敗");
}
}
```
關於HttpURLConnection的相關開源工程
由於 Google 已經表明了推薦廣大開發者使用 HttpURLConnection,那麼想必是有一定原因的。
xUtils3
這裡就推薦大夥都很熟悉的開源專案 xUtils 的“弟弟“,xUtils3。xUtils 包含了 view 注入,圖片處理,資料庫操作以及網路請求封裝,xUtils 使用的網路請求封裝是基於 HttpClient 的。xUtils3 使用的網路請求是基於 HttpURLConnection,我們著重說明一下xUtils3。 ``` RequestParams params = new RequestParams("http://10.128.7.34:3000/upload"); // 加到url裡的引數, http://xxxx/s?wd=xUtils params.addQueryStringParameter("wd", "xUtils"); // 新增到請求 body 體的引數, 只有 POST, PUT, PATCH, DELETE 請求支援. // params.addBodyParameter("wd", "xUtils");
// 使用 multipart 表單上傳檔案
params.setMultipart(true);
params.addBodyParameter(
"file",
new File("/sdcard/test.jpg"),
null); // 如果檔案沒有副檔名, 最好設定contentType 引數.
params.addBodyParameter(
"file2",
new FileInputStream(new File("/sdcard/test2.jpg")),
"image/jpeg",
// 測試中文檔名
"你+& \" 好.jpg"); // InputStream 引數獲取不到檔名, 最好設定, 除非服務端不關心這個引數.
x.http().post(params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
}
@Override
public void onCancelled(CancelledException cex) {
Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
}
@Override
public void onFinished() {
}
});
```
具有 cache 功能
``` RequestParams params = new RequestParams("http://10.128.7.34:3000/upload"); params.wd = "xUtils"; // 預設快取存活時間, 單位:毫秒.(如果服務沒有返回有效的max-age 或 Expires) params.setCacheMaxAge(1000 * 60); Callback.Cancelable cancelable // 使用 CacheCallback, xUtils 將為該請求快取資料. = x.http().get(params, new Callback.CacheCallback() {
private boolean hasError = false;
private String result = null;
@Override
public boolean onCache(String result) {
// 得到快取資料, 快取過期後不會進入這個方法.
// 如果服務端沒有返回過期時間, 參考params.setCacheMaxAge(maxAge)方法.
//
// * 客戶端會根據服務端返回的 header 中 max-age 或 expires 來確定本地快取是否給 onCache 方法.
// 如果服務端沒有返回 max-age 或 expires, 那麼快取將一直儲存, 除非這裡自己定義了返回false的
// 邏輯, 那麼xUtils將請求新資料, 來覆蓋它.
//
// * 如果信任該快取返回 true, 將不再請求網路;
// 返回 false 繼續請求網路, 但會在請求頭中加上ETag, Last-Modified等資訊,
// 如果服務端返回 304, 則表示資料沒有更新, 不繼續載入資料.
//
this.result = result;
return false; // true: 信任快取資料, 不在發起網路請求; false 不信任快取資料.
}
@Override
public void onSuccess(String result) {
// 注意: 如果服務返回 304 或 onCache 選擇了信任快取, 這裡將不會被呼叫,
// 但是 onFinished 總會被呼叫.
this.result = result;
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
hasError = true;
Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
if (ex instanceof HttpException) { // 網路錯誤
HttpException httpEx = (HttpException) ex;
int responseCode = httpEx.getCode();
String responseMsg = httpEx.getMessage();
String errorResult = httpEx.getResult();
// ...
} else { // 其他錯誤
// ...
}
}
@Override
public void onCancelled(CancelledException cex) {
Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
}
@Override
public void onFinished() {
if (!hasError && result != null) {
// 成功獲取資料
Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();
}
}
});
```
post請求直接更改 post 方式,以及需要提交的引數即可,篇幅問題這裡就不在一一列舉了。通過以上程式碼可以看到,我們在使用 xUtils 來請求網路的時候會非常的方便,只用在回撥函式裡面做對應的程式碼邏輯處理即可,不用關心執行緒問題,極大的解放了我們的生產力。 Android Stuido Gradle使用方法如下:
compile 'org.xutils:xutils:3.1.+'
Volley
在 Android 2.3 及以上版本,使用的是 HttpURLConnection,而在Android 2.2 及以下版本,使用的是 HttpClient。鑑於現在的手機行業發展速度,我們已經不考慮 Android2.2 了。
簡單提供一些 Volley 的例項:
//簡單的 GET 請求
mQueue = Volley.newRequestQueue(getApplicationContext());
mQueue.add(new JsonObjectRequest(Method.GET, url, null,
new Listener() {
@Override
public void onResponse(JSONObject response) {
Log.d(TAG, "response : " + response.toString());
}
}, null));
mQueue.start();
```
//對 ImageView 的操作
ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_launcher, android.R.drawable.ic_launcher);
mImageLoader.get(url, listener);
//對 ImageView 網路載入的處理 mImageView.setImageUrl(url, imageLoader);
當然我們也可以定製自己的 request
mRequestQueue.add( new GsonRequest(url, ListResponse.class, null,
new Listener() {
public void onResponse(ListResponse response) {
appendItemsToList(response.item);
notifyDataSetChanged();
}
}
}
```
HttpClient
同樣,我們來看一下 HttpClient 的簡單請求。
```
// HttpGet 方式請求
public static void requestByHttpGet() throws Exception {
String path = "http://10.128.7.34:3000/name=helloworld&password=android";
// 新建 HttpGet 物件
HttpGet httpGet = new HttpGet(path);
// 獲取 HttpClient 物件
HttpClient httpClient = new DefaultHttpClient();
// 獲取 HttpResponse 例項
HttpResponse httpResp = httpClient.execute(httpGet);
// 判斷是夠請求成功
if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {
// 獲取返回的資料
String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8");
Log.i(TAG_HTTPGET, "HttpGet 方式請求成功,返回資料如下:");
Log.i(TAG_HTTPGET, result);
} else {
Log.i(TAG_HTTPGET, "HttpGet 方式請求失敗");
}
}
```
// HttpPost 方式請求
public static void requestByHttpPost() throws Exception {
String path = "https://reg.163.com/login";
// 新建 HttpPost 物件
HttpPost httpPost = new HttpPost(path);
// Post 引數
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("name", "helloworld"));
params.add(new BasicNameValuePair("password", "android"));
// 設定字符集
HttpEntity entity = new UrlEncodedFormEntity(params, HTTP.UTF_8);
// 設定引數實體
httpPost.setEntity(entity);
// 獲取 HttpClient 物件
HttpClient httpClient = new DefaultHttpClient();
// 獲取 HttpResponse 例項
HttpResponse httpResp = httpClient.execute(httpPost);
// 判斷是夠請求成功
if (httpResp.getStatusLine().getStatusCode() == HTTP_200) {
// 獲取返回的資料
String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8");
Log.i(TAG_HTTPGET, "HttpPost 方式請求成功,返回資料如下:");
Log.i(TAG_HTTPGET, result);
} else {
Log.i(TAG_HTTPGET, "HttpPost 方式請求失敗");
}
}
關於 HttpClinet 的相關開源工程
HttpClient 也擁有這大量優秀的開源工程,afinal、xUtils 以及AsyncHttpClient,也可以為廣大開發者提供已經造好的輪子。由於xUtils 是基於 afinal 重寫的,現在 xUtils3 也有替代 xUtils 的趨勢,所以我們在這就簡單介紹一下 AsyncHttpClient。
AsyncHttpClient
見名知意,AsyncHttpClient 對處理非同步 Http 請求相當擅長,並通過匿名內部類處理回撥結果,Http 非同步請求均位於非 UI 執行緒,不會阻塞 UI 操作,通過執行緒池處理併發請求處理檔案上傳、下載、響應結果自動打包 JSON 格式。使用起來會很方便。
``` //GET請求 AsyncHttpClient client = new AsyncHttpClient(); //當然這裡也可以換成 new JsonHttpResponseHandler(),我們就能直接獲得 JSON 資料了。 client.get("http://www.google.com", new AsyncHttpResponseHandler() {
@Override
public void onStart() {
// called before request is started
}
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
// called when response HTTP status is "200 OK"
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
}
@Override
public void onRetry(int retryNo) {
// called when request is retried
}
});
```
``` //POST 請求 AsyncHttpClient client = new AsyncHttpClient(); RequestParams params = new RequestParams(); params.put("key", "value"); params.put("more", "data"); //同上,這裡一樣可以改成處理 JSON 資料的方法 client.get("http://www.google.com", params, new TextHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, String response) { System.out.println(response); }
@Override
public void onFailure(int statusCode, Header[] headers, String responseBody, Throwable error) {
Log.d("ERROR", error);
}
}
); ```
經過上面的程式碼發現,AsyncHttpClient 使用起來也是異常簡潔,主要靠回撥方法來處理成功或失敗之後的邏輯。仔細想想,xUtils 的處理方式和這個處理方式很類似,看來好用設計還是很受人青睞的。
OkHttp
如果兩種網路請求都想使用怎麼辦?那麼 OkHttp 是一個最佳解決方案了。
OkHttp 在網路請求方面的口碑很好,就連 Google 自己也有使用OkHttp。雖然 Google6.0 中剔除了 HttpClient 的 Api,但是由於OkHttp 的影響力以及其強大的功能,使用 OkHttp 無需重寫您程式中的網路程式碼。同時最重要的一點 OkHttp 實現了幾乎和java.net.HttpURLConnection 一樣的 API。如果您用了 Apache HttpClient,則 OkHttp 也提供了一個對應的 okhttp-apache 模組。足以說明 OkHttp 的強大,下面是一些例子。
- 一般的 get 請求
- 一般的 post 請求
- 基於 Http 的檔案上傳
- 檔案下載
- 載入圖片
- 支援請求回撥,直接返回物件、物件集合
- 支援 session 的保持
簡單程式碼例項
``` //GET 請求 OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build();
Response response = client.newCall(request).execute(); return response.body().string(); }
```
``` //POST 請求 public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException { RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); return response.body().string(); }
```
Android Studio Gradle 使用方式:
compile 'com.squareup.okhttp:okhttp:2.7.0'
總結
Android 開發應用少不了使用網路,移動互聯時代,搶佔終端入口變得異常重要,那麼我們在開發過程中,不管使用哪一種網路請求,HttpURLConnection 或者是 HttpClient,都可以滿足我們和伺服器的溝通。
可是釋出的 App 到使用者手中後,有用 WIFI 的,有用流量的,網路環境多樣,我們怎麼能知道使用者在什麼樣的情況下訪問伺服器的流暢度呢?
答案很簡單,只要整合了OneAPM Mobile Insight,即可輕鬆知曉網路互動情況,輕鬆瞭解使用者在使用App的過程中哪裡容易出問題,並對症下藥。
OneAPM Mobile Insight ,監控網路請求及網路錯誤,提升使用者留存。訪問 OneAPM 官方網站感受更多應用效能優化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術部落格。 本文轉自 OneAPM 官方部落格
相關文章
- Android中的幾種網路請求方式詳解Android
- Android網路請求(終) 網路請求框架RetrofitAndroid框架
- Android網路請求(3) 網路請求框架OkHttpAndroid框架HTTP
- Android網路請求(4) 網路請求框架VolleyAndroid框架
- Android網路請求(2)Android
- Android okHttp網路請求之Get/Post請求AndroidHTTP
- Android Http請求框架二:xUtils 框架網路請求AndroidHTTP框架
- Android網路請求 v1.0Android
- Android測試Http網路請求。AndroidHTTP
- 前後端資料互動(一)——網路請求詳解後端
- android 網路請求時,顯示progrossBarAndroidROS
- Android探索之HttpURLConnection網路請求AndroidHTTP
- 網路請求了
- 網路請求優化之取消請求優化
- Android App優化之高效網路請求AndroidAPP優化
- Android Volley 非同步網路請求分析Android非同步
- Android okHttp網路請求之Json解析AndroidHTTPJSON
- Http請求體詳解HTTP
- HTTP網路請求原理HTTP
- iOS原生網路請求iOS
- 網路請求圖片
- 網路請求LCNetwork
- 網路資料請求
- DNS 請求報文詳解DNS
- springMVC請求流程詳解SpringMVC
- HTTP Get,Post請求詳解HTTP
- Get和Post請求詳解
- Axios 原始碼解讀 —— 網路請求篇iOS原始碼
- iOS 網路請求快取:NSURLCache詳解iOS快取
- iOS網路請求快取:NSURLCache詳解iOS快取
- Android RxJava2+Retrofit2搭建網路請求框架AndroidRxJava框架
- Android 網路框架之OKhttp實現https請求Android框架HTTP
- get請求和post請求區別詳解
- Jest中Mock網路請求Mock
- OC:封裝網路請求封裝
- iOS 使用Moya網路請求iOS
- RxJava + Retrofit完成網路請求RxJava
- iOS網路請求穿值iOS