Android 网络请求详解
我們知道大多數(shù)的 Android 應(yīng)用程序都是通過和服務(wù)器進行交互來獲取數(shù)據(jù)的。如果使用 HTTP 協(xié)議來發(fā)送和接收網(wǎng)絡(luò)數(shù)據(jù),就免不了使用 HttpURLConnection 和 HttpClient,而 Android 中主要提供了上述兩種方式來進行 HTTP 操作。并且這兩種方式都支持 HTTPS 協(xié)議、以流的形式進行上傳和下載、配置超時時間、IPv6、以及連接池等功能。
但是 Googl e發(fā)布 6.0 版本的時候聲明原生剔除 HttpClient,但是筆者認(rèn)為 HttpClient 會提供相應(yīng)的 jar 包做支持,畢竟 Google 對向下兼容這方面一直都做的很好,相信在選擇網(wǎng)絡(luò)功能的時候我們會選自己喜歡的方法。
HttpURLConnection
接著我們來看一下如何使用 HttpURLConnection 來處理簡單的網(wǎng)絡(luò)請求。
// 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(); // 設(shè)置連接超時時間 urlConn.setConnectTimeout(6 * 1000); // 開始連接 urlConn.connect(); // 判斷請求是否成功 if (urlConn.getResponseCode() == HTTP_200) { // 獲取返回的數(shù)據(jù) byte[] data = readStream(urlConn.getInputStream()); Log.i(TAG_GET, new String(data, "UTF-8")); } else { Log.i(TAG_GET, "Get方式請求失敗"); } // 關(guān)閉連接 urlConn.disconnect(); } // Post方式請求 public static void requestByPost() throws Throwable { String path = "http://10.128.7.34:3000/login"; // 請求的參數(shù)轉(zhuǎn)換為byte數(shù)組 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(); // 設(shè)置連接超時時間 urlConn.setConnectTimeout(5 * 1000); // Post請求必須設(shè)置允許輸出 urlConn.setDoOutput(true); // Post請求不能使用緩存 urlConn.setUseCaches(false); // 設(shè)置為Post請求 urlConn.setRequestMethod("POST"); urlConn.setInstanceFollowRedirects(true); // 配置請求Content-Type urlConn.setRequestProperty("Content-Type","application/x-www-form-urlencode"); // 開始連接 urlConn.connect(); // 發(fā)送請求參數(shù) DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream()); dos.write(postData); dos.flush(); dos.close(); // 判斷請求是否成功 if (urlConn.getResponseCode() == HTTP_200) { // 獲取返回的數(shù)據(jù) byte[] data = readStream(urlConn.getInputStream()); Log.i(TAG_POST, new String(data, "UTF-8")); } else { Log.i(TAG_POST, "Post方式請求失敗"); } }關(guān)于HttpURLConnection的相關(guān)開源工程
由于 Google 已經(jīng)表明了推薦廣大開發(fā)者使用 HttpURLConnection,那么想必是有一定原因的。
xUtils3
這里就推薦大伙都很熟悉的開源項目 xUtils 的“弟弟“,xUtils3。xUtils 包含了 view 注入,圖片處理,數(shù)據(jù)庫操作以及網(wǎng)絡(luò)請求封裝,xUtils 使用的網(wǎng)絡(luò)請求封裝是基于 HttpClient 的。xUtils3 使用的網(wǎng)絡(luò)請求是基于 HttpURLConnection,我們著重說明一下xUtils3。
RequestParams params = new RequestParams("http://10.128.7.34:3000/upload"); // 加到url里的參數(shù), http://xxxx/s?wd=xUtilsparams.addQueryStringParameter("wd", "xUtils");// 添加到請求 body 體的參數(shù), 只有 POST, PUT, PATCH, DELETE 請求支持.// params.addBodyParameter("wd", "xUtils");// 使用 multipart 表單上傳文件params.setMultipart(true);params.addBodyParameter("file",new File("/sdcard/test.jpg"),null); // 如果文件沒有擴展名, 最好設(shè)置contentType 參數(shù).params.addBodyParameter("file2",new FileInputStream(new File("/sdcard/test2.jpg")),"image/jpeg",// 測試中文文件名"你+& \" 好.jpg"); // InputStream 參數(shù)獲取不到文件名, 最好設(shè)置, 除非服務(wù)端不關(guān)心這個參數(shù).x.http().post(params, new Callback.CommonCallback<String>() {@Overridepublic void onSuccess(String result) {Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();}@Overridepublic void onError(Throwable ex, boolean isOnCallback) {Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();}@Overridepublic void onCancelled(CancelledException cex) {Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();}@Overridepublic void onFinished() {}});具有 cache 功能
RequestParams params = new RequestParams("http://10.128.7.34:3000/upload"); params.wd = "xUtils";// 默認(rèn)緩存存活時間, 單位:毫秒.(如果服務(wù)沒有返回有效的max-age 或 Expires)params.setCacheMaxAge(1000 * 60);Callback.Cancelable cancelable// 使用 CacheCallback, xUtils 將為該請求緩存數(shù)據(jù).= x.http().get(params, new Callback.CacheCallback<String>() {private boolean hasError = false;private String result = null;@Overridepublic boolean onCache(String result) {// 得到緩存數(shù)據(jù), 緩存過期后不會進入這個方法.// 如果服務(wù)端沒有返回過期時間, 參考params.setCacheMaxAge(maxAge)方法.//// * 客戶端會根據(jù)服務(wù)端返回的 header 中 max-age 或 expires 來確定本地緩存是否給 onCache 方法.// 如果服務(wù)端沒有返回 max-age 或 expires, 那么緩存將一直保存, 除非這里自己定義了返回false的// 邏輯, 那么xUtils將請求新數(shù)據(jù), 來覆蓋它.//// * 如果信任該緩存返回 true, 將不再請求網(wǎng)絡(luò);// 返回 false 繼續(xù)請求網(wǎng)絡(luò), 但會在請求頭中加上ETag, Last-Modified等信息,// 如果服務(wù)端返回 304, 則表示數(shù)據(jù)沒有更新, 不繼續(xù)加載數(shù)據(jù).//this.result = result;return false; // true: 信任緩存數(shù)據(jù), 不在發(fā)起網(wǎng)絡(luò)請求; false 不信任緩存數(shù)據(jù).}@Overridepublic void onSuccess(String result) {// 注意: 如果服務(wù)返回 304 或 onCache 選擇了信任緩存, 這里將不會被調(diào)用,// 但是 onFinished 總會被調(diào)用.this.result = result;}@Overridepublic void onError(Throwable ex, boolean isOnCallback) {hasError = true;Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();if (ex instanceof HttpException) { // 網(wǎng)絡(luò)錯誤HttpException httpEx = (HttpException) ex;int responseCode = httpEx.getCode();String responseMsg = httpEx.getMessage();String errorResult = httpEx.getResult();// ...} else { // 其他錯誤// ...}}@Overridepublic void onCancelled(CancelledException cex) {Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();}@Overridepublic void onFinished() {if (!hasError && result != null) {// 成功獲取數(shù)據(jù)Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();}}});post請求直接更改 post 方式,以及需要提交的參數(shù)即可,篇幅問題這里就不在一一列舉了。通過以上代碼可以看到,我們在使用 xUtils 來請求網(wǎng)絡(luò)的時候會非常的方便,只用在回調(diào)函數(shù)里面做對應(yīng)的代碼邏輯處理即可,不用關(guān)心線程問題,極大的解放了我們的生產(chǎn)力。
Android Stuido Gradle使用方法如下:
Volley
在 Android 2.3 及以上版本,使用的是 HttpURLConnection,而在Android 2.2 及以下版本,使用的是 HttpClient。鑒于現(xiàn)在的手機行業(yè)發(fā)展速度,我們已經(jīng)不考慮 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 網(wǎng)絡(luò)加載的處理 mImageView.setImageUrl(url, imageLoader); 當(dāng)然我們也可以定制自己的 requestmRequestQueue.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) { // 獲取返回的數(shù)據(jù) String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8"); Log.i(TAG_HTTPGET, "HttpGet 方式請求成功,返回數(shù)據(jù)如下:"); 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 參數(shù) List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("name", "helloworld")); params.add(new BasicNameValuePair("password", "android")); // 設(shè)置字符集 HttpEntity entity = new UrlEncodedFormEntity(params, HTTP.UTF_8); // 設(shè)置參數(shù)實體 httpPost.setEntity(entity); // 獲取 HttpClient 對象 HttpClient httpClient = new DefaultHttpClient(); // 獲取 HttpResponse 實例 HttpResponse httpResp = httpClient.execute(httpPost); // 判斷是夠請求成功 if (httpResp.getStatusLine().getStatusCode() == HTTP_200) { // 獲取返回的數(shù)據(jù) String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8"); Log.i(TAG_HTTPGET, "HttpPost 方式請求成功,返回數(shù)據(jù)如下:"); Log.i(TAG_HTTPGET, result); } else { Log.i(TAG_HTTPGET, "HttpPost 方式請求失敗"); } }關(guān)于 HttpClinet 的相關(guān)開源工程
HttpClient 也擁有這大量優(yōu)秀的開源工程,afinal、xUtils 以及AsyncHttpClient,也可以為廣大開發(fā)者提供已經(jīng)造好的輪子。由于xUtils 是基于 afinal 重寫的,現(xiàn)在 xUtils3 也有替代 xUtils 的趨勢,所以我們在這就簡單介紹一下 AsyncHttpClient。
AsyncHttpClient
見名知意,AsyncHttpClient 對處理異步 Http 請求相當(dāng)擅長,并通過匿名內(nèi)部類處理回調(diào)結(jié)果,Http 異步請求均位于非 UI 線程,不會阻塞 UI 操作,通過線程池處理并發(fā)請求處理文件上傳、下載、響應(yīng)結(jié)果自動打包 JSON 格式。使用起來會很方便。
//GET請求 AsyncHttpClient client = new AsyncHttpClient(); //當(dāng)然這里也可以換成 new JsonHttpResponseHandler(),我們就能直接獲得 JSON 數(shù)據(jù)了。 client.get("http://www.google.com", new AsyncHttpResponseHandler() {@Overridepublic void onStart() {// called before request is started}@Overridepublic void onSuccess(int statusCode, Header[] headers, byte[] response) {// called when response HTTP status is "200 OK"}@Overridepublic void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {// called when response HTTP status is "4XX" (eg. 401, 403, 404)}@Overridepublic 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 數(shù)據(jù)的方法 client.get("http://www.google.com", params, new TextHttpResponseHandler() {@Overridepublic void onSuccess(int statusCode, Header[] headers, String response) {System.out.println(response);}@Overridepublic void onFailure(int statusCode, Header[] headers, String responseBody, Throwable error) {Log.d("ERROR", error);} } );經(jīng)過上面的代碼發(fā)現(xiàn),AsyncHttpClient 使用起來也是異常簡潔,主要靠回調(diào)方法來處理成功或失敗之后的邏輯。仔細(xì)想想,xUtils 的處理方式和這個處理方式很類似,看來好用設(shè)計還是很受人青睞的。
OkHttp
如果兩種網(wǎng)絡(luò)請求都想使用怎么辦?那么 OkHttp 是一個最佳解決方案了。
OkHttp 在網(wǎng)絡(luò)請求方面的口碑很好,就連 Google 自己也有使用OkHttp。雖然 Google6.0 中剔除了 HttpClient 的 Api,但是由于OkHttp 的影響力以及其強大的功能,使用 OkHttp 無需重寫您程序中的網(wǎng)絡(luò)代碼。同時最重要的一點 OkHttp 實現(xiàn)了幾乎和java.net.HttpURLConnection 一樣的 API。如果您用了 Apache HttpClient,則 OkHttp 也提供了一個對應(yīng)的 okhttp-apache 模塊。足以說明 OkHttp 的強大,下面是一些例子。
- 一般的 get 請求
- 一般的 post 請求
- 基于 Http 的文件上傳
- 文件下載
- 加載圖片
- 支持請求回調(diào),直接返回對象、對象集合
- 支持 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:3.7.0'總結(jié)
Android 開發(fā)應(yīng)用少不了使用網(wǎng)絡(luò),移動互聯(lián)時代,搶占終端入口變得異常重要,那么我們在開發(fā)過程中,不管使用哪一種網(wǎng)絡(luò)請求,HttpURLConnection 或者是 HttpClient,都可以滿足我們和服務(wù)器的溝通。
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Android 网络请求详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自定义组合控件
- 下一篇: Android LayoutAnimat