OkHttp使用详解

  OkHttp特点:

Okhttp是一个支持HTTP 和 HTTP/2 的客户端,可以在Android和Java应用程序中使用,其具有以下特点: 

1. API设计轻巧,基本上通过几行代码的链式调用即可获取结果。 
2. 既支持同步请求,也支持异步请求。同步请求会阻塞当前线程,异步请求不会阻塞当前线程,异步执行完成后执行相应的回调方法。 
3. 其支持HTTP/2协议,通过HTTP/2,可以让客户端中到同一服务器的所有请求共用同一个Socket连接。 
4. 如果请求不支持HTTP/2协议,那么Okhttp会在内部维护一个连接池, 通过该连接池,可以对HTTP/1.x的连接进行重用,减少了延迟。 
5. 透明的GZIP处理降低了下载数据的大小。 
6. 请求的数据会进行相应的缓存处理,下次再进行请求时,如果服务器告知304(表明数据没有发生变化),那么就直接从缓存中读取数据,降低了重复请求的数量。

OkHttp API文档:http://square.github.io/okhttp/3.x/okhttp/

引入依赖

         compile'com.squareup.okhttp3:okhttp:3.2.0'

 

基本使用

         以最新的okhttp3为例。

(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" //请求头中加入参数
             .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);
     }
}



 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值