Android高級開發之【OkHttp】詳解(附項目源碼)

文章大綱

1、OkHttp簡介
2、OkHttp簡單使用
3、OkHttp封裝
4、項目源碼下載javascript

1、OkHttp簡介

1. 什麼是OkHttp

通常在Java平臺上,咱們會使用Apache HttpClient做爲Http客戶端,用於發送 HTTP 請求,並對響應進行處理。好比可使用http客戶端與第三方服務(如SSO服務)進行集成,固然還能夠爬取網上的數據等。OKHttp與HttpClient相似,也是一個Http客戶端,提供了對 HTTP/2 和 SPDY 的支持,並提供了鏈接池,GZIP 壓縮和 HTTP 響應緩存功能。java

2. OkHttp優勢

(1)支持HTTP2/SPDY(SPDY是Google開發的基於TCP的傳輸層協議,用以最小化網絡延遲,提高網絡速度,優化用戶的網絡使用體驗)
(2)socket自動選擇最好路線,並支持自動重連,擁有自動維護的socket鏈接池,減小握手次數,減小了請求延遲,共享Socket,減小對服務器的請求次數
(3)基於Headers的緩存策略減小重複的網絡請求
(4)擁有Interceptors輕鬆處理請求與響應(自動處理GZip壓縮)面試

3. OkHttp功能

(1)通常的get請求
(2)通常的post請求
(3)基於Http的文件上傳
(4)文件下載
(5)上傳下載的進度回調
(6)加載圖片
(7)支持請求回調,直接返回對象、對象集合
(8)支持session的保持
(9)支持自簽名網站https的訪問,提供方法設置下證書就行
(10)支持取消某個請求

複製代碼

3. OkHttp使用步驟

(1)get請求的步驟,首先構造一個Request對象,參數最起碼有個url,固然你能夠經過Request.Builder設置更多的參數好比:header、method等。
(2)而後經過request的對象去構造獲得一個Call對象,相似於將你的請求封裝成了任務,既然是任務,就會有execute()和cancel()等方法。
(3)最後,咱們但願以異步的方式去執行請求,因此咱們調用的是call.enqueue,將call加入調度隊列,而後等待任務執行完成,咱們在Callback中便可獲得結果。
(4)onResponse回調的參數是response,通常狀況下,好比咱們但願得到返回的字符串,
能夠經過response.body().string()獲取;若是但願得到返回的二進制字節數組,則調用response.body().bytes();若是你想拿到返回的inputStream,則調用response.body().byteStream()
(5)看到這,你可能會奇怪,居然還能拿到返回的inputStream,看到這個最起碼能意識到一點,這裏支持大文件下載,有inputStream咱們就能夠經過IO的方式寫文件。不過也說明一個問題,這個onResponse執行的線程並非UI線程。的確是的,若是你但願操做控件,仍是須要使用handler等
(6)okHttp還支持GJson的處理方式
(7)okhttp支持同步請求和異步請求,Call call = client.newCall(request);爲同步請求,發送請求後,就會進入阻塞狀態,知道收到響應call.enqueue(new Callback()爲異步請求
(8)在okhttp3.Callback的回調方法裏面有個參數是Call 這個call能夠單獨取消相應的請求,隨便在onFailure或者onResponse方法內部執行call.cancel()均可以。若是想取消全部的請求,則能夠okhttpclient.dispatcher().cancelAll();編程

2、OkHttp簡單使用

1. 進行get請求

/** * 原始的get請求 * * @author 吳曉暢 * */
public class OkHttpGet {

    public void get() {

         //1.okhttpClient對象
        OkHttpClient okHttpClient = new OkHttpClient.Builder().
                //在這裏,還能夠設置數據緩存等
                //設置超時時間
                connectTimeout(15, TimeUnit.SECONDS).
                readTimeout(20, TimeUnit.SECONDS).
                writeTimeout(20,  TimeUnit.SECONDS).
                //錯誤重連 
                retryOnConnectionFailure(true).
                build();

        //2構造Request,
        //builder.get()表明的是get請求,url方法裏面放的參數是一個網絡地址
        Request.Builder builder = new Request.Builder();

        Request request = builder.get().url("http://www.baidu.com/").build();

        //3將Request封裝成call
        Call call = okHttpClient.newCall(request);

        //4,執行call,這個方法是異步請求數據
        call.enqueue(new Callback() {

            @Override
            public void onFailure(Call arg0, IOException arg1) {

                //失敗調用
            }

            @Override
            //因爲OkHttp在解析response的時候依靠的是response頭信息當中的Content-Type字段來判斷解碼方式
            //OkHttp會使用默認的UTF-8編碼方式來解碼
            //這裏使用的是異步加載,若是須要使用控件,則在主線程中調用
            public void onResponse(Call arg0, Response arg1) throws IOException {

                 //成功調用

            }
        });

    }
}

複製代碼

2. 進行post請求

/** * 使用okhttp進行post請求 * * @author 吳曉暢 * */
public class OkHttpPost {

    public void initPost() {

        //1.okhttpClient對象
        OkHttpClient okHttpClient = new OkHttpClient.Builder().
                //在這裏,還能夠設置數據緩存等
                //設置超時時間
                connectTimeout(15, TimeUnit.SECONDS).
                readTimeout(20, TimeUnit.SECONDS).
                writeTimeout(20,  TimeUnit.SECONDS).
                //錯誤重連 
                retryOnConnectionFailure(true).
                build();

         RequestBody requestBodyPost = new FormBody.Builder()
         .add("page", "1")
         .add("code", "news")
         .add("pageSize", "20")
         .add("parentid", "0")
         .add("type", "1")
         .build();

         Request requestPost = new Request.Builder()
         .url("www.baidu.com")
         .post(requestBodyPost)
         .build();

         okHttpClient.newCall(requestPost).enqueue(new Callback() {

            @Override
            public void onFailure(Call arg0, IOException arg1) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onResponse(Call arg0, Response arg1) throws IOException {

                //okHttp還支持GJson的處理方式
                //在這裏能夠進行List<bean>和bean處理

            }

         });

 }

}

複製代碼

3. 進行圖片上傳和下載

/** * 使用OkHttp進行圖片上傳和下載 * * @author 吳曉暢 * */
public class OkHttpPicture {

    public void getPicture() {

         //1.建立一個okhttpclient對象 
         OkHttpClient okHttpClient = new OkHttpClient();  

         //2.建立Request.Builder對象,設置參數,請求方式若是是Get,就不用設置,默認就是Get 
         Request request = new Request.Builder()  
                .url("www.baidu.com")  
                .build();  

         //3.建立一個Call對象,參數是request對象,發送請求 
         Call call = okHttpClient.newCall(request);  

         //4.異步請求,請求加入調度 
         call.enqueue(new Callback() {

            @Override
            public void onFailure(Call arg0, IOException arg1) {
                // TODO Auto-generated method stub

            }

            @Override
            public void onResponse(Call arg0, Response arg1) throws IOException {

// //獲得從網上獲取資源,轉換成咱們想要的類型 
// byte[] Picture_bt = response.body().bytes(); 
// //經過handler更新UI 
// Message message = handler.obtainMessage(); 
// message.obj = Picture_bt; 
// message.what = SUCCESS; 
// handler.sendMessage(message); 

            } 

        });  

    }

    public void shangChuanPicture() {

        OkHttpClient mOkHttpClent = new OkHttpClient();

        //獲取sd卡中的文件
        File file = new File(Environment.getExternalStorageDirectory()+"/HeadPortrait.jpg");

        MultipartBody.Builder builder = new MultipartBody.Builder()
                //設置類型
                .setType(MultipartBody.FORM)
                //設置正文內容
                .addFormDataPart("img", "HeadPortrait.jpg",
                        RequestBody.create(MediaType.parse("image/png"), file));

        RequestBody requestBody = builder.build();

        Request request = new Request.Builder()
                .url("www.baidu.com")
                .post(requestBody)
                .build();

        Call call = mOkHttpClent.newCall(request);

    }
}

複製代碼

3. 攔截器使用

什麼是攔截器
  首先咱們須要瞭解什麼事攔截器。打個比方,鏢局押着一箱元寶在行走在一個山間小路上,忽然從山上下來一羣山賊攔住了鏢局的去路,將鏢局身上值錢的東西搜刮乾淨後將其放行。其中山賊至關於攔截器,鏢局至關於一個正在執行任務的網絡請求,請求中的參數就是鏢局攜帶的元寶。攔截器能夠將網絡請求攜帶的參數進行修改驗證,而後放行。這裏面其實設計了AOP編程的思想(面向切面編程)。
  在介紹攔截器的做用和好處以前,咱們仍是要回到山賊這個角色上,若是讓你作一次山賊,你會在什麼地方埋伏?確定是在鏢局必經之路上埋伏。也就是說,攔截器就是在全部的網絡請求的必經之地上進行攔截。
(1)攔截器能夠一次性對全部的請求和返回值進行修改。
(2)攔截器能夠一次性對請求的參數和返回的結果進行編碼,好比統一設置爲UTF-8.
(3)攔截器能夠對全部的請求作統一的日誌記錄,不須要在每一個請求開始或者結束的位置都添加一個日誌操做。
(4)其餘須要對請求和返回進行統一處理的需求….json

OkHttp中攔截器分類
OkHttp中的攔截器分2個:APP層面的攔截器(Application Interception)、網絡請求層面的攔截器(Network Interception)
(1)Application Interceptor是在請求執行剛開始,尚未執行OkHttp的核心代碼前進行攔截,Application攔截器的做用:
1)不須要擔憂是否影響OKHttp的請求策略和請求速度。
2)即便是從緩存中取數據,也會執行Application攔截器。
3)容許重試,即Chain.proceed()能夠執行屢次。(固然請不要盲目執行屢次,須要加入你的邏輯判斷)
(2)Network Interception是在鏈接網絡以前
1)能夠修改OkHttp框架自動添加的一些屬性(固然最好不要修改)。
2)能夠觀察最終完整的請求參數(也就是最終服務器接收到的請求數據和熟悉)數組

使用注意點
若是對攔截器不是很熟的同窗,開發過程當中,建議使用Application Interception。這樣避免對OkHttp請求策略的破壞。緩存

常見實際場景
(1)對請求參數進行統一加密處理。
(2)攔截不符合規則的URL。
(3)對請求或者返回參數設置統一的編碼方式
(4)其它…。性能優化

代碼實操bash

public class OkHttpLanJieQi {

    /** * 應用攔截器 */
    Interceptor appInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {

            Request request = chain.request();

            //———請求以前要作的事情————
            HttpUrl url = request.url();
            String s = url.url().toString();

            Response response = chain.proceed(request);

            //———請求以後要作事情————
            Log.d("aa","app interceptor:begin");

            return response;

        }

    };

    /** * 網絡攔截器 */
    Interceptor networkInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();

            //———請求以前要作的事情————

            Response  response = chain.proceed(request);

          //———請求以後要作事情————

            return response;
        }
    };

    /** * 進行get請求,並配置攔截器 */
    public void initGet() {

        OkHttpClient okHttpClient = new OkHttpClient
                .Builder()
                .addInterceptor(appInterceptor)//Application攔截器
                .addNetworkInterceptor(networkInterceptor)//Network攔截器
                .build();

        //2構造Request,
        //builder.get()表明的是get請求,url方法裏面放的參數是一個網絡地址
        Request.Builder builder = new Request.Builder();

        Request request = builder.get().url("http://www.baidu.com/").build();

        //3將Request封裝成call
        Call call = okHttpClient.newCall(request);

        //4,執行call,這個方法是異步請求數據
        call.enqueue(new Callback() {

            @Override
            public void onFailure(Call arg0, IOException arg1) {

                //失敗調用
            }

            @Override
            //因爲OkHttp在解析response的時候依靠的是response頭信息當中的Content-Type字段來判斷解碼方式
            //OkHttp會使用默認的UTF-8編碼方式來解碼
            //這裏使用的是異步加載,若是須要使用控件,則在主線程中調用
            public void onResponse(Call arg0, Response arg1) throws IOException {

                 //成功調用

            }
        });

    }

}

複製代碼

3、OkHttp封裝

1. 自行簡單封裝

/** * okhttp操做進行封裝 * * @author 吳曉暢 * */
public class OkHttp {

    public void get(String url, Callback callback) {

        //1.okhttpClient對象
        OkHttpClient okHttpClient = new OkHttpClient.Builder().
                //在這裏,還能夠設置數據緩存等
                //設置超時時間
                connectTimeout(15, TimeUnit.SECONDS).
                readTimeout(20, TimeUnit.SECONDS).
                writeTimeout(20,  TimeUnit.SECONDS).
                addInterceptor(appInterceptor).//Application攔截器
                //錯誤重連 
                retryOnConnectionFailure(true).
                build();

        //2構造Request,
        //builder.get()表明的是get請求,url方法裏面放的參數是一個網絡地址
        Request.Builder builder = new Request.Builder();

        Request request = builder.get().url(url).build();

        //3將Request封裝成call
        Call call = okHttpClient.newCall(request);

        //4,執行call,這個方法是異步請求數據
        call.enqueue(callback);

    }

    public void post(String url, List<String> list, Callback callback, RequestBody requestBody) {

        //1.okhttpClient對象
        OkHttpClient okHttpClient = new OkHttpClient.Builder().
                //在這裏,還能夠設置數據緩存等
                //設置超時時間
                connectTimeout(15, TimeUnit.SECONDS).
                addInterceptor(appInterceptor).//Application攔截器
                readTimeout(20, TimeUnit.SECONDS).
                writeTimeout(20,  TimeUnit.SECONDS).
                //錯誤重連 
                retryOnConnectionFailure(true).
                build();

         RequestBody requestBodyPost = requestBody;

         Request requestPost = new Request.Builder()
         .url(url)
         .post(requestBodyPost)
         .build();

         okHttpClient.newCall(requestPost).enqueue(callback);

    }

    /** * 應用攔截器 */
    Interceptor appInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {

            Request request = chain.request();

            //———請求以前要作的事情————

            Response response = chain.proceed(request);

            //———請求以後要作事情————

            return response;

        }

    };
}

複製代碼

2. Android--OKHttpUtils框架封裝

簡介
  OKHttpUtils:一個專一於讓網絡請求更簡單的網絡請求框架,對於任何形式的網絡請求只須要一行代碼。它是OKHttp的一次二次封裝,封裝的目的是讓網絡請求更加方便。服務器

OKHttpUtils優點
(1)性能高,使用主流的okhttp的進行封裝
  OKHttp咱們知道它支持http2和socket的重連。自動選擇最好的路線,擁有本身維護socket維護的鏈接池。能夠減小TCP的握手次數,同時它擁有隊列線程池能夠輕鬆的併發請求。
(2)特有的網絡緩存模式
  OKHttpUtils是大多數網絡框架不具有的,好比咱們公司的網絡老闆要求不只在有網的狀況下,進行展現網絡數據,在無網的狀況下使用緩存數據。這時候咱們使用普通網絡請求,就須要大量的判斷。當前是否有網和無網狀態,根據不一樣的狀態保存不一樣的數據。而後再決定是否使用緩存。可是這是一個通用的寫法。因而OKHttpUtils使用自動網絡緩存模式。讓用戶只關注數據處理。
(3)方便易用的擴展接口
  能夠添加全局的公共參數、全局的攔截器、全局的超時時間,更能夠對單個請求定製攔截器。請求參數修改等等。
(4)強大的Cookie的保存策略
  在客戶端對Cookie的獲取不是一個特別簡單的事情,Cookie全程自動管理,而且提供了額外的Cookie管理方法,引入額外的自動管理中,添加任何你想建立的Cookie。

依賴包導入

compile 'com.zhy:okhttputils:2.0.0'

複製代碼

進行get請求

private String get(String url) throws IOException {

      Request request = new Request.Builder()

          .url(url)//傳url

          .build();//建立

      //把request傳進client
      //execute()執行線程
      Response response = client.newCall(request).execute();

      return response.body().string();
    }

複製代碼

進行post請求

private 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();
    }

複製代碼

使用okhttp-utils請求單張圖片

public void getImage() {
         tv_result.setText("");
            String url = "http://images.csdn.net/20150817/1.jpg";
            OkHttpUtils
                    .get()//
                    .url(url)//
                    .tag(this)//
                    .build()//
                    .connTimeOut(20000)//連接超時
                    .readTimeOut(20000)//讀取超時
                    .writeTimeOut(20000)//寫入超時
                    .execute(new BitmapCallback()
                    {
                        @Override
                        public void onError(Call call, Exception e, int id) {
                            tv_result.setText("onError:" + e.getMessage());
                        }

                        @Override
                        public void onResponse(Bitmap bitmap, int id) {
                            Log.e("TAG", "onResponse:complete");
                            iv_icon.setImageBitmap(bitmap);
                        }
                    });
        }

複製代碼

使用okhttp-utils上傳多個或者單個文件

/** * 使用okhttp-utils上傳多個或者單個文件 */     
     public void multiFileUpload() {

         //FileUploadServlet
         String mBaseUrl = "http://192.168.3.27:8080/FileUpload/FileUploadServlet";

            File file = new File(Environment.getExternalStorageDirectory(), "tupian.jpg");
            File file2 = new File(Environment.getExternalStorageDirectory(), "zanghao.jpg");
            if (!file.exists())
            {
                Toast.makeText(OKHttpActivity.this, "文件不存在,請修改文件路徑", Toast.LENGTH_SHORT).show();
                return;
            }
// Map<String, String> params = new HashMap<String, String>();
// params.put("username", "黃敏瑩");
// params.put("password", "123");

            String url = mBaseUrl;
            OkHttpUtils.post()//
                    .addFile("mFile", "server_tupian.jpg", file)//
                    .addFile("mFile", "server_zanghao.jpg", file2)//兩個addFile就是多文件上傳,註釋掉一個就是單文件上傳
                    .url(url)
// .params(params)//
                    .build()//
                    .execute(new MyStringCallBack());//回調
        }

複製代碼

回調處理

/** * 用於回調 * @author Mloong * */
    private class MyStringCallBack extends StringCallback{

        @Override
        public void onBefore(Request request, int id) {
            // TODO Auto-generated method stub
            super.onBefore(request, id);

            setTitle("loading...");
        }

        @Override
        public void onAfter(int id) {
            // TODO Auto-generated method stub
            super.onAfter(id);

            setTitle("sample-okhttp");
        }

        //出錯
        @Override
        public void onError(Call arg0, Exception e, int arg2) {

            e.printStackTrace();

            tv_result.setText("onError:"+e.getMessage());

        }

        //成功後回調
        @Override
        public void onResponse(String response, int id) {

            //顯示文本信息
            tv_result.setText("onResponse:"+ response);

            switch (id) {
            case 100:

                Toast.makeText(OKHttpActivity.this, "http", Toast.LENGTH_LONG).show();

                break;

            case 101:

                Toast.makeText(OKHttpActivity.this, "https", Toast.LENGTH_LONG).show();

                break;

            default:
                break;
            }

        }

        @Override
        public void inProgress(float progress, long total, int id) {

            Log.e(TAG, "inProgress:"+progress);

            mProgressBar.setProgress((int) (100*progress));

        }

    }

複製代碼

4、項目源碼下載

連接:pan.baidu.com/s/1f3eZhmfK…
密碼:cv4b

關於okhttp的所有學習內容,咱們這邊都有系統的知識體系以及進階視頻資料,有須要的朋友能夠加羣免費領取安卓進階視頻教程,源碼,面試資料,羣內有大牛一塊兒交流討論技術;點擊連接加入羣聊【騰訊@Android高級架構】:
(包括自定義控件、NDK、架構設計、混合式開發工程師(React native,Weex)、性能優化、完整商業項目開發等)

Android高級進階視頻教程
相關文章
相關標籤/搜索