一、简介
OkHttp是由square公司开发,Android中公认最好用的网络请求框架,在接口封装上做的简单易用
它由以下默认特性
- 支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
- 使用连接池减少请求延时
- 透明的GZIP压缩减少响应数据的大小
- 缓存响应内容,避免一些完全重复的请求
出现一般的网络问题时OkHttp 会自动恢复:如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会尝试你配置的其他IP。
二、引入
官网最新版本目前是4.10.x,使用的是Kotlin编写的。这里我使用的是3.14.x版本,使用的Java
引入依赖
implementation 'com.squareup.okhttp3:okhttp:3.14.7'
implementation 'com.squareup.okio:okio:1.17.5'
OkHttp 底层依赖 Okio。它利用 Okio 的 Source 和 Sink 高效读写字节流,其缓冲管理机制能让网络数据处理更稳定,还借助 Okio 高效处理数据,如编码解码、压缩解压缩。
配置权限
除了网络权限外,如果还使用网络请求的缓存功能,那么还要申请读写外存的权限(Android10以后,可能还需要一些额外的适配)
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
三、使用方式
基本步骤如下
- 通过构造者模式构建
OkHttpClient
和Request
对象 - 调用
okHttpClient.newCall(request)
生成Call
对象 Call.enqueue()/Call.execute()
发起请求(异步/同步)
3.1 get请求
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.baidu.com")
.get()
.build();
new Thread(()->{
try {
// 同步请求,要放到子线程
Response response =httpClient.newCall(request).execute();
Log.i(TAG, "okhttp execute response:"+ response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
}
}).start();
execute
方法执行的是同步请求,必须放到子线程,如果想在主线程执行,可以使用异步请求
3.1.1 异步请求
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// response.body() 必须关闭,且只能使用一次,
// 多次使用报 java.lang.IllegalStateException: closed
Log.i(TAG, "okhttp enqueue: onResponse:"+ response.body().string());
response.body().close()
}
});
call.enqueue会异步执行,需要注意的是,两个回调方法onFailure
、onResponse
是执行在子线程的,所以如果想要执行UI
操作,需要使用Handler
切换到UI线程
。
请求回调的两个方法是指 传输层的失败和成功
。onFailure
通常是connection连接失败或读写超时;onResponse
是指成功的从服务器获取到了结果
,但是这个结果的响应码可能是404、500等,也可能就是200(response.code()的取值)。
3.1.2 取消请求
每一个Call只能执行一次。 如果想要取消正在执行的请求,可以使用call.cancel()
,通常在离开页面时要取消执行的请求,来防止内存泄漏。
3.2 POST 请求
对于POST
请求,需要对设置请求体和请求头
OkHttpClient httpClient = new OkHttpClient();
MediaType contentType = MediaType.parse("application/json; charset=utf-8");
String content = "hello!";
// 默认媒体类型 application/x-www-form-urlencoded
RequestBody body = RequestBody.create(contentType, content);
Request request = new Request.Builder()
.url("http://your-api-url-here")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
String responseBody = response.body().string();
System.out.println("Response: " + responseBody);
} else {
System.out.println("Error: " + response.code());
}
} catch (Exception e) {
e.printStackTrace();
}
3.2.1 post请求提交String、文件
传入RequestBody
的 MediaType 还可以是其他类型,如客户端要给后台发送json字符串、发送一张图片,那么可以定义为:
// RequestBody:jsonBody,json字符串
String json = "jsonString";
RequestBody jsonBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
//RequestBody:fileBody, 上传文件
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOAD);
File file = new File(path, "1.png");
RequestBody fileBody = RequestBody.create(MediaType.parse("image/png"), file);
3.2.2 post请求提交表单
OK HTTP还提供了FormBody
用于提交表单键值对
//RequestBody:FormBody,表单键值对
RequestBody formBody = new FormBody.Builder()
.add("username", "lyz")
.add("password", "xml")
.build();
3.2.3 post请求提交复杂请求体
在实际业务中,一个post请求往往需要包含string、文件、表单等多种类型的数据,如注册:用户填写完姓名、电话,同时要上传头像图片,这时就要用到MultipartBody
了。
OkHttpClient httpClient = new OkHttpClient();
//RequestBody:fileBody,上传文件
File file = drawableToFile(this, R.mipmap.bigpic, new File("00.jpg"));
RequestBody fileBody = RequestBody.create(MediaType.parse("image/jpg"), file);
//RequestBody:multipartBody, 多类型 (用户名、密码、头像)
MultipartBody multipartBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("username", "lyz")
.addFormDataPart("phone", "123456")
.addFormDataPart("touxiang", "tx.png", fileBody)
.build();
Request req = new Request.Builder()
.url("/your-api-url-here")
.post(multipartBody)
.build();
Call call = httpClient.newCall(req);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i(TAG, "okHttpPost enqueue: \n onFailure:"+ call.request().toString() +"\n body:" +call.request().body().contentType()
+"\n IOException:"+e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG, "okHttpPost enqueue: \n onResponse:"+ response.toString() +"\n body:" +response.body().string());
}
});
3.3 请求配置项
配置分为全局配置和单请求配置
- 在构建的
OkHttpClient
实例上设置的配置,会应用在该实例发送的所有请求上
通常OkHttpClient实例是全局唯一的,这样这些基本配置就是统一,
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS) //
.cache(new Cache(getExternalCacheDir(), 500 * 1024 * 1024)) // 缓存位置,最大缓存大小
.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
Log.i(TAG, "intercept: proceed start: url"+ url+ ", at "+System.currentTimeMillis());
Response response = chain.proceed(request);
ResponseBody body = response.body();
Log.i(TAG, "intercept: proceed end: url"+ url+ ", at "+System.currentTimeMillis());
return response;
}
})
.build();
—
- 单个请求配置
Request getRequest = new Request.Builder()
.url("http://yun918.cn/study/public/file_upload.php")
.post(multipartBody)
.addHeader("key","value") // 添加请求头
.cacheControl(CacheControl.FORCE_NETWORK) // 设置此次请求是能使用网络,不用缓存。
.build();
以上,我们已经学会了OkHttp的基本使用了
参考
胡飞洋大神的 网络请求框架OkHttp3全解系列