- Android开发之OkHttp的使用
-
本篇记录的是Android开发中OkHttp框架的使用,下面介绍OkHttp库的用法,本篇会给出OkHttp的使用demo,demo中包含了常用的get请求、post请求、文件的上传和下载,demo运行的效果如下图所示:
下面上代码一一说明:
要使用OkHttp,必须在项目中先导入OkHttp,在app模块的build.gradle文件中,加入下面的代码:
123456dependencies {
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'
}
(1)GET请求
最简单的GET请求用法如下:
12345678//简单的Get请求,不带参数
public
void
simpleGetClick(View view) {
okHttpClient =
new
OkHttpClient();
Request request =
new
Request.Builder()
.build();
okHttpClient.newCall(request).enqueue(callback);
}
123456789//带参数的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是请求后的回调接口,代码如下:
123456789101112//请求后的回调接口
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请求,用法如下:
123456789101112131415//简单的带参数和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()
.post(requestBody)
.addHeader(
"token"
,
"helloworldhelloworldhelloworld"
)
.build();
okHttpClient.newCall(request).enqueue(callback);
}
如果我们的POST请求稍微复杂点,比如携带的参数既有文本类型的,又有文件类型的,那么可以用下面的方式来请求:
1234567891011121314151617//带文本参数和文件参数的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()
.post(requestBody)
.addHeader(
"token"
,
"helloworldhelloworldhelloworld"
)
.build();
okHttpClient.newCall(request).enqueue(callback);
}
(3)文件的上传
文件上传并显示进度,这个代码稍微有些复杂,下面直接上代码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204package
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()
.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了,下面上代码:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162import
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()
.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);
}
}