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为例。
最简单的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);
}
|
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
);
}
};
|
(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);
}
|
如果我们的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);
}
|
(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);
}
}
|
(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;
|