本篇記錄的是Android開發中OkHttp框架的使用。OkHttp是Java中用於網絡請求的一個庫,主頁爲:http://square.github.io/okhttp/。 github地址爲:https://github.com/square/okhttpphp
如下介紹OkHttp庫的使用方法,本篇會給出OkHttp的使用demo。demo中包括了常常使用的get請求、post請求、文件的上傳和下載,demo執行的效果例如如下圖所看到的:java
如下上代碼一一說明:android
要使用OkHttp,必須在項目中先導入OkHttp。在app模塊的build.gradle文件裏,增長如下的代碼:git
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請求github
最簡單的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是請求後的回調接口。代碼例如如下:app
//請求後的回調接口 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請求,使用方法例如如下:ide
//簡單的帶參數和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(); } //點擊button開始上傳文件 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就可以了。
參考:http://blog.csdn.net/lmj623565791/article/details/47911083