編輯:關於Android編程
目前在Android開發中,開源的網絡請求框架有Android-async-http ,Volley,Okhttp等,這幾種框架都是市面上主流的,每種都有各自的優點。今天主要來看看Okhttp。之前的項目中沒有使用過Okhttp,所以這篇文章也是自己對Okhttp的初步認識,如有錯誤,歡迎指出!
一、概述。
OkHttp官網地址:http://square.github.io/okhttp/。
OkHttp GitHub地址:https://github.com/square/okhttp。首先看看OkHttp官網的介紹,
An HTTP & HTTP/2 client for Android and Java applications.
OkHttp是一個為Android和Java應用程序提供HTTP 或 HTTP/2的客戶端。
HTTP is the way modern applications network. It’s how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth. OkHttp is an HTTP client that’s efficient by default: HTTP/2 support allows all requests to the same host to share a socket. Connection pooling reduces request latency (if HTTP/2 isn’t available). Transparent GZIP shrinks download sizes. Response caching avoids the network completely for repeat requests. OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and for services hosted in redundant data centers. OkHttp initiates new connections with modern TLS features (SNI, ALPN), and falls back to TLS 1.0 if the handshake fails. Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks. OkHttp supports Android 2.3 and above. For Java, the minimum requirement is 1.7.HTTP是現代應用網絡的方式。它描述了如何交換數據及媒體。有效的使用HTTP,可以使得你更快的加載數據,節省帶寬。
OkHttp默認是一個有效的HTTP客戶端:
HTTP/2支持允許所有的請求相同的主機共享同一個套接字。
連接池可以減少請求延遲(HTTP/2不可使用)。
下載文件透明的GZIP壓縮。
響應緩存可以避免完全重復的網絡請求。
當網絡不好時,OkHttp是很堅強的:它會默認從常見的連接問題中恢復過來。如果你的服務有多個IP地址,如果第一個連接失敗,那麼OkHttp將嘗試連接備用的地址。這是必須的對於IPv4+IPv6和冗余數據中心托管的服務。OkHttp發起與新的TLS特性(SNI, ALPN)連接,如何握手失敗,將退回到TLS 1.0。
使用OkHttp很容易。 其請求/響應API設計具有流暢的構建器和不變性。 它支持同步阻塞呼叫和帶回調的異步調用。
OkHttp支持Android 2.3及更高版本。 對於Java,最低要求是1.7。
2.使用配置。
(1). 下載jar包,
點擊後面的下載連接,可以下載最新的jar, OkHttp最新jar包下載地址。
(2). Maven配置,
com.squareup.okhttp3 okhttp3.4.1
(3). Gradle配置,
compile 'com.squareup.okhttp3:okhttp:3.4.1'
二、使用例子。
1. 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(); }(1).OkHttpClient:新建一個OkHttpClient實例,用於處理請求。
(2).Request:Request是OkHttp中訪問的請求類。
(3).Builder:是輔助類,構建請求參數,如url,請求方式,請求參數,header等。
(4).Call:生成一個具體請求實例,相當於將請求封裝成了任務;兩種方式:
①、call.execute(),非異步方式,會阻塞線程,等待返回結果。這種方式不會開啟新的線程,要在android中使用這個需要自己開啟一個新線程,然後在線程中調用這個東西。
②、call.enqueue(Callback),異步方式。call.enqueue會開啟一個新的線程,在新線程中調用網絡請求。記得不要再回調方法中直接更新UI。可以通過Handler來與 android UI交互或者runOnUiThread()等。推薦使用這個異步方式。不需要自己新建線程,維護線程等。
(5).Response:響應結果。比如我們希望獲得返回的字符串,可以通過response.body().string()獲取;如果希望獲得返回的二進制字節數組,則調用response.body().bytes();如果你想拿到返回的inputStream,則調用response.body().byteStream()。
2. POST請求。
POST請求跟Get基本相同,主要是兩者的Request類的構造不一樣,POST請求需要增加RequestBody來存儲請求的參數信息。
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(); }上述例子中Request中攜帶了RequestBody(請求參數)。
當然請求參數也可以傳入鍵值對,
OkHttpClient client = new OkHttpClient(); String post(String url) throws IOException { RequestBody formBody = new FormBody.Builder() .add("platform", "android") .add("name", "bug") .build(); Request request = new Request.Builder() .url(url) .post(formBody) .build(); Response response = client.newCall(request).execute(); return response.body().string(); }
3. 文件下載。
下載文件,可以使用POST或者GET。下面例子是使用POST請求,
public void download(String url) throws IOException { OkHttpClient client = new OkHttpClient(); RequestBody formBody = new FormBody.Builder() .add("version", "123") .build(); Request request = new Request.Builder() .url(url) .post(formBody) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { callback.onCompleted(e, null); } @Override public void onResponse(Call call, Response response) throws IOException { InputStream is = null; byte[] buf = new byte[1024]; int len = 0; FileOutputStream fos = null; try { is = response.body().byteStream(); File file = new File(destFileDir, StringUtils.hashKeyForDisk(url)); fos = new FileOutputStream(file); while ((len = is.read(buf)) != -1) { fos.write(buf, 0, len); } fos.flush(); callback.onCompleted(null, file.getAbsolutePath()); } catch (IOException e) { callback.onCompleted(e, null); } finally { try { if (is != null) is.close(); } catch (IOException e) { } try { if (fos != null) fos.close(); } catch (IOException e) { } } } }); }調用接口返回成功後,從響應中以流的方式寫入到指定目錄,最後返回目錄!
4.文件上傳。
OkHttp中的文件上傳和web網頁中的上傳文件原理、方式基本一致。有關web網頁中的上傳文件,可以查看鴻洋的這篇文章, 從原理角度解析Android (Java) http 文件上傳 。上傳文件如果需要傳遞參數,
if (params != null) for (String key : params.keySet()) { sb.append("--" + BOUNDARY + "\r\n"); sb.append("Content-Disposition: form-data; name=\"" + key + "\"" + "\r\n"); sb.append("\r\n"); sb.append(params.get(key) + "\r\n"); }else{ ab.append("\r\n"); }或者直接使用OkHttp提供的方法,
MultipartBody.Builder.addFormDataPart(key,value);上傳文件的代碼是,
if (file != null) { RequestBody fileBody = RequestBody.create(MediaType.parse(CONTENTTYPEFOR), file); String fileName = file.getName(); //根據文件名設置contentType builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\""), fileBody); }完整的代碼見封裝類。
如果我們不封裝請求,那麼代碼比較雜亂,並且會有很多重復代碼。
三、封裝
/** * OkHttpClient 請求封裝類 */ public class OkHttpUtils { public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); public static final String CONTENTTYPEFOR = "application/octet-stream";//二進制數據傳輸 private volatile static OkHttpUtils mOkHttpUtils; public static OkHttpClient mOkHttpClient; public OkHttpUtils() { mOkHttpClient = new OkHttpClient(); mOkHttpClient = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).readTimeout(300, TimeUnit.SECONDS).writeTimeout(300, TimeUnit.SECONDS).build(); } public static OkHttpUtils getInstance() { if (mOkHttpUtils == null) { synchronized (OkHttpUtils.class) { if (mOkHttpUtils == null) { mOkHttpUtils = new OkHttpUtils(); } } } return mOkHttpUtils; } /** * get同步請求方法 * * @param url * @param params * @return */ public Response get(String url, Map至此,OkHttp的簡單使用就結束!params) { Request request = buildRequest(buildRequestGetUrl(url, params), null, HttpMethodType.GET); return request(request); } /** * post同步請求方法 * * @param url * @param params * @return */ public Response post(String url, Map params) { Request request = buildRequest(url, params, HttpMethodType.POST); return request(request); } /** * get異步請求方法 * * @param url * @param callback */ public static void getAsyn(String url, Map params, final TypeToken typeToken, final BaseResponseCallback callback) { Request request = buildRequest(buildRequestGetUrl(url, params), null, HttpMethodType.GET); request(request, typeToken, callback); } /** * post異步請求方法 * * @param url * @param params * @param callback */ public static void postAsyn(String url, Map params, final TypeToken typeToken, final BaseResponseCallback callback) { Request request = buildRequest(url, params, HttpMethodType.POST); request(request, typeToken, callback); } /** * 文件下載異步 * * @param url 下載地址 * @param params 參數 * @param destFileDir 本地文件存儲目錄 * @param callback 回調 */ public static void downloadFileAsyn(final String url, Map params, final String destFileDir, final BaseResponseCallback callback) { Request request = buildRequest(buildRequestGetUrl(url, params), null, HttpMethodType.GET); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { callback.onCompleted(e, null); } @Override public void onResponse(Call call, Response response) throws IOException { InputStream is = null; byte[] buf = new byte[1024]; int len = 0; FileOutputStream fos = null; try { is = response.body().byteStream(); File file = new File(destFileDir, StringUtils.hashKeyForDisk(url)); fos = new FileOutputStream(file); while ((len = is.read(buf)) != -1) { fos.write(buf, 0, len); } fos.flush(); callback.onCompleted(null, file.getAbsolutePath()); } catch (IOException e) { callback.onCompleted(e, null); } finally { try { if (is != null) is.close(); } catch (IOException e) { } try { if (fos != null) fos.close(); } catch (IOException e) { } } } }); } /** * 異步文件上傳 * * @param url * @param params * @param file * @param fileName */ public static void postAsyn(String url, Map params, File file, String fileName, final BaseResponseCallback callback) { Request request = buildFromRequestBody(url, params, file, fileName); request(request, callback); } /** * 構建請求對象 * * @param url * @param params * @param type * @return */ private static Request buildRequest(String url, Map params, HttpMethodType type) { Request.Builder builder = new Request.Builder(); builder.url(url); if (type == HttpMethodType.GET) { builder.get(); } else if (type == HttpMethodType.POST) { builder.post(buildRequestBody(params)); } return builder.build(); } /** * 通過Map的鍵值對構建請求對象的body * * @param params * @return */ private static RequestBody buildRequestBody(Map params) { FormBody.Builder builder = new FormBody.Builder(); if (params != null && params.size() != 0) { for (Map.Entry entity : params.entrySet()) { builder.add(entity.getKey(), entity.getValue()); } } return builder.build(); } //構造上傳請求,類似web表單 private static Request buildFromRequestBody(String url, Map params, File file, String name) { MultipartBody.Builder builder = new MultipartBody.Builder(); //設置類型 builder.setType(MultipartBody.FORM); if (params != null && params.size() != 0) { for (Map.Entry entity : params.entrySet()) { builder.addFormDataPart(entity.getKey(), entity.getValue()); } } if (file != null) { RequestBody fileBody = RequestBody.create(MediaType.parse(CONTENTTYPEFOR), file); String fileName = file.getName(); //根據文件名設置contentType builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + name + "\"; filename=\"" + fileName + "\""), fileBody); } RequestBody requestBody = builder.build(); return new Request.Builder() .url(url) .post(requestBody) .build(); } /** * 為HttpGet請求拼接多個參數 * * @param url * @param params * @return */ public static String buildRequestGetUrl(String url, Map params) { StringBuffer stringBuilder = new StringBuffer(); stringBuilder.append(url).append("?"); try { if (params != null && params.size() != 0) { for (Map.Entry entry : params.entrySet()) { //轉換成UTF-8 stringBuilder.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "utf-8")); stringBuilder.append("&"); } //刪除最後一個字符& stringBuilder.deleteCharAt(stringBuilder.length() - 1); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return stringBuilder.toString(); } public static Response request(final Request request) { Response response = null; try { response = mOkHttpClient.newCall(request).execute(); } catch (IOException e) { e.printStackTrace(); } return response; } /** * 封裝一個request方法,不管post或者get方法中都會用到 */ public static void request(final Request request, final TypeToken typeToken, final BaseResponseCallback callback) { mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { //返回失敗 callback.onCompleted(e, null); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()) { String responseBody = response.body().string(); try { //todo 解析json } catch (JsonParseException exception) { //json解析失敗 } } else { //返回失敗 } } }); } /** * @param request * @param callback */ public static void request(final Request request, final BaseResponseCallback callback) { mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { callback.onCompleted(e, null); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()) { String responseBody = response.body().string(); callback.onCompleted(null, responseBody); } } }); } enum HttpMethodType { GET, POST } }
當做一款APP,需要選擇本地圖片時,首先考慮的無疑是系統相冊,但是Android手機五花八門,再者手機像素的提升,大圖無法返回等異常因數,導致適配機型比較困難,微信、QQ
Bluetooth Android平台包括藍牙網絡協議棧,它允許設備以無線方式與其他藍牙設備進行數據交換的支持。應用程序框架提供了訪問通過Android藍牙API的藍牙功
第1節 線程概述安卓應用只有一個主線程-各個組件都是在這個線程中運行。作為組件的之一的Activity就是在這個線程中更新應用界面的,例如,用戶點擊界面上的一個按鈕,按鈕
用過ios的都知道ios上輸入法關閉的同時會自動關閉輸入框,那麼在android上如何實現監聽輸入法彈出和關閉呢?本篇文章就為你提供了一種可靠的實現方式。演示效果視頻地址