HTTP網絡請求中的GET,POST,PUT,DELETE就對應着查,改,增,刪4個操做,而通常用的的是GET和POST。
1.GET請求的數據會附在URL以後(就是把數據放置在HTTP協議頭中),以?分割URL和傳輸數據,參數之間以&相連.若是數據是英文字母/數字,原樣發送,若是是空格,轉換爲+,若是是中文/其餘字符,則直接把字符串用BASE64加密。
GET方式提交的數據受瀏覽器及服務器對URL長度的限制。
可使用GET方式提交數據流,獲取數據流或下載文件。java
2.POST把提交的數據放置在是HTTP包的包體中,必需要到FORM(表單)。
POST方式提交的數據受服務器對錶單處理能力的限制。
能夠用POST方式提交數據流或文件,獲取數據流。json
這裏介紹的HttpURLConnection,是java的標準類, 在Android2.3版本及之後,HttpURLConnection是最佳的選擇。它的API簡單,體積較小。默認帶gzip壓縮和緩存機制能夠有效地減小網絡訪問的流量,在提高速度和省電方面也起到了較大的做用。
* 注意:在Android2.2版本以前,HttpURLConnection一直存在着一些使人厭煩的bug,建議用HttpClient。 此外,不建議上傳大文件,若是要上傳大文件,建議用HttpClient。瀏覽器
HttpURLConnection在用POST方式發送URL請求時,URL請求參數的設定順序是重中之重:
對connection對象的一切配置(那一堆set函數)都必需要在connect()以前完成,若是沒有connect(),則必須在getInputStream()以前完成。
而getOutputStream().write()上傳參數或上傳文件必須在getInputStream()以前完成。緩存
也就是說,getInputStream()之後,請求已經發送出去了,全部關於請求的配置都要在此前完成,該函數的返回流已是服務器的反饋了。服務器
public class HttpURLConnectionUtil { public static final int READ_TIMEOUT = 30*1000; public static final int CONNECT_TIMEOUT = 10*1000; public static final int EXCEPTION_ERR = 888; // 文件下載的狀態 public static final int DOWNLOAD_FAIL = -1; //下載失敗 public static final int DOWNLOAD_PART = 0; //下載了一部分 public static final int DOWNLOAD_COMPLETE = 1; //徹底下載 /** * 採用POST方式提交數據流 * POST把提交的數據放置在是HTTP包的包體中,必需要到FORM(表單)。 * POST方式提交的數據受服務器對錶單處理能力的限制。此方法不支持提交大文件 * @param handler * 用消息發送機制返回HTTP Response結果 * @param urlStr * 連接 * @param params * 提交到服務器的參數 * */ public static void postWithHttpURLConnection(final Handler handler, final String urlStr, final Map<String, String> params){ StringBuffer data = new StringBuffer(); Message msg = handler.obtainMessage(); HttpURLConnection conn = null; InputStream is = null; OutputStream os = null; try { /* 發送HTTP請求前的Request部分 */ URL url = new URL(urlStr); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setReadTimeout(READ_TIMEOUT); conn.setRequestMethod("POST"); // 設置是否向connection輸出,由於這個是post請求,參數要放在http正文內,所以須要設爲true conn.setDoOutput(true); // 要讀取http的反饋,所以須要設爲true conn.setDoInput(true); // 說明正文是urlencoded編碼過的form參數,同時咱們對參數要進行UTF_8 轉碼成byte字節流 conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); // Post 請求不能使用緩存 conn.setUseCaches(false); conn.setInstanceFollowRedirects(true); // 正文的內容是經過outputStream流寫入 if( params != null ) { StringBuffer param = new StringBuffer(); int index = 0; for (Map.Entry<String, String> entry : params.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if( index > 0 ){ param.append("&"); } param.append(key); param.append("="); param.append(value); index += 1; } if(param.length() > 0) { os = conn.getOutputStream(); // 這種格式數據value先進行UTF_8 轉碼成byte字節流 os.write(param.toString().getBytes("UTF_8")); } } /* Request部分配置完成 */ /* 發送HTTP請求後的Response部分 */ int responseCode = conn.getResponseCode(); is = conn.getInputStream(); if ( is != null ) { int readSize = 0; byte[] buffer = new byte[1024]; while( ( readSize = is.read(buffer) ) != -1 ) { //將流字節經過UTF-8轉碼成字符串 String str = new String(buffer,"UTF_8"); data.append(str); } } String json = data.toString(); msg.what = responseCode; msg.obj = json; } catch (Exception e) { msg.what = EXCEPTION_ERR; msg.obj = e.getMessage(); } finally { // 無論是否發生異常,都會調用的代碼段 msg.sendToTarget(); if (os!=null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (conn!=null){ conn.disconnect(); } } } /** * 採用GET方式請求數據流 * GET提交的數據顯示放置在HTTP協議頭中,以?分割URL和傳輸數據,參數之間以&相連. * GET提交的數據受瀏覽器及服務器對URL長度的限制,通常是1024字節。 * @param handler * 用消息發送機制返回HTTP Response結果 * @param urlStr * 連接 * */ public static void getWithHttpURLConnection(final Handler handler, final String urlStr){ StringBuffer data = new StringBuffer(); Message msg = handler.obtainMessage(); HttpURLConnection conn = null; InputStream is = null; try { URL url = new URL(urlStr); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setReadTimeout(READ_TIMEOUT); conn.setRequestMethod("GET"); int responseCode = conn.getResponseCode(); is = conn.getInputStream(); if( is != null ) { int readSize = 0; byte[] buffer = new byte[1024]; is = conn.getInputStream(); while( ( readSize = is.read(buffer) ) != -1 ) { //將流字節經過UTF-8轉碼成字符串 String str = new String(buffer,"UTF_8"); data.append(str); } } msg.what = responseCode; msg.obj = data.toString(); } catch (Exception e) { msg.what = EXCEPTION_ERR; msg.obj = e.getMessage(); } finally { msg.sendToTarget(); if (conn!=null){ conn.disconnect(); } if (is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 使用GET的方法下載大文件,能夠支持斷點續傳。下載完的文件保存在用戶定義的指定位置。 * * @param handler * 與主線程的交互,返回下載結果狀態 * @param urlStr * 下載連接 * @param filePath * 下載文件保存路徑 * @param fileName * 下載文件的文件名 * */ public static void downloadFileWithHttpURLConnection(final Handler handler, final String urlStr, final String filePath, final String fileName) { DownloadFileInfo fileInfo = new DownloadFileInfo(urlStr,filePath,fileName); int status = downloadWithHttpURLConnection(fileInfo); if( status == DOWNLOAD_PART ) { if( shouldRangeDownload(fileInfo) ) { status = downloadRangeWithHttpURLConnection(fileInfo); } } Message msg = handler.obtainMessage(); msg.what = status; msg.obj = ""; handler.sendMessage(msg); } /** * 判斷是否能夠斷點續傳 * * @param DownloadFileInfo 保存下載信息: * mURL:下載連接 * mFilePath:指定下載文件保存路徑 * mFileName:下載文件的文件名 * mCompleteFileLength:待下載文件的Size * mCurrentFileLength:已經下載的文件的Size,用於斷點續傳 * mEtag:服務器爲某個文件生產的惟一標識值,每次文件有更新該值就會變化。 * */ private static boolean shouldRangeDownload(DownloadFileInfo fileInfo){ File file = new File(fileInfo.mFilePath+File.separator+fileInfo.mFileName); if(!file.exists()){ // 原來下載的文件已經被刪除了,從新下載 fileInfo.clearFileSizeRecord(); return false; } HttpURLConnection conn = null; try { URL url = new URL(fileInfo.mURL); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setRequestMethod("GET"); String acceptRange= conn.getHeaderField("Accept-Ranges"); String etag = conn.getHeaderField("ETag"); if( acceptRange!=null && acceptRange.equalsIgnoreCase("bytes") && fileInfo.mEtag!=null && etag!=null && etag.equalsIgnoreCase( fileInfo.mEtag )) { return true; } else { // 1. 服務器不支持斷點續傳 // 2. ETag改變了 // 應該從新下載整個文件 return false; } } catch (Exception e) { return false; } finally { if (conn!=null){ conn.disconnect(); } } } /** * 一次性所有下載 * 1. 不要設置ReadTimeOut,若是要設置,最好時間長一些,由於大文件的下載原本就耗時 * 2. 下載過程採用一邊讀輸入流,一邊寫入文件的方法,節約內存 * 3. 若是隻下載了一部分就被中斷,可是服務器不支持斷點續傳的話,必須從新下載 */ public static int downloadWithHttpURLConnection(DownloadFileInfo fileInfo) { HttpURLConnection conn = null; InputStream is = null; RandomAccessFile savedfile = null; try { URL url = new URL(fileInfo.mURL); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setRequestMethod("GET"); //保存文件的標示 String etag = conn.getHeaderField("ETag"); if(etag!=null){ fileInfo.mEtag = etag; } //新建須要寫入的RandomAccessFile File dir = new File(fileInfo.mFilePath); if(!dir.exists()){ dir.mkdirs(); } File file = new File(fileInfo.mFilePath+File.separator+fileInfo.mFileName); if(!file.exists()){ file.createNewFile(); } savedfile = new RandomAccessFile(file,"rwd"); int responseCode = conn.getResponseCode(); if ( responseCode == 200 ) {//用來表示請求成功. int len = conn.getContentLength(); if( len <= 0 ) { return DOWNLOAD_FAIL; } fileInfo.mCompleteFileLength = len; int readSize = 0; byte[] buffer = new byte[1024];//1k內存 is = conn.getInputStream(); while( ( readSize = is.read(buffer) ) != -1 ) { // 循環從輸入流中讀1k數據,寫入文件 savedfile.write(buffer, 0, readSize); fileInfo.mCurrentFileLength += readSize; } } else { return DOWNLOAD_FAIL; } int status = DOWNLOAD_FAIL; if( fileInfo.mCurrentFileLength > 0 ) { if( fileInfo.mCurrentFileLength == fileInfo.mCompleteFileLength ) { status = DOWNLOAD_COMPLETE; } else { status = DOWNLOAD_PART; } } return status; } catch (Exception e) { int status = DOWNLOAD_FAIL; if( fileInfo.mCurrentFileLength > 0 ) { status = DOWNLOAD_PART; } return status; } finally { if( conn != null ) { conn.disconnect(); } if( is != null ) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if( savedfile != null ) { try { savedfile.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 斷點續傳下載 * 注意:斷點續傳成功的getResponseCode()是206,不是200 */ public static int downloadRangeWithHttpURLConnection(DownloadFileInfo fileInfo) { HttpURLConnection conn = null; InputStream is = null; RandomAccessFile savedfile = null; try { URL url = new URL(fileInfo.mURL); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setRequestMethod("GET"); int startPos = fileInfo.mCurrentFileLength;//開始位置 conn.setRequestProperty("Range", "bytes=" + startPos + "-");//設置獲取實體數據的範圍 conn.connect(); //新建須要寫入的RandomAccessFile File file = new File(fileInfo.mFilePath+File.separator+fileInfo.mFileName); savedfile = new RandomAccessFile(file,"rwd"); savedfile.seek(startPos); int responseCode = conn.getResponseCode(); if ( responseCode == 206 ) {//斷點續傳成功的反饋值是206,不是200 int len = conn.getContentLength(); if( len <= 0 ) { return DOWNLOAD_FAIL; } byte[] buffer = new byte[1024]; int readSize = 0; is = conn.getInputStream(); while( (readSize=is.read(buffer)) != -1 ) { savedfile.write(buffer, 0, readSize); fileInfo.mCurrentFileLength += readSize; } } else { return DOWNLOAD_FAIL; } int status = DOWNLOAD_FAIL; if( fileInfo.mCurrentFileLength > 0 ) { if( fileInfo.mCurrentFileLength == fileInfo.mCompleteFileLength ) { status = DOWNLOAD_COMPLETE; } else { status = DOWNLOAD_PART; } } return status; } catch (Exception e) { int status = DOWNLOAD_FAIL; if( fileInfo.mCurrentFileLength > 0 ) { status = DOWNLOAD_PART; } return status; } finally { if (conn!=null){ conn.disconnect(); } if(is!=null){ try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if(savedfile!=null){ try { savedfile.close(); } catch (IOException e) { e.printStackTrace(); } } } } }