版权所有,转载请注明出处:linzhiyong https://www.jianshu.com/p/af144d662bfd https://blog.csdn.net/u012527802/article/details/81013772
相关文章
1、OkHttp3入门介绍:https://www.jianshu.com/p/af144d662bfd
2、OkHttp3入门介绍之Cookie持久化:https://www.jianshu.com/p/23b35d403148
OkHttp是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架,本文主要介绍OkHttp3的基本使用方法。
官网:http://square.github.io/okhttp/
Github:https://github.com/square/okhttp
OkHttp3Demo传送门:https://github.com/linzhiyong/OkHttp3Demo
服务端Demo传送门:https://github.com/linzhiyong/SpringMVCDemo
结合自己的项目经验,主要从以下几方面介绍:
- OkHttpClient基本参数配置介绍
- 普通GET请求(同步/异步)
- 普通POST请求(同步/异步)
- 根据tag取消请求
- POST请求提交String
- POST请求提交流
- POST请求提交JSON(实体转JSON)
- POST请求提交普通Form表单
- POST请求提交混合Form表单(文本参数+文件)
- POST请求提交单/多文件(带进度条)
- GET请求下载文件(带进度条)
开发配置
使用AndroidStudio开发,在app的build.gradle文件中增加对okhttp3的依赖:
dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
}
网络请求需要申请网络权限,需要在AndroidManifest.xml配置:
<uses-permission android:name="android.permission.INTERNET" />
OkHttpClient基本参数介绍
OkHttpClient是通过OkHttpClient.Builder来配置参数,基础参数如下:
OkHttpClient.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();
至于其他参数,如Cookie保持、Https证书设置、Interceptor拦截器设置等,会在后续章节中介绍。
注:
1、这里建议在项目中创造一个OkHttpClient实例并重复使用,这是因为每个实例都有它自己的连接池和线程池,重用连接池和线程池可以减少延迟同时节省内存。
2、本例中,我将OkHttpClient实例封装在LOkHttp3Utils中,使用LOkHttp3Utils.okHttpClient()获取。
普通GET请求(同步/异步)
// 创建请求体
Request request = new Request.Builder()
.url(url) // 请求地址
.get() // get请求
.addHeader("name", "value") // 请求头参数
.tag("getSync") // 为当前request请求增加tag,可在okHttpClient中使用tag获取到当前请求
.build();
Call call = LOkHttp3Utils.okHttpClient().newCall(request);
// 同步请求
Response response = call.execute();
// 异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i("", "");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
int code = response.code();
if (code == 200) {
String result = response.body().string();
Log.i("r", result);
}
else {
Log.e("e", response.message());
}
}
});
注:
1、Call对象作为请求执行者,可以取消请求,同时Call请求只能执行一次;
2、Response作为响应体,获取返回的string使用response.body().string(),获取返回的字节使用response.body().bytes(),获取返回的字节流(如下载文件)使用response.body().byteStream();
普通POST请求(同步/异步)
// 创建请求body,MediaType请求包体类型
RequestBody requestBody = RequestBody.create(MediaType.parse("text/html; charset=utf-8"), content);
// 创建请求体
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.addHeader("name", "value")
.tag("postSync")
.build();
Call call = LOkHttp3Utils.okHttpClient().newCall(request);
// 同步请求
Response response = call.execute();
// 异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i("", "");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
int code = response.code();
if (code == 200) {
String result = response.body().string();
Log.i("r", result);
}
else {
Log.e("e", response.message());
}
}
});
注: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) {
}
}
}
}