本篇记录的是Android开发中OkHttp框架的使用,下面介绍OkHttp库的用法,本篇会给出OkHttp的使用demo,demo中包含了常用的get请求、post请求、文件的上传和下载,demo运行的效果如下图所示:
和力记易招商-容灾备份蓝海! 【点击进入】 和力记易诚招省市级总代理,VIP行业代理 容灾备份软件 数据备份 灾备一体机 虚拟机 查 看 和力记易招商-容灾备份蓝海! 【点击进入】 和力记易诚招省市级总代理,VIP行业代理 容灾备份软件 数据备份 灾备一体机 虚拟机 查 看 和力记易招商-容灾备份蓝海! 【点击进入】 和力记易诚招省市级总代理,VIP行业代理 容灾备份软件 数据备份 灾备一体机 虚拟机 查 看 和力记易招商-容灾备份蓝海! 【点击进入】 和力记易诚招省市级总代理,VIP行业代理 容灾备份软件 数据备份 灾备一体机 虚拟机 查 看
下面上代码一一说明:
要使用OkHttp,必须在项目中先导入OkHttp,在app模块的build.gradle文件中,加入下面的代码:
? 1 2 3 4 5 6 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请求用法如下:
? 1 2 3 4 5 6 7 8 //简单的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头和参数,可以用下面的方式:
? 1 2 3 4 5 6 7 8 9 //带参数的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是请求后的回调接口,代码如下:
? 1 2 3 4 5 6 7 8 9 10 11 12 //请求后的回调接口 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请求,用法如下:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 //简单的带参数和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请求稍微复杂点,比如携带的参数既有文本类型的,又有文件类型的,那么可以用下面的方式来请求:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //带文本参数和文件参数的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)文件的上传
文件上传并显示进度,这个代码稍微有些复杂,下面直接上代码:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 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了,下面上代码:
? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 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就可以了。