Okhttp3网络请求框架的使用简介
添加Okhttp3的依赖
dependencies {
//Okhttp3 网络请求框架
implementation 'com.squareup.okhttp3:okhttp:3.10.0'
}
在AndroidManifest.xml中声明一下网络权限才行:
<uses-permission android:name="android.permission.INTERNET" />
Okhttp3的使用
1. 异步GET请求
使用步骤:
- new OkHttpClient
- 构造Request对象
- 通过前两步中的对象构建Call对象
- 通过Call.enqueue(Callback)方法来提交异步请求
//请求地址
String url = "http://wwww.bilibili.com";
//创建Okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//超时处理
okHttpClient.setConnectTimeout(10, TimeUnit.SECONDS);
okHttpClient.setWriteTimeout(10, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(30, TimeUnit.SECONDS);
//创建请求对象并设置对应的参数
final Request request = new Request.Builder()
.url(url)
.get()//默认就是GET请求
.build();
//创建相应的回调对象
Call call = okHttpClient.newCall(request);
//开启网络请求,并处理请求的回调
call.enqueue(new Callback() {
@Override
//请求失败的回调
public void onFailure(Call call, IOException e) {
Log.d(TAG, "请求失败");
}
@Override
//请求成功的回调
public void onResponse(Call call, Response response) throws IOException {
//打印响应的结果
Log.d(TAG, "请求成功: " + response.body().string());
}
});
//可以立即停止掉一个正在执行的call
//call.cancel();
注:
异步发起的请求会被加入到 Dispatcher 中的 runningAsyncCalls双端队列中通过线程池来执行
当call没有必要的时候,使用这个api可以节约网络资源。例如当用户离开一个应用时。不管同步还是异步的call都可以取消
2. 同步GET请求
//请求地址
String url = "http://wwww.bilibili.com";
//创建Okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建请求对象并设置对应的参数
final Request request = new Request.Builder()
.url(url)
.build();
//创建相应的回调对象
final Call call = okHttpClient.newCall(request);
//创建一个子线程
new Thread(new Runnable() {
@Override
public void run() {
try {
//创建Response对象接收请求的相应并;开启网络请求
Response response = call.execute();
//打印响应的结果
Log.d(TAG, "run: " + response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
3. POST方式提交String字符串
在构造Request对象时,需要多构造一个RequestBody对象,用它来携带我们要提交的数据。在构造 RequestBody 需要指定MediaType,用于描述请求/响应 body 的内容类型
补充: MediaType 的更多信息可以查看 RFC 2045
//请求地址
String url = "https://api.github.com/markdown/raw";
//创建Okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建MediaType对象
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
//定义要发送个服务端的字符串
String requestBody = "I am Jdqm";
//创建请求对象并设置对应的参数
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(mediaType, requestBody))
.build();
//开启网络请求,并处理请求的回调
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
//请求失败的回调
public void onFailure(Call call, IOException e) {
//进行异常的捕获和处理
}
@Override
//请求成功的回调
public void onResponse(Call call, Response response) throws IOException {
String protocol = response.protocol();//协议
int code =response.code();//响应码
String message = response.message();//信息
Headers headers = response.headers();//响应头
String = response.body().string();//响应体
}
});
4. POST方式提交 " 流 "
//请求地址
String url = "https://api.github.com/markdown/raw";
//创建Okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建请求体对象
RequestBody requestBody = new RequestBody() {
@Nullable
@Override
public MediaType contentType() {
return MediaType.parse("text/x-markdown; charset=utf-8");
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
//请求携带的数据流
sink.writeUtf8("I am Jdqm.");
}
};
//创建请求对象并设置对应的参数
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
//开启网络请求,并处理请求的回调
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
//请求失败的回调
public void onFailure(Call call, IOException e) {
//进行异常的捕获和处理
}
@Override
//请求成功的回调
public void onResponse(Call call, Response response) throws IOException {
String protocol = response.protocol();//协议
int code =response.code();//响应码
String message = response.message();//信息
Headers headers = response.headers();//响应头
String = response.body().string();//响应体
}
});
5. POST提交文件
//请求地址
String url = "https://api.github.com/markdown/raw";
//创建MediaType对象
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
//创建Okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建文件对象
File file = new File("test.md");
//创建请求对象并设置对应的参数
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(mediaType, file))
.build();
//开启网络请求,并处理请求的回调
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
//请求失败的回调
public void onFailure(Call call, IOException e) {
//进行异常的捕获和处理
}
@Override
//请求成功的回调
public void onResponse(Call call, Response response) throws IOException {
String protocol = response.protocol();//协议
int code =response.code();//响应码
String message = response.message();//信息
Headers headers = response.headers();//响应头
String = response.body().string();//响应体
}
});
6. POST方式提交表单
提交表单时,使用 RequestBody 的实现类FormBody来描述请求体,它可以携带一些经过编码的 key-value 请求体,
键值对存储在下面两个集合中:
- private final List encodedNames;
- private final List encodedValues;
//请求地址
String url = "https://en.wikipedia.org/w/index.php";
//创建Okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建请求体对象
RequestBody requestBody = new FormBody.Builder()
.add("search", "Jurassic Park")
.build();
//创建请求对象并设置对应的参数
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
//开启网络请求,并处理请求的回调
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
//请求失败的回调
public void onFailure(Call call, IOException e) {
//进行异常的捕获和处理
}
@Override
//请求成功的回调
public void onResponse(Call call, Response response) throws IOException {
String protocol = response.protocol();//协议
int code =response.code();//响应码
String message = response.message();//信息
Headers headers = response.headers();//响应头
String = response.body().string();//响应体
}
});
7. POST方式提交分块请求
MultipartBody 可以构建复杂的请求体,与HTML文件上传形式兼容。多块请求体中每块请求都是一个请求体,可以定义自己的请求头
//请求地址
String url= "https://api.imgur.com/3/image";
//创建Okhttp客户端对象
OkHttpClient client = new OkHttpClient();
//创建MultipartBody对象
MultipartBody body = new MultipartBody.Builder("AaB03x")
.setType(MultipartBody.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo")
)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png"))
)
.build();
//创建请求对象并设置对应的参数
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url(url)
.post(body)
.build();
//创建相应的回调对象
Call call = client.newCall(request);
//开启网络请求,并处理请求的回调
call.enqueue(new Callback() {
@Override
//请求失败的回调
public void onFailure(Call call, IOException e) {
//进行异常的捕获和处理
}
@Override
//请求成功的回调
public void onResponse(Call call, Response response) throws IOException {
String protocol = response.protocol();//协议
int code =response.code();//响应码
String message = response.message();//信息
Headers headers = response.headers();//响应头
String = response.body().string();//响应体
}
});
8. Okhttp缓存的使用
缓存的原理: DiskLruCache 缓存机制
//请求地址
String url = "http://wwww.bilibili.com";
//配置缓存的路径,和缓存空间的大小
Cache cache = new Cache(new File("/Users/zeal/Desktop/temp"),10*10*1024);
//创建Okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(15, TimeUnit.SECONDS)//超时设置
.writeTimeout(15, TimeUnit.SECONDS)
.connectTimeout(15, TimeUnit.SECONDS)
.cache(cache) //打开缓存
.build();
//创建请求对象并设置对应的参数
final Request request = new Request.Builder()
.url(url)
//.request 请求单独配置缓存策略
//.noCache(): 就算是本地有缓存,也不会读缓存,直接访问服务器
//.noStore(): 不会缓存数据,直接访问服务器
//.onlyIfCached():只请求缓存中的数据,不靠谱
.cacheControl(new CacheControl.Builder().build())//设置缓存控制
.build();
//创建相应的回调对象
Call call = okHttpClient.newCall(request);
//开启网络请求,并处理请求的回调
call.enqueue(new Callback() {
@Override
//请求失败的回调
public void onFailure(Call call, IOException e) {
//进行异常的捕获和处理
}
@Override
//请求成功的回调
public void onResponse(Call call, Response response) throws IOException {
//打印响应的结果
Log.d(TAG, "请求成功: " + response.body().string());
}
});
9. 失败重连的拦截器的使用
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.retryOnConnectionFailure();//配置失败重连
注:
不是所有的网络请求失败了都可以进行重连的。它内部会进行检测网络请求异常和响应码的情况,根据这些情况判断是否需要重新进行网络请求
10. 使用Gson来解析JSON响应
请自己去添加Gson的依赖
//请求地址
String url = "http://wwww.bilibili.com";
//创建Gson对象
Gson gson = new Gson();
//创建Okhttp客户端对象
OkHttpClient okHttpClient = new OkHttpClient();
//创建请求对象并设置对应的参数
final Request request = new Request.Builder()
.url(url)
.build();
//创建相应的回调对象
Call call = okHttpClient.newCall(request);
//开启网络请求,并处理请求的回调
call.enqueue(new Callback() {
@Override
//请求失败的回调
public void onFailure(Call call, IOException e) {
//进行异常的捕获和处理
}
@Override
//请求成功的回调
public void onResponse(Call call, Response response) throws IOException {
//打印响应的结果
Log.d(TAG, "请求成功: " + response.body().string());
//使用Gson
Gist gist = gson.fromJson(response.body().charStream(), Gist.class);
}
});
.
.
拦截器-interceptor的使用
两种自定义拦截器的区别:
1. 创建拦截器
在 LoggingInterceptor 中主要做了 3 件事:
1.请求前 - 打印请求信息
2.网络请求 - 递归去调用其他拦截器发生网络请求
3.网络响应后 - 打印响应信息
class MyInterceptor implements Interceptor {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
//1.请求前--打印请求信息
long t1 = System.nanoTime();
String url1 = request.url();
String connection1 = chain.connection();
Headers headers1 =request.headers().toString;
//2.开始网络请求
Response response = chain.proceed(request);
//3.网络响应后--打印响应信息
long t2 = System.nanoTime();
String url2 = request.url();
Headers headers2 =request.headers().toString;
//请求的时间
long time = (t2 - t1) / 1e6d;
return response;//返回响应的结果
}
}
Okhttp系统自带的拦截器:
作用:
负责失败重连的拦截器,如果我们想要实现失败重连,那么就要在 OkHttpClient 进行配置主要功能:
- 创建 StreamAllocation 对象
- 调用 RealInterceptorChain.proceed(…)进行网络请求
- 根据异常结果或者响应结果判断是否要进行重新请求
注:
第二和第三点是在 while (true)内部执行的,也就是系统通过死循环来实现重连机制作用:
添加必要的请求头信息和GZIP 压缩主要功能:
- 负责将用户构建的一个 Request 请求转化为能够进行网络访问的请求
- 将这个符合网络请求的 Request 进行网络请求
- 将网络请求回来的响应 Response 转化为用户可用的 Response
作用:
对响应进行缓存处理,取得缓存 response 返回并刷新缓存缓存的原理: DiskLruCache 缓存机制
CacheStrategy策略器:负责判断是使用缓存还是请求网络获取新的数据
作用:
负责与服务器建立 Socket 连接,并且创建了一个 HttpStream 它包括通向服务器的输入流和输出流作用:
利用 HttpStream 向服务器发送请求数据和接受服务器返回的数据主要功能:
- 写入请求头信息
- 有请求体的情况下,写入请求体信息
- 结束请求
- 读取响应头信息
5 获取响应体信息输入流- 往上一级 ConnectInterceptor 返回一个网络请求回来的 Response
补充: Okio
2. 添加 Application Interceptor
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new MyInterceptor())
.build();
Application Interceptor 是第一个执行的
Interceptor
,因此这里的 request 还是最原始的。而对于 response 是CallServerInterceptor
执行完毕之后才会将 Response 进行返回;整请求过程是递归的调用的过程结论: Application Interceptor 这里得到的 response 就是最终的响应,虽然中间有重定向过。
使用场景:
Application Interceptor 适用于在请求前统一添加一些公共参数,例如在添加 APP 的版本号,用户 ID ,手机版本号,运营商类型等参数。或者对响应体的数据进行 json 转化等操作
2. 添加 NetworkInterceptor
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new MyInterceptor())
.build();
NetwrokInterceptor 处于第 6 个拦截器中,它会经过
RetryAndFollowIntercptor
进行重定向并且也会通过BridgeInterceptor
进行 request 请求头和 响应 resposne 的处理,因此这里可以得到的是更多的信息结论: NetworkInterceptor 可以比 Application Interceptor 得到更多的信息
使用场景:
NetwrokInterceptor 在这一层拦截器中可以获取到最终发送请求的 request ,也可以获取到真正发生网络请求回来的 response 响应,从而修改对应的请求或者响应数据
.
.
OkHttp3中的线程池的介绍
OkHttp 中的对所有的任务采用 NamedRunnable
,约束每个执行单元给出对应的业务名称,以便于线程维护
Okhttp3线程池的种类:
1. 异步请求线程池-OkHttp Dispatcher
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
特点:
-
该线程池与Android下的 Executors.newCachedThreadPool() 比较类似
-
无任务上限,自动回收闲置60s的线程,适用于大量耗时较短的任务
-
虽然线程池无任务上限,但是Dispatcher对入口
enqueue()
进行了把关,最大的异步任务数默认是64,同一个主机默认是5,
Dispatcher提供的修改接口:
okHttpClient.dispatcher().setMaxRequests(128);//设置最大异步任务数
okHttpClient.dispatcher().setMaxRequestsPerHost(10);//设置最大同主机数
-
通过两个双端队列来维护准备执行的任务和正在执行的任务:
Deque<AsyncCall> readyAsyncCalls
,Deque<AsyncCall> runningAsyncCalls
-
在每个任务结束时,都会检查
readyAsyncCalls
是否有任务,在条件满足的情况下,按照先进先出的原则将任务移动到runningAsyncCalls
中,并在线程池中执行
.
2. 连接池清理线程池-OkHttp ConnectionPool
private static final Executor executor = new ThreadPoolExecutor(0 ,
Integer.MAX_VALUE , 60L , TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));
特点:
-
该线程池用来清理长时间闲置的和泄漏的连接
-
该线程池本身无任务上限,线程闲置60s自动回收
-
虽然任务无上限,但其通过
cleanupRunning
标记来控制只有一个线程在运行,当连接池中没有连接后才会被重新设置为false
; -
线程会不断地清理,当清理完一遍后超时连接后,根据当前连接池中最近的下一个空闲超时连接计算出一个阻塞时间并阻塞,直到连接池中没有任何连接才结束,并将
cleanupRunning
设为false
-
在每次有连接加入连接池时,如果当前没有清理任务运行,会加入一个清理任务到到线程池中执行
.
3. 缓存整理线程池-OkHttp DiskLruCache
Executor executor = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp DiskLruCache", true));
特点:
-
该线程池用于整理本地请求缓存数据
-
缓存的整理包含: 达到阀值大小的文件,删除最近最少使用的记录,在有关操作达到一定数量以后对记录进行重建
-
最大运行线程数1,无需考虑线程安全问题,自动回收闲置60s的线程
.
4. HTTP2异步事务线程池-OkHttp Http2Connection
-
HTTP2采用了多路复用,因此需要维护连接有效性,本线程池就是用于维护相关的各类HTTP2事务
-
线程池本身无任务上限,自动回收闲置60s的线程
-
每一个HTTP2连接都有这么一个线程池存在