OKhttp * 实现实践环节源码解析
作者:itbird01 发布时间:2023-04-27 05:00:21
本节我们开始自我实现我们自己okhttp框架中的每个 * 。
先简单回顾一下各个 * 的作用:
RetryAndFollowUpInterceptor:重试 *
处理重试的一个 * ,会去处理一些异常,根据底层返回的响应数据,先进行一些特殊状态码的判断,例如:如果底层返回307,则根据服务端返回的最新location,重新构建新的请求,交由底层 * ,重新发起请求。如果底层返回路由异常、某些IO异常,则会continue,重新发起请求。
BridgeInterceptor:基础的 *
给我们平常发起的请求,添加通用和请求首部信息,做一个简单的处理,设置一些通用的请求头,Cookie、Connection、Content-Type、Content-Length,做一些返回的处理,如果返回的数据被压缩了,采用 ZipSource,保存Cookie。
CacheInterceptor:缓存 *
缓存存储策略、缓存过期策略、缓存对比策略的具体实现。
ConnectInterceptor:连接的 *
ConnectInterceptor负责连接复用、建立socket连接,okio与socket输入输出流绑定。
CallServerInterceptor: 具体与服务器通信,给服务器写数据和读取数据;
* 的自我实现
好了,接下来,把我们之前写的框架的代码,重新梳理一下,新增一下几个 * 。 RealCall.java
package com.itbird.okhttpstudy.okhttp;
import android.util.Log;
import com.itbird.okhttpstudy.interceptor.BridgeInterceptor;
import com.itbird.okhttpstudy.interceptor.CacheInterceptor;
import com.itbird.okhttpstudy.interceptor.CallServerInterceptor;
import com.itbird.okhttpstudy.interceptor.ConnectInterceptor;
import com.itbird.okhttpstudy.interceptor.Interceptor;
import com.itbird.okhttpstudy.interceptor.RetryAndFollowUpInterceptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by itbird on 2022/11/21
*/
public class RealCall implements Call {
private Request request;
private OkhttpClient okhttpClient;
public RealCall(Request request, OkhttpClient okhttpClient) {
this.request = request;
this.okhttpClient = okhttpClient;
}
@Override
public void enqueue(Callback callback) {
okhttpClient.dispatcher().enqueue(new AsyncCall(callback));
}
@Override
public Response execute() {
return getResponseWithInterceptorChain();
}
@Override
public Request request() {
return request;
}
private Response getResponseWithInterceptorChain() {
List<Interceptor> interceptors = new ArrayList<Interceptor>();
interceptors.add(new BridgeInterceptor());// 基础
interceptors.add(new CacheInterceptor());// 缓存
interceptors.add(new ConnectInterceptor());// 建立连接
interceptors.add(new CallServerInterceptor());// 写数据
interceptors.add(new RetryAndFollowUpInterceptor());// 重试
Interceptor.Chain chain = new RealInterceptorChain(this, interceptors, request);
try {
return chain.proceed(request);
} catch (IOException e) {
//处理过程被中断时,通过错误码返回
return null;
}
}
class AsyncCall extends NamedRunnable {
private Callback callback;
public AsyncCall(Callback callback) {
this.callback = callback;
}
@Override
public void execute() {
Log.d(Constants.TAG, "AsyncCall execute");
//这里有问题的
Response response = getResponseWithInterceptorChain();
if (callback != null) {
try {
callback.onResponse(RealCall.this, response);
} catch (IOException e) {
}
}
}
}
}
接下来还是老办法,按照AS提示,新建这些类。
RetryAndFollowUpInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
/**
* Created by itbird on 2022/11/24
*/
public class RetryAndFollowUpInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "RetryAndFollowUpInterceptor");
Request request = chain.request();
//okhttp表现为,此处,去根据底层抛出的异常,决定是否为关键错误异常,如果不是,则while true循环,去执行重试请求
return chain.proceed(request);
}
}
BridgeInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.RequsetBody;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
/**
* Created by itbird on 2022/11/24
*/
public class BridgeInterceptor implements Interceptor {
public BridgeInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "BridgeInterceptor");
Request request = chain.request();
// 添加一些请求头
// request.addParam("Connection", "keep-alive");
// 做一些其他处理
if (request.requsetBody() != null) {
RequsetBody requestBody = request.requsetBody();
request.addParam("Content-Type", requestBody.getContentType());
request.addParam("Content-Length", Long.toString(requestBody.getContentLength()));
}
//GZIP数据流转换
return chain.proceed(request);
}
}
CacheInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.CacheControl;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
/**
* Created by itbird on 2022/11/24
*/
public class CacheInterceptor implements Interceptor {
public CacheInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "CacheInterceptor");
Request request = chain.request();
if (request.cache() == CacheControl.FORCE_CACHE) {
//本地缓存有没有,缓存过期了没有,缓存对比服务器返回307
}
return chain.proceed(request);
}
}
ConnectInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
/**
* Created by itbird on 2022/11/24
*/
public class ConnectInterceptor implements Interceptor {
public ConnectInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "ConnectInterceptor");
Request request = chain.request();
//表现为okhttp的话,这里就是socket简历连接,并且将socket输入输出流与okio绑定在一起
return chain.proceed(request);
}
}
CallServerInterceptor
package com.itbird.okhttpstudy.interceptor;
import android.util.Log;
import com.itbird.okhttpstudy.okhttp.Constants;
import com.itbird.okhttpstudy.okhttp.Request;
import com.itbird.okhttpstudy.okhttp.Response;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by itbird on 2022/11/24
*/
public class CallServerInterceptor implements Interceptor {
public CallServerInterceptor() {
}
@Override
public Response intercept(Chain chain) throws IOException {
Log.d(Constants.TAG, "CallServerInterceptor");
Request request = chain.request();
try {
//获取连接请求
URL url = new URL(request.url());
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//设置连接超时
httpURLConnection.setConnectTimeout(3000);
//设置方法
httpURLConnection.setRequestMethod(request.method());
if (request.requsetBody() != null) {
httpURLConnection.setRequestProperty("Content-Type", request.requsetBody().getContentType());
httpURLConnection.setRequestProperty("Content-Length", String.valueOf(request.requsetBody().getContentLength()));
Log.d(Constants.TAG, httpURLConnection.getRequestProperty("Content-Length"));
Log.d(Constants.TAG, httpURLConnection.getRequestProperty("Content-Type"));
}
//开始连接
httpURLConnection.connect();
//插入,如果requsetbody不为空,则继续写入内容
if (request.requsetBody() != null) {
request.requsetBody().writeBodyData(httpURLConnection.getOutputStream());
}
//判断返回的状态码
if (httpURLConnection.getResponseCode() == 200) {
//获取返回的数据
InputStream inputStream = httpURLConnection.getInputStream();
//将返回的数据,封装为response
Response response = new Response(inputStream);
return response;
}
} catch (MalformedURLException e) {
throw e;
} catch (IOException e) {
throw e;
}
return null;
}
}
运行一下
题外话
说到责任链模式,这里有一个题外话,我们之前分析view事件源码的时候,也看到过,view 事件源码,也是责任链机制,它是通过每层返回true、false来决定是否拦截。
大家想一下,和okhttp这里的责任链有啥不同的?我们上面查看okhttp源码的时候知道,它并不是通过每层返回true or false来决定是否拦截的,而是根据每层返回的response 以及 是否抛出异常来决定是否拦截。
来源:https://juejin.cn/post/7181992810667573306


猜你喜欢
- 1. 你知道volatile是如何保证可见性吗?我们先看一组代码:public class VolatileVisibleDemo { &n
- 本文主要讲解如何通过RabbitMQ实现定时任务(延时队列)环境准备需要在MQ中进行安装插件 地址链接插件介绍地址:https://www.
- 一:什么是协变与逆变协变指能够使用比原始指定的派生类型的派生程度更大(更具体的)的类型,逆变指能够使用比原始指定的派生类型的派生程度更小(不
- 话不多说,上来就是干!?1234567891011121314151617181920212223242526272829303132333
- 实现HandlerInterceptor接口或者继承HandlerInterceptor的子类,比如Spring 已经提供的实现了Handl
- 每一个应用程序,其实都会有分享的需求,比如一键分享一篇文章或者一些活动到微博或者微信亦或者是twitter等社交平台,因为人类是社交动物,而
- Kotlin基础教程之数据类型一切都是对象.在Kotlin中一切都是对象.Kotlin有一些基本类型Boolean,Byte,Shot,In
- 前言不知道从哪一个版本起,Android studio 设置界面中已经没有忽略文件的设置。可能也是没有找到。下面简单记录下如何简单高效的配置
- 概述 这是一个自定义色盘,根据点,直线和圆的几何学加上hsv颜色模型完成技术点几何:圆的标准方程式:(x-a)²
- 一、前言Redis是一个NoSQL(非关系型数据库)数据库之一,key-value存储系统或者说是一个缓存键值对数据库,具有如下特性:基于内
- 因为在Action的execute方法声明时就抛出了Exception异常,所以我们无需再execute方法中捕捉异常,仅需在struts.
- 上一篇介绍了elasticsearch的client结构,client只是一个门面,在每个方法后面都有一个action来承接相应的功能。但是
- 在上篇文章给大家介绍了使用XSD校验Mybatis的SqlMapper配置文件的方法(1),需要的朋友可以参考下。编写好XSD文件,然后来看
- 调试的时候,在循环里增加条件判断,可以极大的提高效率,心情也能愉悦。以下介绍下IDEA使用条件【Condition】断点的方法1、编写一段样
- 前言本文主要给大家介绍了关于Android在Gradle中更改APK文件名的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细
- Kotlin中SharedFlow的使用 VS StateFlowSharedFlow 是继承于 Flow ,同时它是 StateFlow
- Java中的Runable,Callable,Future,FutureTask,ExecutorService,Excetor,Excut
- 可以用如下方法: 修改AudioYusuStreamOut.cpp,添加方法: void AudioYusuStreamOut::swS2M
- 一、平衡二叉树的定义平衡二叉树是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1 。它是一种高度平衡的二叉排序树。意思是说,
- 一、项目简述功能: 主页显示商品; 所有二手书商品展示,可进行商品搜索; 点击商品进入商品详情页,具有立即购买和加入购物车功能,可增减购买商