Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發之OkHttp的使用

Android開發之OkHttp的使用

日期:2017/2/23 13:32:20      編輯:關於Android編程

本篇記錄的是Android開發中OkHttp框架的使用,下面介紹OkHttp庫的用法,本篇會給出OkHttp的使用demo,demo中包含了常用的get請求、post請求、文件的上傳和下載,demo運行的效果如下圖所示:

\\\\\

下面上代碼一一說明:

要使用OkHttp,必須在項目中先導入OkHttp,在app模塊的build.gradle文件中,加入下面的代碼:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.squareup.okhttp3:okhttp:3.2.0'
}
這樣就將OkHttp導入到項目中了。

 

(1)GET請求

最簡單的GET請求用法如下:

 

//簡單的Get請求,不帶參數
public void simpleGetClick(View view) {
    okHttpClient = new OkHttpClient();
    Request request = new Request.Builder()
            .url("http://192.168.1.170:8088/okhttp/test_simple_get.php")
            .build();
    okHttpClient.newCall(request).enqueue(callback);
}
如果請求中要添加Header頭和參數,可以用下面的方式:

 

 

//帶參數的Get請求
public void addParamGetClick(View view) {
    okHttpClient = new OkHttpClient();
    Request request = new Request.Builder()
            .addHeader("token", "asdlfjkasdljfaskdjfalsjkljalk")  //請求頭中加入參數
            .url("http://192.168.1.170:8088/okhttp/test_param_get.php?username=zhangsan&phone=13888888888") //攜帶參數
            .build();
    okHttpClient.newCall(request).enqueue(callback);
}

需要注意的是,上面的代碼中,callback是請求後的回調接口,代碼如下:

//請求後的回調接口
private Callback callback = new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        setResult(e.getMessage(), false);
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        setResult(response.body().string(), true);
    }
};
這個回調接口需要注意的是,onResponse和onFailure都不是在UI線程中執行的,所以如果我們要在onResponse或onFailure中進行UI相關的操作,需要在UI線程中進行。

(2)POST請求

比較簡單的POST請求,用法如下:

 

//簡單的帶參數和Header的post請求
public void simplePostClick(View view) {
    okHttpClient = new OkHttpClient();
    RequestBody requestBody = new FormBody.Builder()
            .add("username", "wangwu")
            .add("password", "hello12345")
            .add("gender", "female")
            .build();
    Request request = new Request.Builder()
            .url("http://192.168.1.170:8088/okhttp/test_simple_post.php")
            .post(requestBody)
            .addHeader("token", "helloworldhelloworldhelloworld")
            .build();
    okHttpClient.newCall(request).enqueue(callback);
}
這裡我們需要先構造一個RequestBody,然後把需要攜帶的參數放到RequestBody中,然後使用這個RequestBody構建一個Request請求,最後將這個請求放入隊列中執行

 

如果我們的POST請求稍微復雜點,比如攜帶的參數既有文本類型的,又有文件類型的,那麼可以用下面的方式來請求:

 

//帶文本參數和文件參數的post請求
public void filePostClick(View view) {
    RequestBody fileBody = RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), tempFile);
    RequestBody requestBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("username", "wangwu")
            .addFormDataPart("password", "hello12345")
            .addFormDataPart("gender", "female")
            .addFormDataPart("file", "info.txt", fileBody)
            .build();
    Request request = new Request.Builder()
            .url("http://192.168.1.170:8088/okhttp/test_param_post.php")
            .post(requestBody)
            .addHeader("token", "helloworldhelloworldhelloworld")
            .build();
    okHttpClient.newCall(request).enqueue(callback);
}
上面的代碼中,tempFile是一個文本文件,為了POST提交文件和一些其他的參數,我們使用MultipartBody來構建一個請求體,需要注意的是,因為POST的內容含有文件,所以我們必須為這個請求體設置setType(MultipartBody.FORM)

(3)文件的上傳

文件上傳並顯示進度,這個代碼稍微有些復雜,下面直接上代碼:

package com.test.testokhttp;

import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.ForwardingSink;
import okio.ForwardingSource;
import okio.Okio;
import okio.Sink;
import okio.Source;

public class UploadActivity extends AppCompatActivity {

    private OkHttpClient okHttpClient;
    private TextView resultTextView;
    private ProgressBar progressBar;
    private File tempFile;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_upload);
        setTitle("上傳文件並顯示進度");

        resultTextView = (TextView) findViewById(R.id.result_textview);
        progressBar = (ProgressBar) findViewById(R.id.progress_bar);
        progressBar.setMax(100);

        okHttpClient = new OkHttpClient.Builder()
                .readTimeout(30, TimeUnit.SECONDS)
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .build();
    }

    //點擊按鈕開始上傳文件
    public void startUploadClick(View view) {
        tempFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.pdf");
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", "test.pdf", RequestBody.create(MediaType.parse("application/pdf; charset=utf-8"), tempFile))
                .build();
        ProgressRequestBody progressRequestBody = new ProgressRequestBody(requestBody, progressListener);
        Request request = new Request.Builder()
                .url("http://192.168.1.170:8088/okhttp/test_upload_file.php")
                .post(progressRequestBody)
                .build();
        okHttpClient.newCall(request).enqueue(callback);
    }

    //通過實現進度回調接口中的方法,來顯示進度
    private ProgressListener progressListener = new ProgressListener() {
        @Override
        public void update(long bytesRead, long contentLength, boolean done) {
            int progress = (int) (100.0 * bytesRead / contentLength);
            progressBar.setProgress(progress);
        }
    };

    //請求後的回調方法
    private Callback callback = new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            setResult(e.getMessage(), false);
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            setResult(response.body().string(), true);
        }
    };

    //顯示請求返回的結果
    private void setResult(final String msg, final boolean success) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (success) {
                    Toast.makeText(UploadActivity.this, "請求成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(UploadActivity.this, "請求失敗", Toast.LENGTH_SHORT).show();
                }
                resultTextView.setText(msg);
            }
        });
    }

    //自定義的RequestBody,能夠顯示進度
    public class ProgressRequestBody extends RequestBody {
        //實際的待包裝請求體
        private final RequestBody requestBody;
        //進度回調接口
        private final ProgressListener progressListener;
        //包裝完成的BufferedSink
        private BufferedSink bufferedSink;

        /**
         * 構造函數,賦值
         *
         * @param requestBody      待包裝的請求體
         * @param progressListener 回調接口
         */
        public ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) {
            this.requestBody = requestBody;
            this.progressListener = progressListener;
        }

        /**
         * 重寫調用實際的響應體的contentType
         *
         * @return MediaType
         */
        @Override
        public MediaType contentType() {
            return requestBody.contentType();
        }

        /**
         * 重寫調用實際的響應體的contentLength
         *
         * @return contentLength
         * @throws IOException 異常
         */
        @Override
        public long contentLength() throws IOException {
            return requestBody.contentLength();
        }

        /**
         * 重寫進行寫入
         *
         * @param sink BufferedSink
         * @throws IOException 異常
         */
        @Override
        public void writeTo(BufferedSink sink) throws IOException {
            if (bufferedSink == null) {
                //包裝
                bufferedSink = Okio.buffer(sink(sink));
            }
            //寫入
            requestBody.writeTo(bufferedSink);
            //必須調用flush,否則最後一部分數據可能不會被寫入
            bufferedSink.flush();

        }

        /**
         * 寫入,回調進度接口
         *
         * @param sink Sink
         * @return Sink
         */
        private Sink sink(Sink sink) {
            return new ForwardingSink(sink) {
                //當前寫入字節數
                long bytesWritten = 0L;
                //總字節長度,避免多次調用contentLength()方法
                long contentLength = 0L;

                @Override
                public void write(Buffer source, long byteCount) throws IOException {
                    super.write(source, byteCount);
                    if (contentLength == 0) {
                        //獲得contentLength的值,後續不再調用
                        contentLength = contentLength();
                    }
                    //增加當前寫入的字節數
                    bytesWritten += byteCount;
                    //回調
                    progressListener.update(bytesWritten, contentLength, bytesWritten == contentLength);
                }
            };
        }
    }

    //進度回調接口
    interface ProgressListener {
        void update(long bytesRead, long contentLength, boolean done);
    }

}
上面需要注意的是,上傳文件需要實現自定義的RequestBody,也就是上面的ProgressRequestBody,在ProgressRequestBody中獲取上傳的進度。

(4)文件的下載

下載和上傳類似,區別在於,需要我們實習自定義的ResponseBody而不是RequestBody了,下面上代碼:

import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;

public class DownloadActivity extends AppCompatActivity {

    private OkHttpClient okHttpClient;
    private TextView resultTextView;
    private ProgressBar progressBar;
    private File tempFile;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);
        setTitle("下載文件並顯示進度");

        okHttpClient = new OkHttpClient.Builder()
                .addNetworkInterceptor(new Interceptor() {
                    @Override public Response intercept(Interceptor.Chain chain) throws IOException {
                        Response originalResponse = chain.proceed(chain.request());
                        return originalResponse.newBuilder()
                                .body(new ProgressResponseBody(originalResponse.body(), progressListener))
                                .build();
                    }
                })
                .connectTimeout(5, TimeUnit.SECONDS)
                .readTimeout(300, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();
        resultTextView = (TextView) findViewById(R.id.result_textview);
        progressBar = (ProgressBar) findViewById(R.id.progress_bar);
        tempFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + System.currentTimeMillis() + ".pdf");
    }

    //下載文件
    public void startDownloadClick(View view) {
        Request request = new Request.Builder()
                .url("http://192.168.1.170:8088/okhttp/test.pdf")
                .build();
        okHttpClient.newCall(request).enqueue(callback);
    }

    private ProgressListener progressListener = new ProgressListener() {
        @Override
        public void update(long bytesRead, long contentLength, boolean done) {
            int progress = (int) (100.0 * bytesRead / contentLength);
            progressBar.setProgress(progress);
        }
    };

    //請求後的回調方法
    private Callback callback = new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            setResult(e.getMessage(), false);
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            if(response != null) {
                //下載完成,保存數據到文件
                InputStream is = response.body().byteStream();
                FileOutputStream fos = new FileOutputStream(tempFile);
                byte[] buf = new byte[1024];
                int hasRead = 0;
                while((hasRead = is.read(buf)) > 0) {
                    fos.write(buf, 0, hasRead);
                }
                fos.close();
                is.close();
                setResult("下載成功", true);
            }
        }
    };

    //顯示請求返回的結果
    private void setResult(final String msg, final boolean success) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (success) {
                    Toast.makeText(DownloadActivity.this, "請求成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(DownloadActivity.this, "請求失敗", Toast.LENGTH_SHORT).show();
                }
                resultTextView.setText(msg);
            }
        });
    }
    
    //自定義的ResponseBody,在其中處理進度
    private static class ProgressResponseBody extends ResponseBody {

        private final ResponseBody responseBody;
        private final ProgressListener progressListener;
        private BufferedSource bufferedSource;

        public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
            this.responseBody = responseBody;
            this.progressListener = progressListener;
        }

        @Override public MediaType contentType() {
            return responseBody.contentType();
        }

        @Override public long contentLength() {
            return responseBody.contentLength();
        }

        @Override public BufferedSource source() {
            if (bufferedSource == null) {
                bufferedSource = Okio.buffer(source(responseBody.source()));
            }
            return bufferedSource;
        }

        private Source source(Source source) {
            return new ForwardingSource(source) {
                long totalBytesRead = 0L;

                @Override public long read(Buffer sink, long byteCount) throws IOException {
                    long bytesRead = super.read(sink, byteCount);
                    // read() returns the number of bytes read, or -1 if this source is exhausted.
                    totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                    progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
                    return bytesRead;
                }
            };
        }
    }

    //進度回調接口
    interface ProgressListener {
        void update(long bytesRead, long contentLength, boolean done);
    }
}
如果我們在項目中直接使用上面的代碼來進行http請求的話,勢必會比較麻煩,所以這裡我們需要封裝上面的代碼,盡量在項目中能用簡短的代碼完成網絡請求。另外,一個項目中肯定會有很多個網絡請求,我們沒必要在每次網絡請求中都創建一個OkHttpClient對象,所有的請求公用一個OkHttpClient就可以了。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved