自研一个简易版本的OkHTTP

一,背景

为了彻底搞明白okhttp原理,仿照okhttp自研一个

二,思路

业务上没发出一个request,使用AsyncCall包装起来,然后在网络分发器的作用下,执行具体的每一个Call,这些具体的Call会经过层层的拦截器,最终会调用到CallServiceInterceptor,这个对象里面有一个ConnectionPool,保存着每一个url对应的socket,最终处理的结果返回交给具体的每一个call,然后在回调到业务层

三,相关类图

四,具体代码实现

4.1 request

public class Request {private Map<String, String> headers;private String method;private HttpUrl url;private RequestBody body;public Request(Builder builder) {this.url = builder.url;this.method = builder.method;this.headers = builder.headers;this.body = builder.body;}public String method() {return method;}public HttpUrl url() {return url;}public RequestBody body() {return body;}public Map<String, String> headers() {return headers;}public final static class Builder {HttpUrl url;Map<String, String> headers = new HashMap<>();String method;RequestBody body;public Builder url(String url) {try {this.url = new HttpUrl(url);return this;} catch (MalformedURLException e) {throw new IllegalStateException("Failed Http Url", e);}}public Builder addHeader(String name, String value) {headers.put(name, value);return this;}public Builder removeHeader(String name) {headers.remove(name);return this;}public Builder get() {method = "GET";return this;}public Builder post(RequestBody body) {this.body = body;method = "POST";return this;}public Request build() {if (url == null) {throw new IllegalStateException("url == null");}if (TextUtils.isEmpty(method)) {method = "GET";}return new Request(this);}}
}

4.2 Response 

public class Response {int code;int contentLength = -1;Map<String, String> headers = new HashMap<>();String body;//保持连接boolean isKeepAlive;public Response() {}public Response(int code, int contentLength, Map<String, String> headers, String body,boolean isKeepAlive) {this.code = code;this.contentLength = contentLength;this.headers = headers;this.body = body;this.isKeepAlive = isKeepAlive;}public int getCode() {return code;}public int getContentLength() {return contentLength;}public Map<String, String> getHeaders() {return headers;}public String getBody() {return body;}public boolean isKeepAlive() {return isKeepAlive;}
}

4.3 HttpUrl

public class HttpUrl {String protocol;String host;String file;int port;public HttpUrl(String url) throws MalformedURLException {URL url1 = new URL(url);host = url1.getHost();file = url1.getFile();file = TextUtils.isEmpty(file) ? "/" : file;protocol = url1.getProtocol();port = url1.getPort();port = port == -1 ? url1.getDefaultPort() : port;}public String getProtocol() {return protocol;}public String getHost() {return host;}public String getFile() {return file;}public int getPort() {return port;}
}

4.4 Call

public class Call {Request request;HttpClient client;//是否执行过boolean executed;boolean cancel;public boolean isCancel() {return cancel;}public Request getRequest() {return request;}public Call(Request request, HttpClient client) {this.request = request;this.client = client;}public HttpClient getClient() {return client;}public void enqueue(Callback callback) {synchronized (this) {if (executed) {throw new IllegalStateException("已经执行过了,就不要执行");}executed = true;}//把任务交给调度器调度client.dispatcher().enqueue(new AsyncCall(callback));}/*** 是否取消*/public void cancel() {cancel = true;}/*** 执行网络请求的线程*/class AsyncCall implements Runnable {private Callback callback;public AsyncCall(Callback callback) {this.callback = callback;}@Overridepublic void run() {//是否回调过boolean singaledCallbacked = false;try {//执行真正的请求Response response = getResponse();if (cancel) {singaledCallbacked = true;callback.onFailure(Call.this, new IOException("客户端主动执行了cancel"));} else {singaledCallbacked = true;callback.onResponse(Call.this, response);}} catch (Exception e) {
//                e.printStackTrace();if (!singaledCallbacked) {//如果没有回调过callback.onFailure(Call.this, e);}} finally {//将这个任务从调度器移除client.dispatcher().finished(this);}}public String host() {return request.url().getHost();}}/*** 这里是重点!!!* @return*/private Response getResponse() throws Exception{//创建拦截器责任链List<Interceptor> interceptors = new ArrayList();//重试拦截器interceptors.add(new RetryInterceptor());//请求头拦截器interceptors.add(new HeaderInterceptor());//缓存拦截器interceptors.add(new CacheInterceptor());//连接拦截器interceptors.add(new ConnectionInterceptor());//通信拦截器interceptors.add(new CallServiceInterceptor());InterceptorChain chain = new InterceptorChain(interceptors, 0, this, null);return chain.process();}
}

4.5 InterceptorChain

public class InterceptorChain {List<Interceptor> interceptors;int index;Call call;HttpConnection httpConnection;public InterceptorChain(List<Interceptor> interceptors, int index, Call call, HttpConnection connection) {this.interceptors = interceptors;this.index = index;this.call = call;this.httpConnection = connection;}public Response process(HttpConnection httpConnection) throws IOException{this.httpConnection = httpConnection;return process();}public Response process() throws IOException {if (index >= interceptors.size()) throw new IOException("Interceptor China index out max length");//获得拦截器 去执行Interceptor interceptor = interceptors.get(index);InterceptorChain next = new InterceptorChain(interceptors, index + 1, call, httpConnection);Response response = interceptor.intercept(next);return response;}
}

4.6 Interceptor

public interface Interceptor {Response intercept(InterceptorChain chain) throws IOException;
}

4.7 ConnectionPool

public class ConnectionPool {private static final String TAG = "ConnectionPool";private static final boolean DEBUG = BuildConfig.DEBUG;private final long keepAlive;private boolean cleanrunning;private Deque<HttpConnection> connections = new ArrayDeque<>();public ConnectionPool() {this(1, TimeUnit.MINUTES);}public ConnectionPool(long keepAlive, TimeUnit unit) {this.keepAlive = unit.toMillis(keepAlive);}private Runnable cleanupRunable = new Runnable() {@Overridepublic void run() {while (true) {long waitDuration = cleanup(System.currentTimeMillis());if (waitDuration == -1) {return;}if (waitDuration > 0) {synchronized (ConnectionPool.this) {try {ConnectionPool.this.wait(waitDuration);} catch (InterruptedException e) {e.printStackTrace();}}}}}};private long cleanup(long currentTimeMillis) {long longgetIdleDuration = -1;synchronized (this) {Iterator<HttpConnection> iterator = connections.iterator();while (iterator.hasNext()) {HttpConnection connection = iterator.next();long idleDuration = currentTimeMillis - connection.getLastUseTime();//超过了最大允许闲置时间if (idleDuration > keepAlive) {if (DEBUG) Log.d(TAG, "ConnectionPool cleanup: " + "超出闲置时间,移除连接池");iterator.remove();connection.close();continue;}//没有超过闲置时间//记录 最长的闲置时间if (longgetIdleDuration < idleDuration) {longgetIdleDuration = idleDuration;}}//假如keepAlive是10s//if (longgetIdleDuration >= 0) {return keepAlive - longgetIdleDuration;}//连接池中没有连接cleanrunning = false;return longgetIdleDuration;}}private static final Executor executer = new ThreadPoolExecutor(0, Integer.MAX_VALUE,60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread thread = new Thread(r, "Connection Pool");thread.setDaemon(true);//设置为守护线程,可以理解为跟进程同样的生命周期return thread;}});public void put(HttpConnection httpConnection) {//如果没有执行清理线程if (!cleanrunning) {cleanrunning = true;executer.execute(cleanupRunable);}connections.add(httpConnection);}public synchronized HttpConnection get(String host, int port) {Iterator<HttpConnection> iterator = connections.iterator();while (iterator.hasNext()) {HttpConnection connection = iterator.next();//如果查找到连接池始终在相同的host和portif (connection.isSameAddress(host, port)) {iterator.remove();return connection;}}return null;}}

4.8  CallServiceInterceptor

public class CallServiceInterceptor implements Interceptor {private static final String TAG = "CallServiceInterceptor";private static final boolean DEBUG = BuildConfig.DEBUG;@Overridepublic Response intercept(InterceptorChain chain) throws IOException {if (DEBUG) Log.d(TAG, "CallServiceInterceptor intercept: " + "通信拦截器");HttpConnection connection = chain.httpConnection;HttpCode httpCode = new HttpCode();InputStream inputStream = connection.call(httpCode);String statusLine = httpCode.readLine(inputStream);Map<String, String> headers = httpCode.readHeaders(inputStream);int contentLength = -1;if (headers.containsKey("Content-Length")) {//如果有content-length,代表可以拿到响应体的字节长度contentLength = Integer.valueOf(headers.get("Content-Length"));}boolean isChunked = false;if (headers.containsKey("Transfer-Encoding")) {//如果有有Transfer-Encoding,表示是分块编码,此时没有响应体的长度isChunked = headers.get("Transfer-Encoding").equalsIgnoreCase("chunked");}String body = null;if (contentLength > 0) {byte[] bytes = httpCode.readBytes(inputStream, contentLength);body = new String(bytes);} else if (isChunked) {body = httpCode.readChunked(inputStream);}String[] status = statusLine.split(" ");boolean isKeepAlive = false;if (headers.containsKey("Connection")) {isKeepAlive = headers.get("Connection").equalsIgnoreCase("Keep-Alive");}connection.updateLastUseTime();return new Response(Integer.valueOf(status[1]), contentLength, headers, body,isKeepAlive);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/135775.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【css | linear-gradient】linear-gradient()的用法

linear-gradient() CSS函数创建一个由两种或多种颜色沿一条直线进行线性过渡的图像,其结果是<gradient>数据类型的对象,此对象是一种特殊的<image> 数据类型。 先看一个线上的示例 https://code.juejin.cn/pen/7277486410842996771 语法 /* 渐变轴为 45 度&…

Iterator设计模式

目录 1、示例 1.1 Aggregate接口 1.2 Iterator接口 1.3 Book类 1.4 BookShelf类 1.6 BookShelfIterator 类 1.7 Main类 2、解释Iterator模式中的角色 2.1 Iterator模式的存在意义 2.2 抽象类和接口 2.3 Aggregate 和 Iterator的对应 2.4 容易弄错"下一个"…

day06_Java中的流程控制语句

流程控制 简单来讲所谓流程就是完成一件事情的多个步骤组合起来就叫做一个流程。在一个程序执行的过程中&#xff0c;各条语句的执行顺序对程序的结果是有直接影响的。我们必须清楚每条语句的执行流程。而且&#xff0c;很多时候要通过控制语句的执行顺序来实现我们想要的功能…

OpenCV(三十五):凸包检测

1.凸包检测介绍 凸包检测是计算凸包的一种技术&#xff0c;凸包就是&#xff1a;给定二维平面上的点集&#xff0c;将最外层的点连接起来构成的凸边形&#xff0c;它是包含点集中所有的点。 2.凸包检测函数convexHull() void cv::convexHull ( InputArray points, OutputArra…

Web应用系统的小安全漏洞及相应的攻击方式

写作目的 本文讲述一个简单的利用WebAPI来进行一次基本没有破坏力的“黑客”行为。 主要目的如下&#xff1a; 了解什么叫安全漏洞 知道什么是api 了解一些获取api的工具 通过对API的认识了解白盒接口测试基本概念和技术 免责声明&#xff1a; 本文主要是以学习交流为目的&a…

【业务功能118】微服务-springcloud-springboot-Kubernetes集群-k8s集群-KubeSphere-OpenELB部署及应用

OpenELB部署及应用 一、OpenELB介绍 网址&#xff1a; openelb.io OpenELB 是一个开源的云原生负载均衡器实现&#xff0c;可以在基于裸金属服务器、边缘以及虚拟化的 Kubernetes 环境中使用 LoadBalancer 类型的 Service 对外暴露服务。OpenELB 项目最初由 KubeSphere 社区发…

【面试题】——Java基础篇(33题)

文章目录 1. 八大基本数据类型分类2. 重写和重载的区别3. int和integer区别4. Java的关键字5. 什么是自动装箱和拆箱&#xff1f;6. 什么是Java的多态性&#xff1f;7. 接口和抽象类的区别&#xff1f;8. Java中如何处理异常&#xff1f;9. Java中的final关键字有什么作用&…

RabbitMQ深入 —— 死信队列

前言 前面荔枝梳理了RabbitMQ中的普通队列、交换机以及相关的知识&#xff0c;在这篇文章中荔枝将会梳理RabbitMQ的一个重要的队列 —— 死信队列&#xff0c;主要了解消息流转到死信队列的三种的方式以及相应的实现demo。希望能帮助到有需要的小伙伴~~~ 文章目录 前言 死信队…

Python统计pdf中英文单词的个数

之前的文章提供了批量识别pdf中英文的方法,详见【python爬虫】批量识别pdf中的英文,自动翻译成中文上。以及自动pdf英文转中文文档,详见【python爬虫】批量识别pdf中的英文,自动翻译成中文下。    本文实现python统计pdf中英文字符的个数。 文章目录 一、要统计字符的pdf…

Pybooks:这十本Python编程语言的入门书籍入门必看!

这个好像没有在微信发过图文版的&#xff0c;补一个。大部分介绍摘自京东等网站。 Python基础教程 评语&#xff1a;Python入门佳作 经典教程的全新修订 10个项目引人入胜 《Python基础教程&#xff08;第2版修订版&#xff09;》是经典的Python入门教程&#xff0c;层次鲜明…

贪心算法的思路和典型例题

一、贪心算法的思想 贪心算法是一种求解问题时&#xff0c;总是做出在当前看来是最好的选择&#xff0c;不从整体最优上加以考虑的算法。 二.用贪心算法的解题策略 其基本思路是从问题的某一个初始解出发一步一步地进行&#xff0c;根据某个优化测度&#xff0c;每一步都要确保…

第21章_瑞萨MCU零基础入门系列教程之事件链接控制器ELC

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

用Python实现链式调用

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 我们在使用Django的models查询数据库时&#xff0c;可以看到有这种写法&#xff1a; form app.models import XXX query XXX.objects.all() query query.filter(name123, age456).filter(salary999)在这种写法里面&#xf…

排序算法-----插入排序

目录 前言&#xff1a; 插入排序 原理图 代码实现 分析总结 二分法插入排序 代码实现 前言&#xff1a; 嗨嗨^_^&#xff0c;米娜桑&#xff0c;今天我们继续学习排序算法中的插入排序&#xff0c;激不激动&#xff0c;兴不兴奋呢&#xff01;好了废话不多说&#xff0c;…

Docker基础学习

Docker 学习目标&#xff1a; 掌握Docker基础知识&#xff0c;能够理解Docker镜像与容器的概念 完成Docker安装与启动 掌握Docker镜像与容器相关命令 掌握Tomcat Nginx 等软件的常用应用的安装 掌握docker迁移与备份相关命令 能够运用Dockerfile编写创建容器的脚本 能够…

python抠图(去水印)开源库lama-cleaner入门应用实践

1. 关于 Lama Cleaner Lama Cleaner 是由 SOTA AI 模型提供支持的免费开源图像修复工具。可以从图片中移除任何不需要的物体、缺陷和人&#xff0c;或者擦除并替换&#xff08;powered by stable diffusion&#xff09;图片上的任何东西。 特征&#xff1a; 完全免费开源&am…

qiankun 乾坤主应用访问微应用css静态图片资源报404

发现static前没有加我指定的前缀 只有加了后才会出来 解决方案: env定义前缀 .env.development文件中 # static前缀 VUE_APP_PUBLIC_PREFIX"" .env.production文件中 # static前缀 VUE_APP_PUBLIC_PREFIX"/szgl" settings文件是封了一下src\settings…

Android平台下奔溃Crash和无响应ANR日志抓取分析

一、使用AndroidStudio 在logcat中查看实时日志&#xff0c;需要选择连接的手机和应用包名 AS下载链接 二、使用adb shell dumpsys dropbox命令获取 #!/bin/bash # path"/data/system/dropbox" # 在手机这个目录下存储了崩溃日志 newest_time$(adb shell dumps…

C++day3

1> 自行封装一个栈的类&#xff0c;包含私有成员属性&#xff1a;栈的数组、记录栈顶的变量 成员函数完成&#xff1a;构造函数、析构函数、拷贝构造函数、入栈、出栈、清空栈、判空、判满、获取栈顶元素、求栈的大小 头文件 #ifndef STACK_H #define STACK_H #include &…

二叉树的概念及存储结构

目录 1.树的概念 1.1树的相关概念 1.2树的表示与应用 2.二叉树的概念及结构 2.1二叉树的概念 2.1.1特殊的二叉树 2.2.2二叉树的性质 2.2二叉树的结构 2.2.1顺序存储 2.2.2链式存储 这是一篇纯理论的博客,会对数据结构中的二叉树进行详细的讲解,让你对树的能有个清晰的…