利器OkHttp的使用以及簡單封裝
前言
Android開發中網絡編程是必不可少的,不接入互聯網的APP就沒有盈利可言。廢話不多說了,下面請先看Android中網絡請求的進化圖:
HttpURLConnection,Apache HTTP Client,Volley到現在的OKHttp,可謂天外有天,人外有人。為什么OKHttp會這么火呢,相信下面的介紹會告訴你答案。
OKHttp的簡介
首先,給出OKHttp的項目地址:https://github.com/square/okhttp
Android為我們提供了兩種HTTP交互的方式: HttpURLConnection 和 Apache HTTP Client,雖然兩者都支持HTTPS,流的上傳和下載,配置超時,IPv6和連接池,已足夠滿足我們各種HTTP請求的需求。但更高效的使用HTTP可以讓您的應用運行更快、更節省流量。而OkHttp庫就是為此而生。 OkHttp是一個高效的HTTP庫:
1.支持 SPDY ,共享同一個Socket來處理同一個服務器的所有請求
2.如果SPDY不可用,則通過連接池來減少請求延時
3.無縫的支持GZIP來減少數據流量
1.緩存響應數據來減少重復的網絡請求
2.一般的get請求
3.一般的post請求
4.基于Http的文件上傳
5.文件下載
6.加載圖片
7.支持請求回調,直接返回對象、對象集合
8.支持session的保持
OkHttp會從很多常用的連接問題中自動恢復。如果您的服務器配置了多個IP地址,當***個IP連接失敗的時候,會自動嘗試下一個IP。OkHttp還處理了代理服務器問題和SSL握手失敗問題。(并發) 使用 OkHttp 無需重寫您程序中的網絡代碼。OkHttp實現了幾乎和java.net.HttpURLConnection一樣的API。如果您用了 Apache HttpClient,則OkHttp也提供了一個對應的okhttp-apache 模塊。 從上面的簡單介紹中可以知道,雖然在編程上面并不會簡潔很多,但是OKHttp內部的一些功能能夠幫助我們自動完成一些很復雜的操作,筆者個人認為***的賣點就是大大節省用戶的流量。
OKHttp的基本使用
1、在Gradle中引入CardView的依賴庫。
- compile 'com.squareup.okhttp:okhttp:3.8.0'
2、OKHttp在使用之前,首先要了解下面幾個比較核心的類以及它的功能。
- OkHttpClient 客戶端對象
- Request是OkHttp中訪問的請求,Post請求中需要包含RequestBody
- Builder是輔助類,用于生產對象
- Response即OkHttp中的響應,響應中可以得到返回是否成功,返回數據
- MediaType 數據類型,用來表明是JSON等一系列格式
- RequestBody 請求數據,在Post請求中用到
- client.newCall(request).execute()是同步的請求方法
- client.newCall(request).enqueue(Callback callBack)是異步的請求方法,但是Callback里面的代碼是執行在子線程的,因此不能更新UI。
3、OKHttp的基本使用步驟(以POST方式從服務器取JSON數據為例)
- 創建OkHttpClient對象,官方文檔要求我們***使用單例模式,在后文對OKHttp進行封裝的時候會提到。
- 如果是post請求的話,需要通過FormEncodingBuilder創建RequestBody對象,指定需要post傳進去的參數。get請求則不用。
- 創建Request對象,這個對象是請求對象,需要指定URL。post請求的時候需要指定RequestBody對象,get請求則不用。
- 調用OkHttpClient的newCall方法,把Request對象傳進去,然后執行execute或者enqueue方法,兩者的區別在上文已提到。在CallBack中的onResponse方法就可以做你需要做的事。onResponse回調的參數是response,一般情況下,比如我們希望獲得返回的字符串,可以通過response.body().string()獲取;如果希望獲得返回的二進制字節數組,則調用response.body().bytes();如果你想拿到返回的inputStream,則調用response.body().byteStream()看到這,你可能會奇怪,竟然還能拿到返回的inputStream,看到這個最起碼能意識到一點,這里支持大文件下載,有inputStream我們就可以通過IO的方式寫文件。不過也說明一個問題,這個onResponse執行的線程并不是UI線程。的確是的,如果你希望操作控件,還是需要使用handler等。
- OkHttpClient client = new OkHttpClient();
- RequestBody body = new FormEncodingBuilder()
- .add("type", "1")
- .build(); Request request = new Request.Builder()
- .url(Constants.URL_BANNER)
- .post(body)
- .build();
- client.newCall(request).enqueue(new Callback() {
- @Override
- public void onFailure(Request request, IOException e) {
- }
- @Override
- public void onResponse(Response response) throws IOException {
- if (response.isSuccessful()) {
- android.os.Message msg = new Message();
- msg.what = 1;
- msg.obj = response.body().string();
- mHandler.sendMessage(msg);
- }
- }
- });
這里只是一個簡單post請求,從服務器獲取數據的介紹,至于get請求只不過是去掉RequestBody對象而已。至于如何向服務器提交數據,可以在熟悉上面的基本用法之后查閱官方的WIKI(GitHub的介紹文檔)。
OKHttp的簡單封裝
回顧上面的代碼,試想一下如果每次請求都寫這么多重復代碼,這樣會嚴重降低開發效率,因此需要對OKHttp進行封裝。對代碼進行封裝是我們最為面向對象程序員的基本素養,減少重復代碼,降低維護難度以及成本。
GitHub上也有對OKHttp進行過封裝,叫做OKHttpUtils。不過這里我們自己動手,一起來學習一下如何進行封裝。具體的注意點有下面幾點:
1、首先,OKHttp官方要求我們***用單例模式去使用OKHttpClient類的,因此我們自定義一個OKHttpHelper類,并且使用單例模式。
2、對get以及post方法進行封裝,主要的思想是把共同的代碼抽取出來,例如代碼中被抽取出來的request方法。
3、對外公開一些靜態方法,包括get和post方法等。
4、Callback基類,對OKHttp的回調進行封裝。這個類用里面有一個type,是方便回調中使用Gson對JSON進行解析的封裝。使用Callback的時候只需要在泛型中傳入類似Data 、List<Data>即可以方便地使用JSON。
5、由于原來的回調不在主線程,因此我們需要使用Handler來將回調放入主線程。
其余的可以參照代碼,有詳細注釋。
- /**
- * 這個類用來輔助OKHttp
- */public class OkHttpHelper { /**
- * 采用單例模式使用OkHttpClient
- */
- private static OkHttpHelper mOkHttpHelperInstance;
- private static OkHttpClient mClientInstance;
- private Handler mHandler;
- private Gson mGson; /**
- * 單例模式,私有構造函數,構造函數里面進行一些初始化
- */
- private OkHttpHelper() {
- mClientInstance = new OkHttpClient();
- mClientInstance.setConnectTimeout(10, TimeUnit.SECONDS);
- mClientInstance.setReadTimeout(10, TimeUnit.SECONDS);
- mClientInstance.setWriteTimeout(30, TimeUnit.SECONDS);
- mGson = new Gson();
- mHandler = new Handler(Looper.getMainLooper());
- } /**
- * 獲取實例
- *
- * @return
- */
- public static OkHttpHelper getinstance() {
- if (mOkHttpHelperInstance == null) {
- synchronized (OkHttpHelper.class) {
- if (mOkHttpHelperInstance == null) {
- mOkHttpHelperInstance = new OkHttpHelper();
- }
- }
- } return mOkHttpHelperInstance;
- } /**
- * 封裝一個request方法,不管post或者get方法中都會用到
- */
- public void request(final Request request, final BaseCallback callback) { //在請求之前所做的事,比如彈出對話框等
- callback.onRequestBefore();
- mClientInstance.newCall(request).enqueue(new Callback() {
- @Override
- public void onFailure(Request request, IOException e) { //返回失敗
- callbackFailure(request, callback, e);
- } @Override
- public void onResponse(Response response) throws IOException {
- if (response.isSuccessful()) { //返回成功回調
- String resString = response.body().string();
- if (callback.mType == String.class) { //如果我們需要返回String類型
- callbackSuccess(response, resString, callback);
- } else { //如果返回的是其他類型,則利用Gson去解析
- try {
- Object o = mGson.fromJson(resString, callback.mType);
- callbackSuccess(response, o, callback);
- } catch (JsonParseException e) {
- e.printStackTrace();
- callbackError(response, callback, e);
- }
- }
- } else { //返回錯誤
- callbackError(response, callback, null);
- }
- }
- });
- } /**
- * 在主線程中執行的回調
- *
- * @param response
- * @param resString
- * @param callback
- */
- private void callbackSuccess(final Response response, final Object o, final BaseCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onSuccess(response, o);
- }
- });
- } /**
- * 在主線程中執行的回調
- * @param response
- * @param callback
- * @param e
- */
- private void callbackError(final Response response, final BaseCallback callback, final Exception e) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onError(response, response.code(), e);
- }
- });
- } /**
- * 在主線程中執行的回調
- * @param request
- * @param callback
- * @param e
- */
- private void callbackFailure(final Request request, final BaseCallback callback, final Exception e) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onFailure(request, e);
- }
- });
- } /**
- * 對外公開的get方法
- *
- * @param url
- * @param callback
- */
- public void get(String url, BaseCallback callback) {
- Request request = buildRequest(url, null, HttpMethodType.GET);
- request(request, callback);
- } /**
- * 對外公開的post方法
- *
- * @param url
- * @param params
- * @param callback
- */
- public void post(String url, Map<String, String> params, BaseCallback callback) {
- Request request = buildRequest(url, params, HttpMethodType.POST);
- request(request, callback);
- } /**
- * 構建請求對象
- *
- * @param url
- * @param params
- * @param type
- * @return
- */
- private Request buildRequest(String url, Map<String, String> 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 RequestBody buildRequestBody(Map<String, String> params) {
- FormEncodingBuilder builder = new FormEncodingBuilder();
- if (params != null) {
- for (Map.Entry<String, String> entity : params.entrySet()) {
- builder.add(entity.getKey(), entity.getValue());
- }
- } return builder.build();
- } /**
- * 這個枚舉用于指明是哪一種提交方式
- */
- enum HttpMethodType {
- GET,
- POST
- }
- }
回調的封裝
- package com.nan.cnshop.http;
- import com.google.gson.internal.$Gson$Types;
- import com.squareup.okhttp.Request;
- import com.squareup.okhttp.Response;
- import java.lang.reflect.ParameterizedType;
- import java.lang.reflect.Type;/**
- * 基本的回調
- */public abstract class BaseCallback<T> { /**
- * type用于方便JSON的解析
- */
- public Type mType; /**
- * 把type轉換成對應的類,這里不用看明白也行。
- *
- * @param subclass
- * @return
- */
- static Type getSuperclassTypeParameter(Class<?> subclass) {
- Type superclass = subclass.getGenericSuperclass();
- if (superclass instanceof Class) {
- throw new RuntimeException("Missing type parameter.");
- }
- ParameterizedType parameterized = (ParameterizedType) superclass;
- return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
- } /**
- * 構造的時候獲得type的class
- */
- public BaseCallback() {
- mType = getSuperclassTypeParameter(getClass());
- } /**
- * 請求之前調用
- */
- public abstract void onRequestBefore();
- /**
- * 請求失敗調用(網絡問題)
- *
- * @param request
- * @param e
- */
- public abstract void onFailure(Request request, Exception e);
- /**
- * 請求成功而且沒有錯誤的時候調用
- *
- * @param response
- * @param t
- */
- public abstract void onSuccess(Response response, T t);
- /**
- * 請求成功但是有錯誤的時候調用,例如Gson解析錯誤等
- *
- * @param response
- * @param errorCode
- * @param e
- */
- public abstract void onError(Response response, int errorCode, Exception e);
- }
OKHttp封裝之后的使用
如下面的代碼所示。首先得到OkHttpHelper的單例,然后調用get方法就可以了。由于繼承了Gson,因此需要在BaseCallback的泛型中傳入JSON對應的數據類型,筆者這里是List<Banner>。***在onSuccess方法中做我們想要做的事情就可以了。
- mHttpHelper=OkHttpHelper.getinstance();
- mHttpHelper.get(Constants.URL_BANNER, new BaseCallback<List<Banner>>() {
- @Override
- public void onRequestBefore() {
- } @Override
- public void onFailure(Request request, Exception e) {
- } @Override
- public void onSuccess(Response response, List<Banner> banners) {
- initBanners(banners);
- } @Override
- public void onError(Response response, int errorCode, Exception e) {
- }
- });
是不是覺得封裝之后OKHttp的使用變得很簡單呢,這就是封裝的強大之處,好了今天的筆記到此為止。
PS:這里只介紹了OKHttp的get和post使用介紹,其余的使用例如文件下載上傳以及對應的代碼封裝請自己去完成~(≧▽≦)/~啦啦啦。全文還有 BaseCallback 和 OkHttpHelper 的代碼,歡迎閱讀原文查看。