1. 程式人生 > >你必須學會的okhttp——入門篇

你必須學會的okhttp——入門篇

早在畢業那段期間,群裡有很多小夥伴在問關於okhttp的問題,當時因為不瞭解。所以沒有回答的上。記得十月份有次面試,一個面試官問我關於網路請求的東西時,我記得當時我是說。我是通過HttpClient封裝了一個網路請求的工具類。當然,或許他想問的是我關於okhttp有沒有了解把。谷歌在6.0中刪除了關於httpclient的API。(其實我有httpclient原始碼)。於是乎,為了瞭解下,最近還是學習了下。

簡單說下學習okhttp的理由

  1. google在Android 6.0中刪除了關於Httpclient的APi,採用的則是okhttp
  2. 高效的使用http,使應用執行更快,更省流量
  3. 響應快取資料,避免重複網路請求
  4. 無縫的支援GZIP從而來減少流量的使用
  5. 使用簡單方便,請求和響應的APi具有流暢的建造和不變性。同時支援同步非同步呼叫回撥函式
  6. 如果網路出現問題,他會從常見的連線問題中恢復
  7. 如果伺服器配置多個URL,當第一個連線失敗時,它會嘗試連結下一個

配置環境

github地址:http://github.com/square/okhttp

在build.gradle新增:

compile 'com.squareup.okhttp:okhttp:2.7.5'
compile 'com.squareup.okio:okio:1.11.0'

jar下載地址:

基本使用教程

1) OkHttpClient:新建一個OkHttpClient例項,用於處理請求。

2) Request:構建請求引數,如url,請求方式,請求引數,header等。

3) Call:生成一個具體請求例項,相當於將請求封裝成了任務;兩種方式:

    ①、call.execute(),非非同步方式,會阻塞執行緒,等待返回結果。

    ②、call.enqueue(Callback),非同步方式。

4) Response:結果響應

HttpGet

同步http請求

private OkHttpClient okHttpClient = new OkHttpClient();
private Response
response; okHttpClient.setConnectTimeout(15, TimeUnit.SECONDS); okHttpClient.setReadTimeout(15, TimeUnit.SECONDS); okHttpClient.setWriteTimeout(15, TimeUnit.SECONDS); Request request = new Request.Builder().url(url).get().build(); try { response = okHttpClient.newCall(request).execute(); if (response.isSuccessful()) { Log.i(TAG, " onResponse() reuslt=" + response.body().string()); } } catch (IOException e) { e.printStackTrace(); } }

非同步http請求

private OkHttpClient okHttpClient = new OkHttpClient();
private Response response;
okHttpClient.setConnectTimeout(15, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(15, TimeUnit.SECONDS);
okHttpClient.setWriteTimeout(15, TimeUnit.SECONDS);
Request request = new Request.Builder().url(url).get().build();
okHttpClient.newCall(request).enqueue(new Callback() {

    public void onFailure(Call call, IOException e) {
        Log.e(TAG, "onFailure() e=" + e);
    }

    public void onResponse(Call call, final Response response) throws IOException {
        Log.i(TAG, " onResponse() reuslt=" + response.body().string());
    }
});

HttpPost

看了上面的簡單的get請求,基本上整個的用法也就掌握了。post和get的用法差不多。

同步的Post請求

okHttpClient.setConnectTimeout(15, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(15, TimeUnit.SECONDS);
okHttpClient.setWriteTimeout(15, TimeUnit.SECONDS);
RequestBody body=new FormEncodingBuilder().add("name","馬雲飛").build();
Request request = new Request.Builder().url(url).post(body).build();
            try {
                response = okHttpClient.newCall(request).execute();
                if (response.isSuccessful()) {
                     Log.i(TAG, " onResponse() reuslt=" + response.body().string());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

非同步的Post請求

okHttpClient.setConnectTimeout(15, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(15, TimeUnit.SECONDS);
okHttpClient.setWriteTimeout(15, TimeUnit.SECONDS);
RequestBody body=new FormEncodingBuilder().add("name","馬雲飛").build();
Request request = new Request.Builder().url(url).post(body).build();
okHttpClient.newCall(request).enqueue(new Callback() {

    public void onFailure(Call call, IOException e) {
        Log.e(TAG, "onFailure() e=" + e);
    }

    public void onResponse(Call call, final Response response) throws IOException {
        Log.i(TAG, " onResponse() reuslt=" + response.body().string());
    }
});

此時,基本的get與post就這麼介紹完了。那麼用到網路請求除了get和post還有什麼呢?對了,就是檔案的上傳與下載。針對群裡一些小夥伴需要得到當前的進度,所以下面會上整體的程式碼,我會在放完程式碼後做出解釋。

檔案上傳

  public String uploadFile(String uploadUrl, File file) {
        if (httpUtils.isConnnected()) {
            RequestBody filebody = createProgressRequestBody(MEDIA_OBJECT_STREAM, file);
            RequestBody body = new MultipartBuilder().type(MultipartBuilder.FORM).addFormDataPart(file.getName(), file.getName(), filebody).build();
            Request request = new Request.Builder()
                    .url(uploadUrl)
                    .post(body)
                    .build();
            Response response = null;
            try {
                response = okHttpClient.newCall(request).execute();
                return response.body().string();
            } catch (IOException e) {

            }
        }
        return null;
    }
  public RequestBody createProgressRequestBody(final MediaType contentType, final File file) {
        return new RequestBody() {
            public MediaType contentType() {
                return contentType;
            }

            public long contentLength() {
                return file.length();
            }

            public void writeTo(BufferedSink sink) throws IOException {
                Source source;
                try {
                    source = Okio.source(file);
                    Buffer buf = new Buffer();
                    long total = contentLength();
                    long current = 0;
                    for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) {
                        sink.write(buf, readCount);
                        current += readCount;
                        if (onProgressStateListener != null) {
                            onProgressStateListener.upload(byteUtils.getSize(current), byteUtils.getSize(total), byteUtils.getByte(current), byteUtils.getByte(total));
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
    }

此時你們會看到我判斷了onProgressStateListener此介面不為空的情況,其實就是如果不需要得到當前的進度,我們就無需實現此介面。如果不需要得到進度,你也可以把上面createProgressRequestBody方法換成這句話:

 filebody=RequestBody.create(MEDIA_OBJECT_STREAM,file);

檔案下載

  Request request = new Request.Builder().url(httpDownloadBean.getUrl()).build();
            okHttpClient.newCall(request).enqueue(new Callback() {
                public void onResponse(Response response) {
                    InputStream inputStream = null;
                    try {
                        download_total = response.body().contentLength();
                        inputStream = response.body().byteStream();
                        if (response.isSuccessful()) {
                            writeSDFromInput(httpDownloadBean.getStoragepath(), httpDownloadBean.getFilepath(), inputStream);
                        } else {
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                public void onFailure(Request arg0, IOException arg1) {
                }
            });

這裡我們需要把inputstream寫入sd卡:

 public File writeSDFromInput(String path, String fileName, InputStream input) {
        File file = null;
        OutputStream output = null;
        try {
            fileSupport.createSDDir(path);
            file = fileSupport.createSDFile(path + fileName);
            output = new FileOutputStream(file);
            byte buffer[] = new byte[1024];
            int length = 0;
            long current = 0;
            while ((length = input.read(buffer)) != -1) {
                current += length;
                if (onProgressStateListener != null) {
                    onProgressStateListener.download(byteUtils.getSize(current), byteUtils.getSize(download_total), byteUtils.getByte(current), byteUtils.getByte(download_total));
                }
                output.write(buffer, 0, length);
            }
            output.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                output.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return file;
    }

這裡也和上傳是一樣的,你需要得到進度的時候實現此介面。

總結

Okhttp預設的配置為我們提供了非常重要實用功能。通過採用上述步驟,你可以增加它的靈活性和內省的能力並提高應用程式的質量。我對比過我用Httpclient和Okhttp的寫的工具類,如果不需要得到進度的話,程式碼量差了一倍之多。從此而知,okhttp的確很好用。