一、okhttp介绍
OKHttp优点
- 支持HTTP2/SPDY(SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。)
- socket自动选择最好路线,并支持自动重连,拥有自动维护的socket连接池,减少握手次数,减少了请求延迟,共享Socket,减少对服务器的请求次数。
- 基于Headers的缓存策略减少重复的网络请求。
- 拥有Interceptors轻松处理请求与响应(自动处理GZip压缩)。
OKHttp的功能
- OkHttpClient基本参数配置介绍
- 普通GET请求(同步/异步)
- 普通POST请求(同步/异步)
- 根据tag取消请求
- POST请求提交String
- POST请求提交流
- POST请求提交JSON(实体转JSON)
- POST请求提交普通Form表单
- POST请求提交混合Form表单(文本参数+文件)
- POST请求提交单/多文件(带进度条)
- GET请求下载文件(带进度条)
一、OkHttpClient基本参数介绍
kHttpClient.Builder builder = new OkHttpClient.Builder()
.readTimeout(HTTP_TIME_OUT, TimeUnit.SECONDS)
.writeTimeout(HTTP_TIME_OUT, TimeUnit.SECONDS)
.connectTimeout(HTTP_TIME_OUT, TimeUnit.SECONDS)
);
OkHttpClient okHttpClient = okHttpClient = builder.build();
二、如何提高效率
OkHttpClient的实例有一个就可以了。为什么用一个实例呢?
因为每个OkHttpClient实例都有自己的连接池和线程池,用同一个实例的话就能更好的复用线程池和链接池,能够减少延迟和减小内存的消耗。
三、普通GET的同步与异步请求
String url="http://xxx.xxx.xxx.xxx/test.php";
//创建请求体
Request request=new Request.Builder()
.url(url)
.get() //get请求
.addHeader("name","value")//请求头参数
.tag("connect1")//为当前request请求增加tag,可在okHttpClient中使用tag获取到当前请求
.build();
final Call call=OkhttpUtils.okHttpClient().newCall(request);
//同步请求在安卓中要在分线程的
new Thread(new Runnable() {
@Override
public void run() {
try {
Response response=call.execute();
Log.e(TAG, "run: "+response.body().string() );
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
//异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if(response.code()==200){
String result=response.body().string();
Log.e(TAG, "onResponse: "+result );
}else{
Log.e(TAG, "onResponse: "+response.message() );;
}
}
});
}
注意:
1、Call对象作为请求执行者,可以取消请求,同时Call请求只能执行一次;
2、Response作为响应体,获取返回的string使用response.body().string(),获取返回的字节使用response.body().bytes(),获取返回的字节流(如下载文件)使用response.body().byteStream();
四、普通POST请求(同步/异步)
String url="http://xxx.xxx.xxx.xxx/test.php";
String str="我一会就会被发送到服务器端了";
//create()有四个重载方法,支持,文本,文件,流等的传输
RequestBody body=RequestBody.create(MediaType.parse("text/html; charset=utf-8"),str);
Request request=new Request.Builder()
.url(url)
.post(body)
.build();
final Call call=OkhttpUtils.okHttpClient().newCall(request);
//同步
new Thread(new Runnable() {
@Override
public void run() {
try {
call.execute().body().string();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
//异步
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
//失败
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//成功
if(response.code()==200){
Log.e(TAG, "onResponse: "+response.body().string());
}
}
});
提示:RequestBody作为请求提的包体,有多种实现形式:FormBody、MultipartBody,作为提交string、file、stream、form的载体;
五、根据tag取消请求
在创建请求Request的时候,如果添加了tag属性,可以通过tag取消请求,下面是具体实现:
/**
* 根据Tag取消请求
*
* @param client OkHttpClient
* @param tag tag
*/
public static void cancelTag(OkHttpClient client, Object tag) {
if (client == null || tag == null) return;
for (Call call : client.dispatcher().queuedCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
for (Call call : client.dispatcher().runningCalls()) {
if (tag.equals(call.request().tag())) {
call.cancel();
}
}
}
/**
* 取消所有请求请求
*
* @param client OkHttpClient
*/
public static void cancelAll(OkHttpClient client) {
if (client == null) return;
for (Call call : client.dispatcher().queuedCalls()) {
call.cancel();
}
for (Call call : client.dispatcher().runningCalls()) {
call.cancel();
}
}
六、其它
注:为省去一些重复的代码量,下面列出的关于提交string、json、file...方法,本文只写到如何创建RequestBody,后续的创建Request、Call、同步异步调用不在重复罗列;
POST请求提交String
// 提交普通字符串
String content = "普通字符串";
RequestBody requestBody = RequestBody.create(MediaType.parse("text/html; charset=utf-8"), content);
POST请求提交流
RequestBody requestBody = new RequestBody() {
@Override
public MediaType contentType() {
return MediaType.parse("application/octet-stream; charset=utf-8");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8("字符串作为流提交");
// 重写此处可以实现文件上传进度检测
}
};
POST请求提交JSON(实体转JSON)
// 提交JSON
String bodyStr = "json";
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), bodyStr);
POST请求提交普通Form表单
FormBody formBody = new FormBody.Builder()
.add("key1", "value1")
.add("key2", "value2")
.add("key3", "value3")
.build();
POST请求提交混合Form表单(文本参数+文件)
File file = new File("filePath");
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream; charset=utf-8"), file);
// 方式一
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("key1", "value1")
.addFormDataPart("key2", "value2")
.addFormDataPart("key3", "value3")
.addFormDataPart("file1", "name1", fileBody)
.build();
// 方式二
FormBody formBody = new FormBody.Builder()
.add("key1", "value1")
.add("key2", "value2")
.add("key3", "value3")
.build();
RequestBody requestBody1 = new MultipartBody.Builder()
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"params\""),
formBody)
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"file\"; filename=\"plans.xml\""),
fileBody)
.build();
POST请求提交单/多文件(带进度条)
1. 提交单文件(不带进度条)
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.txt");
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
// 方式一
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file1", file.getName(), fileBody)
.build();
// 方式二
RequestBody requestBody1 = new MultipartBody.Builder()
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"file1\"; filename=\"" + file.getName() + "\""),
fileBody)
.build();
2. 提交多文件(不带进度条)
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.txt");
RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
RequestBody fileBody2 = RequestBody.create(MediaType.parse("application/octet-stream"), file);
// 方式一
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file1", file.getName(), fileBody)
.addFormDataPart("file2", file.getName(), fileBody2)
.build();
// 方式二
RequestBody requestBody1 = new MultipartBody.Builder()
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"file1\"; filename=\"" + file.getName() + "\""),
fileBody)
.addPart(Headers.of(
"Content-Disposition",
"form-data; name=\"file2\"; filename=\"" + file.getName() + "\""),
fileBody2)
.build();
3. 提交单文件(!!!带进度条!!!)
根据前面提交流的方式,实现RequestBody的writeTo(BufferedSink sink)方法监听文件传输进度:
// 使用方式
RequestBody fileBody = LOkHttp3Utils.createProgressRequestBody(MediaType.parse("application/octet-stream"), file,
new ProgressListener() {
@Override
public void onStart() {
}
@Override
public void onProgress(long total, float progress) {
// progress 显示当前进度
}
@Override
public void onFinish(File file) {
}
@Override
public void onError(Exception e) {
}
});
/**
* 创建文件requestbody,自定义进度监听器
*
* @param mediaType
* @param file
* @param listener
* @return
*/
public static RequestBody createProgressRequestBody(final MediaType mediaType, final File file,
final ProgressListener listener) {
return new RequestBody() {
@Override
public MediaType contentType() {
return mediaType;
}
@Override
public long contentLength() throws IOException {
return file.length();
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
listener.onStart();
Source source;
try {
source = Okio.source(file);
//sink.writeAll(source);
Buffer buf = new Buffer();
Long remaining = contentLength();
for (long readCount; (readCount = source.read(buf, 2048)) != -1; ) {
sink.write(buf, readCount);
listener.onProgress(contentLength(), 1 - (float)(remaining -= readCount) / contentLength());
}
listener.onFinish(file);
} catch (Exception e) {
listener.onError(e);
e.printStackTrace();
}
}
};
}
定义ProgressListener接口作为上传下载进度监听器:
public interface ProgressListener {
void onStart();
void onProgress(long total, float progress);
void onFinish(File file);
void onError(Exception e);
}
GET请求下载文件(带进度条)
此处需要重新实现Callback接口,获取到Response对象中的文件流,进行文件保存操作,如需监听进度,则在文件流读取的时候进行处理;
1. 下载文件(不带进度条)
// 先看使用效果
Request request = new Request.Builder()
.url(url) // 文件地址
.get()
.addHeader("name", "value")
.tag("getFileAsync")
.build();
final Call call = LOkHttp3Utils.okHttpClient().newCall(request);
call.enqueue(new FileNoProgressCallback(Environment.getExternalStorageDirectory().getAbsolutePath(), "test.png") {
@Override
public void onFinish(File file) {
}
@Override
public void onError(Exception e) {
}
});
// 实现Callback接口,进行文件保存操作
public abstract class FileNoProgressCallback implements Callback {
private String destFileDir;
private String destFileName;
public FileNoProgressCallback(String destFileDir, String destFileName) {
this.destFileDir = destFileDir;
this.destFileName = destFileName;
}
@Override
public void onFailure(Call call, IOException e) {
onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
this.saveFile(response);
}
private void saveFile(Response response) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
FileOutputStream fos = null;
try {
is = response.body().byteStream();
final long total = response.body().contentLength();
long sum = 0L;
File dir = new File(this.destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, this.destFileName);
fos = new FileOutputStream(file);
int len = 0;
while ((len = is.read(buf)) != -1) {
sum += (long) len;
fos.write(buf, 0, len);
}
fos.flush();
onFinish(file);
} catch (Exception e) {
onError(e);
} finally {
try {
response.body().close();
if (is != null) {
is.close();
}
} catch (IOException var23) {
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException var22) {
}
}
}
public abstract void onFinish(File file);
public abstract void onError(Exception e);
}
2. 下载文件(!!!带进度条!!!)
// 使用方式
Request request = new Request.Builder()
.url(url) // 文件地址
.get()
.addHeader("name", "value")
.tag("getFileProgressAsync")
.build();
final Call call = LOkHttp3Utils.okHttpClient().newCall(request);
call.enqueue(new FileCallback(Environment.getExternalStorageDirectory().getAbsolutePath(), "test.png") {
@Override
public void onStart() {
}
@Override
public void onProgress(long total, float progress) {
}
@Override
public void onFinish(File file) {
}
@Override
public void onError(Exception e) {
}
});
// FileCallback同样实现了Callback,但是多了对下载进度的监听
public abstract class FileCallback implements Callback, ProgressListener {
private String destFileDir;
private String destFileName;
public FileCallback(String destFileDir, String destFileName) {
this.destFileDir = destFileDir;
this.destFileName = destFileName;
}
@Override
public void onFailure(Call call, IOException e) {
onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
this.saveFile(response);
}
private void saveFile(Response response) throws IOException {
onStart();
InputStream is = null;
byte[] buf = new byte[2048];
FileOutputStream fos = null;
try {
is = response.body().byteStream();
final long total = response.body().contentLength();
long sum = 0L;
File dir = new File(this.destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, this.destFileName);
fos = new FileOutputStream(file);
int len = 0;
while ((len = is.read(buf)) != -1) {
sum += (long) len;
fos.write(buf, 0, len);
onProgress(total, (float) sum * 1.0F / (float) total);
}
fos.flush();
onFinish(file);
} catch (Exception e) {
onError(e);
} finally {
try {
response.body().close();
if (is != null) {
is.close();
}
} catch (IOException var23) {
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException var22) {
}
}
}
}