From 248d71e3b3fa0bf8f1da1fb08cd0aef30a0d654d Mon Sep 17 00:00:00 2001 From: ejlchina Date: Tue, 31 Mar 2020 09:43:45 +0800 Subject: [PATCH 01/94] Initial commit --- .gitee/ISSUE_TEMPLATE.zh-CN.md | 13 ++ .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md | 15 ++ LICENSE | 201 ++++++++++++++++++++++++++ README.en.md | 36 +++++ README.md | 37 +++++ 5 files changed, 302 insertions(+) create mode 100644 .gitee/ISSUE_TEMPLATE.zh-CN.md create mode 100644 .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md create mode 100644 LICENSE create mode 100644 README.en.md create mode 100644 README.md diff --git a/.gitee/ISSUE_TEMPLATE.zh-CN.md b/.gitee/ISSUE_TEMPLATE.zh-CN.md new file mode 100644 index 00000000..f09d98dd --- /dev/null +++ b/.gitee/ISSUE_TEMPLATE.zh-CN.md @@ -0,0 +1,13 @@ +### 该问题是怎么引起的? + + + +### 重现步骤 + + + +### 报错信息 + + + + diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md new file mode 100644 index 00000000..1e5b8d33 --- /dev/null +++ b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md @@ -0,0 +1,15 @@ +### 相关的Issue + + +### 原因(目的、解决的问题等) + + +### 描述(做了什么,变更了什么) + + +### 测试用例 + + + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.en.md b/README.en.md new file mode 100644 index 00000000..724a4724 --- /dev/null +++ b/README.en.md @@ -0,0 +1,36 @@ +# okhttps + +#### Description +OkHttp3 轻量封装 ,功能力求开箱即用、设计力求优雅与纯粹,Java领域前后端处Http问题的新选择。 + +#### Software Architecture +Software architecture description + +#### Installation + +1. xxxx +2. xxxx +3. xxxx + +#### Instructions + +1. xxxx +2. xxxx +3. xxxx + +#### Contribution + +1. Fork the repository +2. Create Feat_xxx branch +3. Commit your code +4. Create Pull Request + + +#### Gitee Feature + +1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md +2. Gitee blog [blog.gitee.com](https://blog.gitee.com) +3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) +4. The most valuable open source project [GVP](https://gitee.com/gvp) +5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) +6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md new file mode 100644 index 00000000..72dc1707 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# okhttps + +#### 介绍 +OkHttp3 轻量封装 ,功能力求开箱即用、设计力求优雅与纯粹,Java领域前后端处Http问题的新选择。 + +#### 软件架构 +软件架构说明 + + +#### 安装教程 + +1. xxxx +2. xxxx +3. xxxx + +#### 使用说明 + +1. xxxx +2. xxxx +3. xxxx + +#### 参与贡献 + +1. Fork 本仓库 +2. 新建 Feat_xxx 分支 +3. 提交代码 +4. 新建 Pull Request + + +#### 码云特技 + +1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md +2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) +3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 +4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 +5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) +6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) From 712eefa2e5723f40dae1b4cb32b55162fe84efef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Tue, 31 Mar 2020 14:18:33 +0800 Subject: [PATCH 02/94] clone from httputils --- .gitee/ISSUE_TEMPLATE.zh-CN.md | 13 - .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md | 15 - .gitignore | 7 + README.en.md | 36 - README.md | 789 +++++++++++++++++- pom.xml | 169 ++++ .../java/com/ejlchina/http/Configurator.java | 17 + .../java/com/ejlchina/http/DownListener.java | 19 + src/main/java/com/ejlchina/http/Download.java | 307 +++++++ src/main/java/com/ejlchina/http/HTTP.java | 70 ++ src/main/java/com/ejlchina/http/HttpCall.java | 28 + .../java/com/ejlchina/http/HttpResult.java | 282 +++++++ src/main/java/com/ejlchina/http/HttpTask.java | 710 ++++++++++++++++ .../java/com/ejlchina/http/HttpUtils.java | 105 +++ .../java/com/ejlchina/http/OnCallback.java | 11 + .../java/com/ejlchina/http/Preprocessor.java | 36 + src/main/java/com/ejlchina/http/Process.java | 31 + .../java/com/ejlchina/http/TaskListener.java | 20 + .../ejlchina/http/internal/AsyncHttpTask.java | 269 ++++++ .../ejlchina/http/internal/HttpClient.java | 445 ++++++++++ .../ejlchina/http/internal/HttpException.java | 21 + .../http/internal/ProcessInputStream.java | 53 ++ .../http/internal/ProcessRequestBody.java | 91 ++ .../http/internal/RealHttpResult.java | 134 +++ .../ejlchina/http/internal/RealProcess.java | 50 ++ .../ejlchina/http/internal/ResultBody.java | 323 +++++++ .../ejlchina/http/internal/SyncHttpTask.java | 92 ++ .../ejlchina/http/internal/TaskExecutor.java | 98 +++ src/test/java/com/ejlchina/test/HttpTest.java | 611 ++++++++++++++ src/test/java/com/ejlchina/test/User.java | 27 + 30 files changed, 4791 insertions(+), 88 deletions(-) delete mode 100644 .gitee/ISSUE_TEMPLATE.zh-CN.md delete mode 100644 .gitee/PULL_REQUEST_TEMPLATE.zh-CN.md create mode 100644 .gitignore delete mode 100644 README.en.md create mode 100644 pom.xml create mode 100644 src/main/java/com/ejlchina/http/Configurator.java create mode 100644 src/main/java/com/ejlchina/http/DownListener.java create mode 100644 src/main/java/com/ejlchina/http/Download.java create mode 100644 src/main/java/com/ejlchina/http/HTTP.java create mode 100644 src/main/java/com/ejlchina/http/HttpCall.java create mode 100644 src/main/java/com/ejlchina/http/HttpResult.java create mode 100644 src/main/java/com/ejlchina/http/HttpTask.java create mode 100644 src/main/java/com/ejlchina/http/HttpUtils.java create mode 100644 src/main/java/com/ejlchina/http/OnCallback.java create mode 100644 src/main/java/com/ejlchina/http/Preprocessor.java create mode 100644 src/main/java/com/ejlchina/http/Process.java create mode 100644 src/main/java/com/ejlchina/http/TaskListener.java create mode 100644 src/main/java/com/ejlchina/http/internal/AsyncHttpTask.java create mode 100644 src/main/java/com/ejlchina/http/internal/HttpClient.java create mode 100644 src/main/java/com/ejlchina/http/internal/HttpException.java create mode 100644 src/main/java/com/ejlchina/http/internal/ProcessInputStream.java create mode 100644 src/main/java/com/ejlchina/http/internal/ProcessRequestBody.java create mode 100644 src/main/java/com/ejlchina/http/internal/RealHttpResult.java create mode 100644 src/main/java/com/ejlchina/http/internal/RealProcess.java create mode 100644 src/main/java/com/ejlchina/http/internal/ResultBody.java create mode 100644 src/main/java/com/ejlchina/http/internal/SyncHttpTask.java create mode 100644 src/main/java/com/ejlchina/http/internal/TaskExecutor.java create mode 100644 src/test/java/com/ejlchina/test/HttpTest.java create mode 100644 src/test/java/com/ejlchina/test/User.java diff --git a/.gitee/ISSUE_TEMPLATE.zh-CN.md b/.gitee/ISSUE_TEMPLATE.zh-CN.md deleted file mode 100644 index f09d98dd..00000000 --- a/.gitee/ISSUE_TEMPLATE.zh-CN.md +++ /dev/null @@ -1,13 +0,0 @@ -### 该问题是怎么引起的? - - - -### 重现步骤 - - - -### 报错信息 - - - - diff --git a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md b/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md deleted file mode 100644 index 1e5b8d33..00000000 --- a/.gitee/PULL_REQUEST_TEMPLATE.zh-CN.md +++ /dev/null @@ -1,15 +0,0 @@ -### 相关的Issue - - -### 原因(目的、解决的问题等) - - -### 描述(做了什么,变更了什么) - - -### 测试用例 - - - - - diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..d848a362 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/target/ +/bin/ +/.settings/ +/.classpath +/.project +/.idea/** +*.iml \ No newline at end of file diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 724a4724..00000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# okhttps - -#### Description -OkHttp3 轻量封装 ,功能力求开箱即用、设计力求优雅与纯粹,Java领域前后端处Http问题的新选择。 - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 72dc1707..b83e8387 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,778 @@ -# okhttps +# HttpUtils -#### 介绍 -OkHttp3 轻量封装 ,功能力求开箱即用、设计力求优雅与纯粹,Java领域前后端处Http问题的新选择。 +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.ejlchina/httputils/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.ejlchina/httputils/) +[![License](https://img.shields.io/hexpm/l/plug.svg)](https://gitee.com/ejlchina-zhxu/httputils/blob/master/LICENSE) +[![Troy.Zhou](https://img.shields.io/badge/%E4%BD%9C%E8%80%85-ejlchina-orange.svg)](https://github.com/ejlchina) -#### 软件架构 -软件架构说明 +## 介绍 +  HttpUtils 是近期开源的对 OkHttp 轻量封装的框架,它独创的异步预处理器,特色的标签,灵活的上传下载进度监听与过程控制功能,在轻松解决很多原本另人头疼问题的同时,设计上也力求纯粹与优雅。 -#### 安装教程 + * 链式调用,一点到底 + * BaseURL、URL占位符、JSON自动封装与解析 + * 同步拦截器、异步预处理器、回调执行器 + * 文件上传下载(过程控制、进度监听) + * TCP连接池、Http2 -1. xxxx -2. xxxx -3. xxxx +### 当前文档版本[2.3.0](近期发布到中央仓库) +#### [查阅[2.1.x]点我](https://gitee.com/ejlchina-zhxu/httputils/blob/master/README-2.1.2.md) | [查阅[2.0.x]点我](https://gitee.com/ejlchina-zhxu/httputils/blob/master/README-2.0.0.md) | [查阅[1.0.x]点我](https://gitee.com/ejlchina-zhxu/httputils/blob/1.x/README.md) -#### 使用说明 +## 目录 -1. xxxx -2. xxxx -3. xxxx ++ [安装教程](#安装教程) + + [Maven](#maven) + + [Gradle](#gradle) ++ [使用说明](#使用说明) + + [1 简单示例](#1-简单示例) + - [1.1 构建HTTP](#11-构建-http) + - [1.2 同步请求](#12-同步请求) + - [1.3 异步请求](#13-异步请求) + + [2 请求方法(GET|POST|PUT|DELETE)](#2-请求方法getpostputdelete) + + [3 解析请求结果](#3-解析请求结果) + - [3.1 回调函数](#31-回调函数) + - [3.2 HttpResult](#32-HttpResult) + - [3.3 HttpCall](#33-HttpCall) + + [4 构建HTTP任务](#4-构建HTTP任务) + + [5 使用标签](#5-使用标签) + + [6 配置 HTTP](#65-配置-http) + - [6.1 设置 BaseUrl](#61-设置-baseurl) + - [6.2 回调执行器](#62-回调执行器) + - [6.3 配置 OkHttpClient](#63-配置-okhttpclient) + - [6.4 并行预处理器](#64-并行预处理器) + - [6.5 串行预处理器](#65-串行预处理器) + - [6.6 全局回调处理](#66-全局回调处理) + - [6.7 全局下载监听](#67-全局下载监听) + + [7 使用 HttpUtils 类](#7-使用-httputils-类) + + [8 文件下载](#8-文件下载) + - [8.1 下载进度监听](#81-下载进度监听) + - [8.2 下载过程控制](#82-下载过程控制) + - [8.3 实现断点续传](#83-实现断点续传) + - [8.4 实现分块下载](#84-实现分块下载) + + [9 文件上传](#9-文件上传) + - [9.1 上传进度监听](#91-上传进度监听) + - [9.2 上传过程控制](#92-上传过程控制) + + [10 执行线程自由切换(for Android)](#10-执行线程自由切换for-android) ++ [参考文档](#参考文档) ++ [联系方式](#联系方式) -#### 参与贡献 +## 安装教程 + +### Maven + +``` + + com.ejlchina + httputils + 2.3.0 + +``` +### Gradle + +`compile 'com.ejlchina:httputils:2.3.0'` + +## 使用说明 + +### 1 简单示例 + +#### 1.1 构建 HTTP + +```java +HTTP http = HTTP.builder().build(); +``` +  以上代码构建了一个最简单的`HTTP`实例,它拥有以下三个方法: + +* `async(String url)` 开始一个异步HTTP任务 +* `sync(String url)` 开始一个同步HTTP任务 +* `cancel(String tag)` 根据标签批量取消HTTP任务 + +  为了使用方便,在构建的时候,我们更愿意指定一个`BaseUrl`(请参见[5.1 设置 BaseUrl](#51-设置-baseurl)): + +```java +HTTP http = HTTP.builder() + .baseUrl("http://api.demo.com") + .build(); +``` +  为了简化文档,下文中出现的`http`均是在构建时设置了`BaseUrl`的`HTTP`实例。 + +#### 1.2 同步请求 + +  使用方法`sync(String url)`开始一个同步请求: + +```java +List users = http.sync("/users") // http://api.demo.com/users + .get() // GET请求 + .getBody() // 获取响应报文体 + .toList(User.class); // 得到目标数据 +``` +  方法`sync`返回一个同步`HttpTask`,可链式使用。 + +#### 1.3 异步请求 + +  使用方法`async(String url)`开始一个异步请求: + +```java +http.async("/users/1") // http://api.demo.com/users/1 + .setOnResponse((HttpResult result) -> { + // 得到目标数据 + User user = result.getBody().toBean(User.class); + }) + .get(); // GET请求 +``` +  方法`async`返回一个异步`HttpTask`,可链式使用。 + +### 2 请求方法(GET|POST|PUT|DELETE) + +  同步与异步的`HttpTask`都拥有`get`、`post`、`put`与`delete`方法。不同的是:同步`HttpTask`的这些方法返回一个`HttpResult`,而异步`HttpTask`的这些方法返回一个`HttpCall`。 + +```java +HttpResult res1 = http.sync("/users").get(); // 同步 GET +HttpResult res2 = http.sync("/users")post(); // 同步 POST +HttpResult res3 = http.sync("/users/1").put(); // 同步 PUT +HttpResult res4 = http.sync("/users/1").delete();// 同步 DELETE +HttpCall call1 = http.async("/users").get(); // 异步 GET +HttpCall call2 = http.async("/users").post(); // 异步 POST +HttpCall call3 = http.async("/users/1").put(); // 异步 PUT +HttpCall call4 = http.async("/users/1").delete();// 异步 DELETE +``` +### 3 解析请求结果 + +#### 3.1 回调函数 + +  只有异步请求才可以设置回调函数: + +```java +http.async("/users/{id}") // http://api.demo.com/users/1 + .addPathParam("id", 1) + .setOnResponse((HttpResult result) -> { + // 响应回调 + }) + .setOnException((IOException e) -> { + // 异常回调 + }) + .setOnComplete((State state) -> { + // 完成回调,无论成功失败都会执行 + }) + .get(); +``` + +#### 3.2 HttpResult + +  `HttpResult`是HTTP请求执行完后的结果,它是同步请求方法( `get`、`post`、`put`、`delete`)的返回值,也是异步请求响应回调(`OnResponse`)的参数,它定义了如下方法: + +* `getState()` 得到请求执行状态枚举,它有以下取值: + * `State.CANCELED` 请求被取消 + * `State.RESPONSED` 已收到响应 + * `State.TIMEOUT` 请求超时 + * `State.NETWORK_ERROR` 网络错误 + * `State.EXCEPTION` 其它请求异常 +* `getStatus()` 得到HTTP状态码 +* `isSuccessful()` 是否响应成功,状态码在 [200..300) 之间 +* `getHeaders()` 得到HTTP响应头 +* `getHeaders(String name)` 得到HTTP响应头 +* `getHeader(String name)` 得到HTTP响应头 +* `getBody()` 得到响应报文体`Body`实例,它定义了如下方法(对同一个`Body`实例,以下的`toXXX()`类方法只能使用一个且仅能调用一次): + * `toBytes()` 返回字节数组 + * `toByteStream()` 返回字节输入流 + * `toCharStream()` 返回字符输入流 + * `toString()` 返回字符串 + * `toJsonObject()` 返回Json对象 + * `toJsonArray()` 返回Json数组 + * `toBean(Class type)` 返回根据type自动json解析后的JavaBean + * `toList(Class type)` 返回根据type自动json解析后的JavaBean列表 + * `toFile(String filePath)` 下载到指定路径 + * `toFile(File file)` 下载到指定文件 + * `toFolder(String dirPath)` 下载到指定目录 + * `toFolder(File dir)` 下载到指定目录 + * `getContentType()` 返回报文体的媒体类型 + * `getContentLength()` 返回报文体的字节长度 + * `cache()` 缓存报文体,开启缓存后可重复使用`toXXX()`类方法 + * `close()` 关闭报文体,未对报文体做任何消费时使用,比如只读取报文头 +* `getError()` 执行中发生的异常,自动捕获执行请求是发生的 网络超时、网络错误 和 其它请求异常 +* `close()` 关闭报文,未对报文体做任何消费时使用,比如只读取长度 + +  示例,请求结果自动转Bean和List: + +```java +// 自动转Bean +Order order = http.sync("/orders/1") + .get().getBody().toBean(Order.class); + +// 自动转List +List orders = http.sync("/orders") + .get().getBody().toList(Order.class); +``` + +  示例,下载文件到指定目录: + +```java +String path = "D:/reports/2020-03-01.xlsx"; // 文件保存目录 + +// 同步下载 +http.sync("/reports/2020-03-01.xlsx") + .get().getBody().toFile(path); + +// 异步下载 +http.async("/reports/2020-03-01.xlsx") + .setOnResponse((HttpResult result) -> { + result.getBody().toFile(path); + }) + .get(); +``` + +#### 3.3 HttpCall + +  `HttpCall`对象是异步请求方法(`get`、`post`、`put`、`delete`)的返回值,与`java`的`Future`接口很像,它有如下方法: + +* `cancel()` 取消本次请求,返回取消结果 +* `isCanceled()` 返回请求是否被取消 +* `isDone()` 返回是否执行完成,包含取消和失败 +* `getResult()` 返回执行结果`HttpResult`对象,若请求未执行完,则挂起当前线程直到执行完成再返回 + +  取消一个异步请求示例: + +```java +HttpCall call = http.async("/users/1").get(); + +System.out.println(call.isCanceled()); // false + +boolean success = call.cancel(); // 取消请求 + +System.out.println(success); // true +System.out.println(call.isCanceled()); // true +``` + +### 4 构建HTTP任务 + +  `HTTP`对象的`sync`与`async`方法返回一个`HttpTask`对象,该对象提供了可链式调用的`addXXX`与`setXXX`系列方法用于构建任务本身。 + +* `addHeader(String name, String value)` 添加请求头 +* `addHeader(Map headers)` 添加请求头 + +* `addPathParam(String name, Object value)` 添加路径参数:替换URL里的{name}占位符 +* `addPathParam(Map params)` 添加路径参数:替换URL里的{name}占位符 + +* `addUrlParam(String name, Object value)` 添加URL参数:拼接在URL的?之后(查询参数) +* `addUrlParam(Map params)` 添加URL参数:拼接在URL的?之后(查询参数) + +* `addBodyParam(String name, Object value)` 添加Body参数:以表单key=value&的形式放在报文体内(表单参数) +* `addBodyParam(Map params)` 添加Body参数:以表单key=value&的形式放在报文体内(表单参数) + +* `addJsonParam(String name, Object value)` 添加Json参数:请求体为Json(支持多层结构) +* `addJsonParam(Map params)` 添加Json参数:请求体为Json(支持多层结构) + +* `setRequestJson(Object json)` 设置请求体的Json字符串 或待转换为 Json的 JavaBean +* `setRequestJson(Object bean, String dateFormat)` 设置请求体的Json字符串 或待转换为 Json的 JavaBean + +* `addFileParam(String name, String filePath)` 上传文件 +* `addFileParam(String name, File file)` 上传文件 +* `addFileParam(String name, String type, InputStream inputStream)` 上传文件 +* `addFileParam(String name, String type, String fileName, InputStream input)` 上传文件 +* `addFileParam(String name, String type, byte[] content)` 上传文件 +* `addFileParam(String name, String type, String fileName, byte[] content)` 上传文件 + +* `setTag(String tag)` 为HTTP任务添加标签 +* `setRange(long rangeStart)` 设置Range头信息,用于断点续传 +* `setRange(long rangeStart, long rangeEnd)` 设置Range头信息,用于分块下载 +* `setRange(long rangeStart, long rangeEnd)` 设置Range头信息,用于分块下载 + +* `bind(Object object)` 绑定一个对象,可用于实现Android里的生命周期绑定 + +### 5 使用标签 + +  有时候我们想对HTTP任务加以分类,这时候可以使用标签功能: + +```java +http.async("/users") //(1) + .setTag("A").get(); + +http.async("/users") //(2) + .setTag("A.B").get(); + +http.async("/users") //(3) + .setTag("B").get(); + +http.async("/users") //(4) + .setTag("B.C").get(); + +http.async("/users") //(5) + .setTag("C").get(); +``` +  当使用标签后,就可以按标签批量的对HTTP任务进行取消: + +```java +int count = http.cancel("B"); //(2)(3)(4)被取消(取消标签包含"B"的任务) +System.out.println(count); // 输出 3 +``` +  同样的,只有异步HTTP任务才可以被取消。标签除了可以用来取消任务,在预处理器中它也可以发挥作用,请参见[并行预处理器](#54-并行预处理器)与[串行预处理器](#55-串行预处理器)。 + +### 6 配置 HTTP + +#### 6.1 设置 BaseUrl + +```java +HTTP http = HTTP.builder() + .baseUrl("http://api.demo.com") // 设置 BaseUrl + .build(); +``` +  该配置全局生效,在配置了`BaseUrl`之后,具体的请求便可以省略`BaseUrl`部分,使得代码更加简洁,例如: + +```java +http.sync("/users").get() // http://api.demo.com/users + +http.sync("/auth/signin") // http://api.demo.com/auth/signin + .addBodyParam("username", "Jackson") + .addBodyParam("password", "xxxxxx") + .post() // POST请求 +``` +  在配置了`BaseUrl`之后,如有特殊请求任务,仍然可以使用全路径的方式,一点都不妨碍: + +```java +http.sync("https://www.baidu.com").get() +``` + +#### 6.2 回调执行器 + +  如何想改变执行回调函数的线程时,可以配置回调执行器。例如在Android里,让所有的回调函数都在UI线程执行,则可以在构建`HTTP`时配置如下: + +```java +HTTP http = HTTP.builder() + .callbackExecutor((Runnable run) -> { + runOnUiThread(run); // 在UI线程执行 + }) + .build(); +``` +  该配置影响所有回调。 + +#### 6.3 配置 OkHttpClient + +  与其他封装 OkHttp 的框架不同,HttpUtils 并不会遮蔽 OkHttp 本身就很好用的功能,如下: + +```java +HTTP http = HTTP.builder() + .config((Builder builder) -> { + // 配置连接池 最小10个连接(不配置默认为 5) + builder.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES)); + // 配置连接超时时间(默认10秒) + builder.connectTimeout(20, TimeUnit.SECONDS); + // 配置拦截器 + builder.addInterceptor((Chain chain) -> { + Request request = chain.request(); + // 必须同步返回,拦截器内无法执行异步操作 + return chain.proceed(request); + }); + // 其它配置: SSL、缓存、代理、事件监听... + }) + .build(); +``` + +#### 6.4 并行预处理器 + +  预处理器(`Preprocessor`)可以让我们在请求发出之前对请求本身做一些改变,但与`OkHttp`的拦截器(`Interceptor`)不同:预处理器可以让我们**异步**处理这些问题。 + +  例如,当我们想为请求任务自动添加`Token`头信息,而`Token`只能通过异步方法`requestToken`获取时,这时使用`Interceptor`就很难处理了,但可以使用预处理器轻松解决: + +```java +HTTP http = HTTP.builder() + .addPreprocessor((PreChain chain) -> { + HttpTask task = chain.getTask();// 获得当前的HTTP任务 + if (!task.isTagged("Auth")) { // 根据标签判断该任务是否需要Token + return; + } + requestToken((String token) -> { // 异步获取 Token + task.addHeader("Token", token);// 为任务添加头信息 + chain.proceed(); // 继续当前的任务 + }); + }) + .build(); +``` +  和`Interceptor`一样,`Preprocessor`也可以添加多个。 + +#### 6.5 串行预处理器 + +  普通预处理器都是可并行处理的,然而有时我们希望某个预处理器同时只处理一个任务。比如 当`Token`过期时我们需要去刷新获取新`Token`,而刷新`Token`这个操作只能有一个任务去执行,因为如果`n`个任务同时执行的话,那么必有`n-1`个任务刚刷新得到的`Token`可能就立马失效了,而这是我们所不希望的。 + +  为了解决这个问题,HttpUtils 提供了串行预处理器,它可以让 HTTP 任务排好队,一个一个地进入预处理器: + +```java +HTTP http = HTTP.builder() + .addSerialPreprocessor((PreChain chain) -> { + HttpTask task = chain.getTask(); + if (!task.isTagged("Auth")) { + return; + } + // 检查过期,若需要则刷新Token + requestTokenAndRefreshIfExpired((String token) -> { + task.addHeader("Token", token); + chain.proceed(); // 调用此方法前,不会有其它任务进入该处理器 + }); + }) + .build(); +``` +  串行预处理器实现了让HTTP任务排队串行处理的功能,但值得一提的是:它并没有因此而阻塞任何线程! + +#### 6.6 全局回调处理 + +```java +HTTP http = HTTP.builder() + .responseListener((HttpTask task, HttpResult result) -> { + // 所有请求响应后都会走这里 + + return true; // 返回 true 表示继续执行 task 的 OnResponse 回调,false 表示不再执行 + }) + .completeListener((HttpTask task, State state) -> { + // 所有请求执行完都会走这里 + + return true; // 返回 true 表示继续执行 task 的 OnComplete 回调,false 表示不再执行 + }) + .exceptionListener((HttpTask task, IOException error) -> { + // 所有请求发生异常都会走这里 + + return true; // 返回 true 表示继续执行 task 的 OnException 回调,false 表示不再执行 + }) + .build(); +``` + +#### 6.7 全局下载监听 + +```java +HTTP http = HTTP.builder() + .downloadListener((HttpTask task, Download download) -> { + // 所有下载在开始之前都会先走这里 + Ctrl ctrl = download.getCtrl(); // 下载控制器 + + }) + .build(); +``` + +### 7 使用 HttpUtils 类 + +  类`HttpUtils`本是 1.x 版本里的最重要的核心类,由于在 2.x 版本里抽象出了`HTTP`接口,使得它的重要性已不如往昔。但合理的使用它,仍然可以带来不少便利,特别是在没有IOC容器的环境里,比如在Android开发和一些工具项目的开发中。 + +  类`HttpUtils`共定义了四个静态方法: + +* `async(String url)` 开始一个异步请求 (内容通过一个`HTTP`单例实现) +* `sync(String url)` 开始一个同步请求 (内容通过一个`HTTP`单例实现) +* `cancel(String tag)` 按标签取消请求(内容通过一个`HTTP`单例实现) +* `of(HTTP http)` 配置`HttpUtils`持有的`HTTP`实例(不调用此方法前默认使用一个没有没有经过任何配置的`HTTP`懒实例) + +  也就是说,能使用`http`实例的地方,都可以使用`HttpUtils`类,例如: + +```java +// 在配置HTTP实例之前,只能使用全路径方式 +List roles = HttpUtils.sync("http://api.demo.com/roles") + .get().getBody().toList(Role.class); + +// 配置HTTP实例,全局生效 +HttpUtils.of(HTTP.builder() + .baseUrl("http://api.demo.com") + .build()); + +// 内部使用新的HTTP实例 +List users = HttpUtils.sync("/users") + .get().getBody().toList(User.class); +``` + +### 8 文件下载 + +  HttpUtils 并没有把文件的下载排除在常规的请求之外,同一套API,它优雅的设计使得下载与常规请求融合的毫无违和感,一个最简单的示例: + +```java +http.sync("/download/test.zip") + .get() // 使用 GET 方法(其它方法也可以,看服务器支持) + .getBody() // 得到报文体 + .toFile("D:/download/test.zip") // 指定下载的目录,文件名将根据下载信息自动生成 + .start(); // 启动下载 +``` +  或使用异步连接方式: + +```java +http.async("/download/test.zip") + .setOnResponse((HttpResult result) -> { + result.getBody().toFile("D:/download/test.zip").start(); + }) + .get(); +``` +  这里要说明一下:`sync`与`async`的区别在于连接服务器并得到响应这个过程的同步与异步(这个过程的耗时在大文件下载中占比极小),而`start`方法启动的下载过程则都是异步的。 + +#### 8.1 下载进度监听 + +  就直接上代码啦,诸君一看便懂: + +```java +http.sync("/download/test.zip") + .get() + .getBody() + .setStepBytes(1024) // 设置每接收 1024 个字节执行一次进度回调(不设置默认为 8192) + // .setStepRate(0.01) // 设置每接收 1% 执行一次进度回调(不设置以 StepBytes 为准) + .setOnProcess((Process process) -> { // 下载进度回调 + long doneBytes = process.getDoneBytes(); // 已下载字节数 + long totalBytes = process.getTotalBytes(); // 总共的字节数 + double rate = process.getRate(); // 已下载的比例 + boolean isDone = process.isDone(); // 是否下载完成 + }) + .toFolder("D:/download/") // 指定下载的目录,文件名将根据下载信息自动生成 + // .toFile("D:/download/test.zip") // 指定下载的路径,若文件已存在则覆盖 + .setOnSuccess((File file) -> { // 下载成功回调 + + }) + .start(); +``` +  值得一提的是:由于 HttpUtils 并没有把下载做的很特别,这里设置的进度回调不只对下载文件起用作,即使对响应JSON的常规请求,只要设置了进度回调,它也会告诉你报文接收的进度(提前是服务器响应的报文有`Content-Length`头),例如: + +```java +List users = http.sync("/users") + .get() + .getBody() + .setStepBytes(2) + .setOnProcess((Process process) -> { + System.out.println(process.getRate()); + }) + .toList(User.class); +``` + +#### 8.2 下载过程控制 + +  过于简单:还是直接上代码: + +```java +Ctrl ctrl = http.sync("/download/test.zip") + .get() + .getBody() + .setOnProcess((Process process) -> { + System.out.println(process.getRate()); + }) + .toFolder("D:/download/") + .start(); // 该方法返回一个下载过程控制器 + +ctrl.status(); // 下载状态 +ctrl.pause(); // 暂停下载 +ctrl.resume(); // 恢复下载 +ctrl.cancel(); // 取消下载(同时会删除文件,不可恢复) +``` +  无论是同步还是异步发起的下载请求,都可以做以上的控制: + +```java +http.async("/download/test.zip") + .setOnResponse((HttpResult result) -> { + // 拿到下载控制器 + Ctrl ctrl = result.getBody().toFolder("D:/download/").start(); + }) + .get(); +``` + +#### 8.3 实现断点续传 + +  HttpUtils 对断点续传并没有再做更高层次的封装,因为这是app该去做的事情,它在设计上使各种网络问题的处理变简单的同时力求纯粹。下面的例子可以看到,HttpUtils 通过一个失败回调拿到**断点**,便将复杂的问题变得简单: + +```java +http.sync("/download/test.zip") + .get() + .getBody() + .toFolder("D:/download/") + .setOnFailure((Failure failure) -> { // 下载失败回调,以便接收诸如网络错误等失败信息 + IOException e = failure.getException(); // 具体的异常信息 + long doneBytes = failure.getDoneBytes(); // 已下载的字节数(断点),需要保存,用于断点续传 + File file = failure.getFile(); // 下载生成的文件,需要保存 ,用于断点续传(只保存路径也可以) + }) + .start(); +``` +  下面代码实现续传: + +```java +long doneBytes = ... // 拿到保存的断点 +File file = ... // 待续传的文件 + +http.sync("/download/test.zip") + .setRange(doneBytes) // 设置断点(已下载的字节数) + .get() + .getBody() + .toFile(file) // 下载到同一个文件里 + .setAppended() // 开启文件追加模式 + .setOnSuccess((File file) -> { + + }) + .setOnFailure((Failure failure) -> { + + }) + .start(); +``` + +#### 8.4 实现分块下载 + +  当文件很大时,有时候我们会考虑分块下载,与断点续传的思路是一样的,示例代码: + +```java +static String url = "http://api.demo.com/download/test.zip" + +public static void main(String[] args) { + long totalSize = HttpUtils.sync(url).get().getBody() + .close() // 因为这次请求只是为了获得文件大小,不消费报文体,所以直接关闭 + .getContentLength(); // 获得待下载文件的大小(由于未消费报文体,所以该请求不会消耗下载报文体的时间和网络流量) + download(totalSize, 0); // 从第 0 块开始下载 + sleep(50000); // 等待下载完成(不然本例的主线程就结束啦) +} + +static void download(long totalSize, int index) { + long size = 3 * 1024 * 1024; // 每块下载 3M + long start = index * size; + long end = Math.min(start + size, totalSize); + HttpUtils.sync(url) + .setRange(start, end) // 设置本次下载的范围 + .get().getBody() + .toFile("D:/download/test.zip") // 下载到同一个文件里 + .setAppended() // 开启文件追加模式 + .setOnSuccess((File file) -> { + if (end < totalSize) { // 若未下载完,则继续下载下一块 + download(totalSize, index + 1); + } else { + System.out.println("下载完成"); + } + }) + .start(); +} +``` + +### 9 文件上传 + +  一个简单文件上传的示例: + +```java +http.sync("/upload") + .addFileParam("test", "D:/download/test.zip") + .post() // 上传发法一般使用 POST 或 PUT,看服务器支持 +``` +  异步上传也是完全一样: + +```java +http.async("/upload") + .addFileParam("test", "D:/download/test.zip") + .post() +``` + +#### 9.1 上传进度监听 + +  HttpUtils 的上传进度监听,监听的是所有请求报文体的发送进度,示例代码: + +```java +http.sync("/upload") + .addBodyParam("name", "Jack") + .addBodyParam("age", 20) + .addFileParam("avatar", "D:/image/avatar.jpg") + .setStepBytes(1024) // 设置每发送 1024 个字节执行一次进度回调(不设置默认为 8192) + // .setStepRate(0.01) // 设置每发送 1% 执行一次进度回调(不设置以 StepBytes 为准) + .setOnProcess((Process process) -> { // 上传进度回调 + long doneBytes = process.getDoneBytes(); // 已发送字节数 + long totalBytes = process.getTotalBytes(); // 总共的字节数 + double rate = process.getRate(); // 已发送的比例 + boolean isDone = process.isDone(); // 是否发送完成 + }) + .post() +``` +  咦!怎么感觉和下载的进度回调的一样?没错!HttpUtils 还是使用同一套API处理上传和下载的进度回调,区别只在于上传是在`get/post`方法之前使用这些API,下载是在`getBody`方法之后使用。很好理解:`get/post`之前是准备发送请求时段,有上传的含义,而`getBody`之后,已是报文响应的时段,当然是下载。 + +#### 9.2 上传过程控制 + +  上传文件的过程控制就很简单,和常规请求一样,只有异步发起的上传可以取消: + +```java +HttpCall call = http.async("/upload") + .addFileParam("test", "D:/download/test.zip") + .setOnProcess((Process process) -> { + System.out.println(process.getRate()); + }) + .post() + +call.cancel(); // 取消上传 +``` +  上传就没有暂停和继续这个功能啦,应该没人有这个需求吧? + +### 10 执行线程自由切换(for Android) + +  在 Android 开发中,经常会把某些代码放到特点的线程去执行,比如网络请求响应后的页面更新在主线程(UI线程)执行,而保存文件则在IO线程操作。HttpUtils 为这类问题提供了良好的方案。 + +  在 **默认** 情况下,**所有回调** 函数都会 **在 IO 线程** 执行。为什么会设计如此呢?这是因为 HttpUtils 只是纯粹的 Java 领域 Http工具包,本身对 Android 不会有任何依赖,因此也不知 Android 的 UI 线程为何物。这么设计也让它在 Android 之外有更多的可能性。 + +  但是在 Android 里使用 HttpUtils 的话,UI线程的问题能否优雅的解决呢?当然可以!简单粗暴的方法就是配置一个 回调执行器: + + ```java +HTTP http = HTTP.builder() + .callbackExecutor((Runnable run) -> { + // 实际编码中可以吧 Handler 提出来,不需要每次执行回调都重新创建 + new Handler(Looper.getMainLooper()).post(run); // 在主线程执行 + }) + .build(); +``` +  上述代码便实现了让 **所有** 的 **回调函数** 都在 **主线程** 执行的目的,如: + +```java +http.async("/users") + .addBodyParam("name", "Jack") + .setOnProcess((Process process) -> { + // 在主线程执行 + }) + .setOnResponse((HttpResult result) -> { + // 在主线程执行 + }) + .setOnException((Exception e) -> { + // 在主线程执行 + }) + .setOnComplete((State state) -> { + // 在主线程执行 + }) + .post(); +``` +  但是,如果同时还想让某些回调放在IO线程,实现 **自由切换**,怎么办呢?HttpUtils 给出了非常灵活的方法,如下: + +```java +http.async("/users") + .addBodyParam("name", "Jack") + .setOnProcess((Process process) -> { + // 在主线程执行 + }) + .nextOnIO() // 指定下一个回调在 IO 线程执行 + .setOnResponse((HttpResult result) -> { + // 在 IO 线程执行 + }) + .setOnException((Exception e) -> { + // 在主线程执行(没有指明 nextOnIO 则在回调执行器里执行) + }) + .nextOnIO() // 指定下一个回调在 IO 线程执行 + .setOnComplete((State state) -> { + // 在 IO 线程执行 + }) + .post(); +``` +  无论是哪一个回调,都可以使用`nextOnIO()`方法自由切换。同样,对于文件下载也是一样: + +```java +http.sync("/download/test.zip") + .get() + .getBody() + .setOnProcess((Process process) -> { + // 在主线程执行 + }) + .toFolder("D:/download/") + .nextOnIO() // 指定下一个回调在 IO 线程执行 + .setOnSuccess((File file) -> { + // 在 IO 线程执行 + }) + .setOnFailure((Failure failure) -> { + // 在主线程执行 + }) + .start(); +``` + +## 参考文档 + + + +## 联系方式 + +* 邮箱:zhou.xu@ejlchina.com + +## 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request - - -#### 码云特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目 -5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..f434d4c5 --- /dev/null +++ b/pom.xml @@ -0,0 +1,169 @@ + + 4.0.0 + + com.ejlchina + httputils + 2.3.0 + HttpUtils + + + 1.8 + UTF-8 + https://oss.sonatype.org/content/repositories/snapshots/ + + + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + Troy + zhou.xu@ejlchina.com + ejlchina + http://www.ejlchina.com + + + + + scm:git://gitee.com/ejlchina-zhxu/httputils.git + scm:http://gitee.com/ejlchina-zhxu/httputils + https://gitee.com/ejlchina-zhxu/httputils + v2.2.0 + + + + + + junit + junit + 4.12 + test + + + + com.alibaba + fastjson + 1.2.67 + + + + com.squareup.okhttp3 + okhttp + 3.14.7 + + + + + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + + + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + ${sonatypeOssDistMgmtSnapshotsUrl} + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + httputils + + + + org.apache.maven.plugins + maven-release-plugin + 2.1 + + forked-path + false + -Psonatype-oss-release + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java-version} + ${java-version} + ${project.build.sourceEncoding} + true + + + + + org.apache.maven.plugins + maven-source-plugin + 2.1.2 + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.7 + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.1 + + + sign-artifacts + verify + + sign + + + + + + + + + https://gitee.com/ejlchina-zhxu/httputils + Http工具包,封装 OkHttp,链式用法、支持异步同步、响应JSON自动解析,回调线程切换、配置灵活、支持URL占位符、支持BaseUrl、外部依赖少、适用 JavaWeb 和 Android,教程全面、简单易用 + + 江苏一键联信息科技有限公司 + http://www.ejlchina.com/ + + + https://gitee.com/ejlchina-zhxu/httputils/issues + + \ No newline at end of file diff --git a/src/main/java/com/ejlchina/http/Configurator.java b/src/main/java/com/ejlchina/http/Configurator.java new file mode 100644 index 00000000..c76a5a85 --- /dev/null +++ b/src/main/java/com/ejlchina/http/Configurator.java @@ -0,0 +1,17 @@ +package com.ejlchina.http; + +import okhttp3.OkHttpClient.Builder; + +/** + * Http 配置器 + * + */ +public interface Configurator { + + /** + * 使用 builder 配置 HttpClient + * @param builder OkHttpClient 构建器 + */ + void config(Builder builder); + +} diff --git a/src/main/java/com/ejlchina/http/DownListener.java b/src/main/java/com/ejlchina/http/DownListener.java new file mode 100644 index 00000000..7802013a --- /dev/null +++ b/src/main/java/com/ejlchina/http/DownListener.java @@ -0,0 +1,19 @@ +package com.ejlchina.http; + +/** + * 下载监听接口 + * @author 15735 + * @since 2.3.0 + */ +public interface DownListener { + + + /** + * 全局下载监听 + * @param task 所属的 HttpTask + * @param download 下载事件 + */ + void listen(HttpTask task, Download download); + + +} diff --git a/src/main/java/com/ejlchina/http/Download.java b/src/main/java/com/ejlchina/http/Download.java new file mode 100644 index 00000000..c7ef89bd --- /dev/null +++ b/src/main/java/com/ejlchina/http/Download.java @@ -0,0 +1,307 @@ +package com.ejlchina.http; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; + +import com.ejlchina.http.internal.HttpException; +import com.ejlchina.http.internal.TaskExecutor; + +import okhttp3.internal.Util; + +/** + * 文件下载 + * @author Troy.Zhou + * @since 2.2.0 + */ +public class Download { + + private File file; + private InputStream input; + private OnCallback onSuccess; + private OnCallback onFailure; + private TaskExecutor taskExecutor; + private long doneBytes; + private int buffSize = 0; + private long seekBytes = 0; + private boolean appended; + private volatile int status; + private Object lock = new Object(); + + protected boolean nextOnIO = false; + private boolean sOnIO; + private boolean fOnIO; + + private Ctrl ctrl; + + public Download(File file, InputStream input, TaskExecutor taskExecutor, long skipBytes) { + this.file = file; + this.input = input; + this.taskExecutor = taskExecutor; + this.seekBytes = skipBytes; + this.ctrl = new Ctrl(); + } + + /** + * 设置缓冲区大小,默认 2K(2048) + * @param buffSize 缓冲区大小(单位:字节) + * @return Download + */ + public Download setBuffSize(int buffSize) { + if (buffSize > 0) { + this.buffSize = buffSize; + } + return this; + } + + /** + * 设置文件追加模式 + * 用预断点续传和分块下载 + * @return Download + */ + public Download setAppended() { + this.appended = true; + return this; + } + + /** + * 设置文件指针,从文件的 seekBytes 位置追加内容 + * @param seekBytes 跨越的字节数 + * @return Download + */ + public Download setFilePointer(long seekBytes) { + this.seekBytes = seekBytes; + return this; + } + + /** + * 在IO线程执行 + * @return Download + */ + public Download nextOnIO() { + nextOnIO = true; + return this; + } + + /** + * 设置下载成功回调 + * @param onSuccess 成功回调函数 + * @return Download + */ + public Download setOnSuccess(OnCallback onSuccess) { + this.onSuccess = onSuccess; + sOnIO = nextOnIO; + nextOnIO = false; + return this; + } + + /** + * 设置下载失败回调 + * @param onFailure 失败回调函数 + * @return Download + */ + public Download setOnFailure(OnCallback onFailure) { + this.onFailure = onFailure; + fOnIO = nextOnIO; + nextOnIO = false; + return this; + } + + /** + * 开始下载 + * @return 下载控制器 + */ + public Ctrl start() { + if (buffSize == 0) { + buffSize = Process.DEFAULT_STEP_BYTES; + } + RandomAccessFile raFile = randomAccessFile(); + status = Ctrl.STATUS__DOWNLOADING; + taskExecutor.execute(() -> { + doDownload(raFile); + }, true); + return ctrl; + } + + /** + * 获取下载控制器 + * @return Ctrl + */ + public Ctrl getCtrl() { + return ctrl; + } + + public class Ctrl { + + /** + * 已取消 + */ + public static final int STATUS__CANCELED = -1; + + /** + * 下载中 + */ + public static final int STATUS__DOWNLOADING = 1; + + /** + * 已暂停 + */ + public static final int STATUS__PAUSED = 2; + + /** + * 已完成 + */ + public static final int STATUS__DONE = 3; + + /** + * 错误 + */ + public static final int STATUS__ERROR = 4; + + /** + * @see #STATUS__CANCELED + * @see #STATUS__DOWNLOADING + * @see #STATUS__PAUSED + * @see #STATUS__DONE + * @return 下载状态 + */ + public int status() { + return status; + } + + /** + * 暂停下载任务 + */ + public void pause() { + synchronized (lock) { + if (status == STATUS__DOWNLOADING) { + status = STATUS__PAUSED; + } + } + } + + /** + * 继续下载任务 + */ + public void resume() { + synchronized (lock) { + if (status == STATUS__PAUSED) { + status = STATUS__DOWNLOADING; + } + } + } + + /** + * 取消下载任务 + */ + public void cancel() { + synchronized (lock) { + if (status == STATUS__PAUSED || status == STATUS__DOWNLOADING) { + status = STATUS__CANCELED; + } + } + } + + } + + public class Failure { + + private IOException exception; + + Failure(IOException exception) { + this.exception = exception; + } + + /** + * @return 下载文件 + */ + public File getFile() { + return file; + } + + /** + * @return 已下载字节数 + */ + public long getDoneBytes() { + return doneBytes; + } + + /** + * @return 异常信息 + */ + public IOException getException() { + return exception; + } + + } + + private RandomAccessFile randomAccessFile() { + try { + return new RandomAccessFile(file, "rw"); + } catch (FileNotFoundException e) { + status = Ctrl.STATUS__ERROR; + Util.closeQuietly(input); + throw new HttpException("无法获取文件[" + file.getAbsolutePath() + "]的输入流", e); + } + } + + private void doDownload(RandomAccessFile raFile) { + try { + if (appended && seekBytes > 0) { + long length = raFile.length(); + if (seekBytes <= length) { + raFile.seek(seekBytes); + doneBytes = seekBytes; + } else { + raFile.seek(length); + doneBytes = length; + } + } + while (status != Ctrl.STATUS__CANCELED && status != Ctrl.STATUS__DONE) { + if (status == Ctrl.STATUS__DOWNLOADING) { + byte[] buff = new byte[buffSize]; + int len = -1; + while ((len = input.read(buff)) != -1) { + raFile.write(buff, 0, len); + doneBytes += len; + if (status == Ctrl.STATUS__CANCELED + || status == Ctrl.STATUS__PAUSED) { + break; + } + } + if (len == -1) { + synchronized (lock) { + status = Ctrl.STATUS__DONE; + } + } + } + } + } catch (IOException e) { + synchronized (lock) { + status = Ctrl.STATUS__ERROR; + } + if (onFailure != null) { + taskExecutor.execute(() -> { + onFailure.on(new Failure(e)); + }, fOnIO); + } else { + throw new HttpException("流传输失败", e); + } + } finally { + Util.closeQuietly(raFile); + Util.closeQuietly(input); + if (status == Ctrl.STATUS__CANCELED) { + file.delete(); + } + } + if (status == Ctrl.STATUS__DONE + && onSuccess != null) { + taskExecutor.execute(() -> { + onSuccess.on(file); + }, sOnIO); + } + } + +} diff --git a/src/main/java/com/ejlchina/http/HTTP.java b/src/main/java/com/ejlchina/http/HTTP.java new file mode 100644 index 00000000..b8fc7c4b --- /dev/null +++ b/src/main/java/com/ejlchina/http/HTTP.java @@ -0,0 +1,70 @@ +package com.ejlchina.http; + +import com.ejlchina.http.internal.AsyncHttpTask; +import com.ejlchina.http.internal.SyncHttpTask; +import com.ejlchina.http.internal.TaskExecutor; +import com.ejlchina.http.internal.HttpClient.Builder; + +import okhttp3.Call; +import okhttp3.Request; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +/** + * HTTP 客户端接口 + * @author 15735 + * + */ +public interface HTTP { + + /** + * 异步请求 + * @param url 请求地址 + * @return 异步HTTP任务 + */ + AsyncHttpTask async(String url); + + /** + * 同步请求 + * @param url 请求地址 + * @return 同步HTTP任务 + */ + SyncHttpTask sync(String url); + + /** + * 根据标签取消HTTP任务,只要任务的标签包含指定的Tag就会被取消 + * @param tag 标签 + * @return 被取消的任务数量 + */ + int cancel(String tag); + + /** + * OkHttp 原生请求 (该请求不经过 预处理器) + * @param request 请求 + * @return Call + */ + Call request(Request request); + + /** + * Websocket(该请求不经过 预处理器) + * @param request 请求 + * @param listener 监听器 + * @return + */ + WebSocket webSocket(Request request, WebSocketListener listener); + + /** + * 获取任务执行器 + * @return TaskExecutor + */ + TaskExecutor getExecutor(); + + /** + * HTTP 构建器 + * @return HTTP 构建器 + */ + static Builder builder() { + return new Builder(); + } + +} diff --git a/src/main/java/com/ejlchina/http/HttpCall.java b/src/main/java/com/ejlchina/http/HttpCall.java new file mode 100644 index 00000000..940d5749 --- /dev/null +++ b/src/main/java/com/ejlchina/http/HttpCall.java @@ -0,0 +1,28 @@ +package com.ejlchina.http; + +public interface HttpCall { + + /** + * 取消 Http 请求 + * @return 是否取消成功 + */ + boolean cancel(); + + /** + * @return 请求是否被取消 + */ + boolean isCanceled(); + + /** + * @return 请求是否执行完成,包含取消和失败 + */ + boolean isDone(); + + /** + * Waits if necessary for the computation to complete, and then + * retrieves its result + * @return 请求执行结果,若请求未执行完,则阻塞当前线程直到请求执行完成 + */ + HttpResult getResult(); + +} diff --git a/src/main/java/com/ejlchina/http/HttpResult.java b/src/main/java/com/ejlchina/http/HttpResult.java new file mode 100644 index 00000000..f1d67b8a --- /dev/null +++ b/src/main/java/com/ejlchina/http/HttpResult.java @@ -0,0 +1,282 @@ +package com.ejlchina.http; + +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.util.List; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ejlchina.http.internal.RealHttpResult; +import com.ejlchina.http.internal.TaskExecutor; + +import okhttp3.Headers; +import okhttp3.MediaType; +import okhttp3.Response; + + +/** + * Http 执行结果 + */ +public interface HttpResult { + + /** + * 构造一个 HttpResult + * 此方法构造的 HttpResult 不可设置进度回调,不可进行下载操作! + * 若需要,请使用方法: {@link #of(Response, TaskExecutor)} + * @param response Response + * @return HttpResult + */ + static HttpResult of(Response response) { + return of(response, null); + } + + /** + * 构造一个 HttpResult + * @param response Response + * @param taskExecutor 任务执行器, 可通过方法 {@link HTTP#getExecutor()} 获得 + * @return HttpResult + */ + static HttpResult of(Response response, TaskExecutor taskExecutor) { + if (response != null) { + return new RealHttpResult(null, response, taskExecutor); + } + throw new IllegalArgumentException("response 不能为空"); + } + + /** + * 获取所属的 Http请求任务 + * @return HttpTask + */ + HttpTask getTask(); + + + public enum State { + + /** + * 执行异常 + */ + EXCEPTION, + + /** + * 请求被取消 + */ + CANCELED, + + /** + * 请求已响应 + */ + RESPONSED, + + /** + * 网络超时 + */ + TIMEOUT, + + /** + * 网络出错 + */ + NETWORK_ERROR + + } + + /** + * HTTP响应报文体 + */ + public interface Body { + + /** + * @return 媒体类型 + */ + MediaType getContentType(); + + /** + * @return 报文体字节长度 + */ + long getContentLength(); + + /** + * 在IO线程执行 + * @return Body + */ + Body nextOnIO(); + + /** + * 设置报文体接收进度回调 + * @param onProcess 进度回调函数 + * @return Body + */ + Body setOnProcess(OnCallback onProcess); + + /** + * 设置进度回调的步进字节,默认 8K(8192) + * 表示每接收 stepBytes 个字节,执行一次进度回调 + * @param stepBytes 步进字节 + * @return Body + */ + Body setStepBytes(long stepBytes); + + /** + * 设置进度回调的步进比例 + * 表示每接收 stepRate 比例,执行一次进度回调 + * @param stepRate 步进比例 + * @return Body + */ + Body setStepRate(double stepRate); + + /** + * 设置进度回调忽略响应的Range头信息,即进度回调会从0开始 + * @return Body + */ + Body setRangeIgnored(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转字节流 + */ + InputStream toByteStream(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转字节数组 + */ + byte[] toBytes(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转字符流 + */ + Reader toCharStream(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转字符串 + */ + String toString(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转Json对象 + */ + JSONObject toJsonObject(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转Json数组 + */ + JSONArray toJsonArray(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param 目标泛型 + * @param type 目标类型 + * @return 报文体Json文本转JavaBean + */ + T toBean(Class type); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param 目标泛型 + * @param type 目标类型 + * @return 报文体Json文本转JavaBean列表 + */ + List toList(Class type); + + /** + * 下载到指定路径 + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param filePath 目标路径 + * @return 下载过程 #Download + */ + Download toFile(String filePath); + + /** + * 下载到指定文件 + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param file 目标文件 + * @return 下载过程 #Download + */ + Download toFile(File file); + + /** + * 下载到指定文件夹 + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param dirPath 目标目录 + * @return 下载过程 #Download + */ + Download toFolder(String dirPath); + + /** + * 下载到指定文件夹 + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param dir 目标目录 + * @return 下载过程 #Download + */ + Download toFolder(File dir); + + /** + * 缓存自己,缓存后可 重复使用 toXXX 类方法 + * @return Body + */ + Body cache(); + + /** + * 关闭报文体 + * 未对报文体做任何消费时使用,比如只读取长度 + * @return Body + */ + Body close(); + + } + + + /** + * @return 执行状态 + */ + State getState(); + + /** + * @return HTTP状态码 + */ + int getStatus(); + + /** + * @return 是否响应成功,状态码在 [200..300) 之间 + */ + boolean isSuccessful(); + + /** + * @return 响应头 + */ + Headers getHeaders(); + + /** + * @param name 头名称 + * @return 响应头 + */ + List getHeaders(String name); + + /** + * @param name 头名称 + * @return 响应头 + */ + String getHeader(String name); + + /** + * @return 响应报文体 + */ + Body getBody(); + + /** + * @return 执行中发生的异常 + */ + Exception getError(); + + /** + * 关闭报文 + * 未对报文体做任何消费时使用,比如只读取报文头 + * @return HttpResult + */ + HttpResult close(); + +} diff --git a/src/main/java/com/ejlchina/http/HttpTask.java b/src/main/java/com/ejlchina/http/HttpTask.java new file mode 100644 index 00000000..04ccf72c --- /dev/null +++ b/src/main/java/com/ejlchina/http/HttpTask.java @@ -0,0 +1,710 @@ +package com.ejlchina.http; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.fastjson.JSON; +import com.ejlchina.http.HttpResult.State; +import com.ejlchina.http.internal.HttpClient; +import com.ejlchina.http.internal.HttpException; +import com.ejlchina.http.internal.ProcessRequestBody; + +import okhttp3.Call; +import okhttp3.FormBody; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.internal.Util; +import okio.Buffer; + + +/** + * Created by 周旭(Troy.Zhou) on 2020/3/11. + */ +@SuppressWarnings("unchecked") +public abstract class HttpTask> { + + private static final MediaType TYPE_JSON = MediaType.parse("application/json; charset=utf-8"); + private static String PATH_PARAM_REGEX = "[A-Za-z0-9_\\-/]*\\{[A-Za-z0-9_\\-]+\\}[A-Za-z0-9_\\-/]*"; + + protected HttpClient httpClient; + protected boolean nothrow; + protected String tag; + protected boolean nextOnIO = false; + + private String urlPath; + private Map headers; + private Map pathParams; + private Map urlParams; + private Map bodyParams; + private Map jsonParams; + private Map files; + private String requestJson; + private OnCallback onProcess; + private boolean pOnIO; + private long stepBytes = 0; + private double stepRate = -1; + + private Object object; + + + public HttpTask(HttpClient httpClient, String url) { + this.httpClient = httpClient; + this.urlPath = url; + } + + /** + * 获取请求任务的URL地址 + * @return URL地址 + */ + public String getUrl() { + return urlPath; + } + + /** + * 获取请求任务的标签 + * @return 标签 + */ + public String getTag() { + return tag; + } + + /** + * 标签匹配 + * 判断任务标签与指定的标签是否匹配(包含指定的标签) + * @param tag 标签 + * @return 是否匹配 + */ + public boolean isTagged(String tag) { + if (this.tag != null && tag != null) { + return this.tag.contains(tag); + } + return false; + } + + /** + * 获取请求任务的头信息 + * @return 头信息 + */ + public Map getHeaders() { + return headers; + } + + /** + * 获得被绑定的对象 + * @return Object + */ + public Object getBound() { + return object; + } + + /** + * 设置在发生异常时不向上抛出,设置后: + * 异步请求可以在异常回调内捕获异常,同步请求在返回结果中找到该异常 + * @return HttpTask 实例 + */ + public C nothrow() { + this.nothrow = true; + return (C) this; + } + + /** + * 为请求任务添加标签 + * @param tag 标签 + * @return HttpTask 实例 + */ + public C setTag(String tag) { + this.tag = tag; + return (C) this; + } + + /** + * 下一个回调在IO线程执行 + * @return HttpTask 实例 + */ + public C nextOnIO() { + nextOnIO = true; + return (C) this; + } + + /** + * 绑定一个对象 + * @return HttpTask 实例 + */ + public C bind(Object object) { + this.object = object; + return (C) this; + } + + /** + * 添加请求头 + * @param name 请求头名 + * @param value 请求头值 + * @return HttpTask 实例 + */ + public C addHeader(String name, String value) { + if (name != null && value != null) { + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(name, value); + } + return (C) this; + } + + /** + * 添加请求头 + * @param headers 请求头集合 + * @return HttpTask 实例 + */ + public C addHeader(Map headers) { + if (headers != null) { + if (this.headers == null) { + this.headers = new HashMap<>(); + } + this.headers.putAll(headers); + } + return (C) this; + } + + /** + * 设置Range头信息 + * 表示接收报文体时跳过的字节数,用于断点续传 + * @param rangeStart 表示从 rangeStart 个字节处开始接收,通常是已经下载的字节数,即上次的断点) + * @return HttpTask 实例 + */ + public C setRange(long rangeStart) { + return addHeader("Range", "bytes=" + rangeStart + "-"); + } + + /** + * 设置Range头信息 + * 设置接收报文体时接收的范围,用于分块下载 + * @param rangeStart 表示从 rangeStart 个字节处开始接收 + * @param rangeEnd 表示接收到 rangeEnd 个字节处 + * @return HttpTask 实例 + */ + public C setRange(long rangeStart, long rangeEnd) { + return addHeader("Range", "bytes=" + rangeStart + "-" + rangeEnd); + } + + /** + * 设置报文体发送进度回调 + * @param onProcess 进度回调函数 + * @return HttpTask 实例 + */ + public C setOnProcess(OnCallback onProcess) { + this.onProcess = onProcess; + pOnIO = nextOnIO; + nextOnIO = false; + return (C) this; + } + + /** + * 设置进度回调的步进字节,默认 8K(8192) + * 表示每接收 stepBytes 个字节,执行一次进度回调 + * @param stepBytes 步进字节 + * @return HttpTask 实例 + */ + public C setStepBytes(long stepBytes) { + this.stepBytes = stepBytes; + return (C) this; + } + + /** + * 设置进度回调的步进比例 + * 表示每接收 stepRate 比例,执行一次进度回调 + * @param stepRate 步进比例 + * @return HttpTask 实例 + */ + public C setStepRate(double stepRate) { + this.stepRate = stepRate; + return (C) this; + } + + /** + * 路径参数:替换URL里的{name} + * @param name 参数名 + * @param value 参数值 + * @return HttpTask 实例 + **/ + public C addPathParam(String name, Object value) { + if (name != null && value != null) { + if (pathParams == null) { + pathParams = new HashMap<>(); + } + pathParams.put(name, value.toString()); + } + return (C) this; + } + + /** + * 路径参数:替换URL里的{name} + * @param params 参数集合 + * @return HttpTask 实例 + **/ + public C addPathParam(Map params) { + if (params != null) { + if (pathParams == null) { + pathParams = new HashMap<>(); + } + params.forEach((String name, Object value) -> { + if (name != null && value != null) { + pathParams.put(name, value.toString()); + } + }); + } + return (C) this; + } + + /** + * URL参数:拼接在URL后的参数 + * @param name 参数名 + * @param value 参数值 + * @return HttpTask 实例 + **/ + public C addUrlParam(String name, Object value) { + if (name != null && value != null) { + if (urlParams == null) { + urlParams = new HashMap<>(); + } + urlParams.put(name, value.toString()); + } + return (C) this; + } + + /** + * URL参数:拼接在URL后的参数 + * @param params 参数集合 + * @return HttpTask 实例 + **/ + public C addUrlParam(Map params) { + if (params != null) { + if (urlParams == null) { + urlParams = new HashMap<>(); + } + params.forEach((String name, Object value) -> { + if (name != null && value != null) { + urlParams.put(name, value.toString()); + } + }); + } + return (C) this; + } + + /** + * Body参数:放在Body里的参数 + * @param name 参数名 + * @param value 参数值 + * @return HttpTask 实例 + **/ + public C addBodyParam(String name, Object value) { + if (name != null && value != null) { + if (bodyParams == null) { + bodyParams = new HashMap<>(); + } + bodyParams.put(name, value.toString()); + } + return (C) this; + } + + /** + * Body参数:放在Body里的参数 + * @param params 参数集合 + * @return HttpTask 实例 + **/ + public C addBodyParam(Map params) { + if (params != null) { + if (bodyParams == null) { + bodyParams = new HashMap<>(); + } + params.forEach((String name, Object value) -> { + if (name != null && value != null) { + bodyParams.put(name, value.toString()); + } + }); + } + return (C) this; + } + + /** + * Json参数:请求体为Json,支持多层结构 + * @param name JSON键名 + * @param value JSON键值 + * @return HttpTask 实例 + */ + public C addJsonParam(String name, Object value) { + if (name != null && value != null) { + if (jsonParams == null) { + jsonParams = new HashMap<>(); + } + jsonParams.put(name, value); + } + return (C) this; + } + + /** + * Json参数:请求体为Json,只支持单层Json + * 若请求json为多层结构,请使用setRequestJson方法 + * @param params JSON键值集合 + * @return HttpTask 实例 + */ + public C addJsonParam(Map params) { + if (params != null) { + if (jsonParams == null) { + jsonParams = new HashMap<>(); + } + jsonParams.putAll(params); + } + return (C) this; + } + + /** + * 设置 json 请求体 + * @param json JSON字符串 或 Java对象(将依据 对象的get方法序列化为 json 字符串) + * @return HttpTask 实例 + **/ + public C setRequestJson(Object json) { + return setRequestJson(json, null); + } + + /** + * 请求体为json + * @param json JSON字符串 或 Java对象,将跟换 bean的get方法序列化程 json 字符串 + * @param dateFormat 序列化json时对日期类型字段的处理格式 + * @return HttpTask 实例 + **/ + public C setRequestJson(Object json, String dateFormat) { + if (json != null) { + if (json instanceof String) { + requestJson = json.toString(); + } else if (dateFormat != null) { + requestJson = JSON.toJSONStringWithDateFormat(json, dateFormat); + } else { + requestJson = JSON.toJSONString(json); + } + } + return (C) this; + } + + /** + * 添加文件参数 + * @param name 参数名 + * @param filePath 文件路径 + * @return HttpTask 实例 + */ + public C addFileParam(String name, String filePath) { + return addFileParam(name, new File(filePath)); + } + + /** + * 添加文件参数 + * @param name 参数名 + * @param file 文件 + * @return HttpTask 实例 + */ + public C addFileParam(String name, File file) { + if (name != null && file != null && file.exists()) { + String fileName = file.getName(); + String type = fileName.substring(fileName.lastIndexOf(".") + 1); + if (files == null) { + files = new HashMap<>(); + } + files.put(name, new FilePara(type, fileName, file)); + } + return (C) this; + } + + /** + * 添加文件参数 + * @param name 参数名 + * @param type 文件类型: 如 png、jpg、jpeg 等 + * @param inputStream 文件输入流 + * @return HttpTask 实例 + */ + public C addFileParam(String name, String type, InputStream inputStream) { + return addFileParam(name, type, null, inputStream); + } + + /** + * 添加文件参数 + * @param name 参数名 + * @param type 文件类型: 如 png、jpg、jpeg 等 + * @param fileName 文件名 + * @param input 文件输入流 + * @return HttpTask 实例 + */ + public C addFileParam(String name, String type, String fileName, InputStream input) { + if (name != null && input != null) { + byte[] content = null; + try { + Buffer buffer = new Buffer(); + content = buffer.readFrom(input).readByteArray(); + buffer.close(); + } catch (IOException e) { + throw new HttpException("读取文件输入流出错:", e); + } finally { + Util.closeQuietly(input); + } + addFileParam(name, type, fileName, content); + } + return (C) this; + } + + /** + * 添加文件参数 + * @param name 参数名 + * @param type 文件类型: 如 png、jpg、jpeg 等 + * @param content 文件内容 + * @return HttpTask 实例 + */ + public C addFileParam(String name, String type, byte[] content) { + return addFileParam(name, type, null, content); + } + + /** + * 添加文件参数 + * @param name 参数名 + * @param type 文件类型: 如 png、jpg、jpeg 等 + * @param fileName 文件名 + * @param content 文件内容 + * @return HttpTask 实例 + */ + public C addFileParam(String name, String type, String fileName, byte[] content) { + if (name != null && content != null) { + if (files == null) { + files = new HashMap<>(); + } + files.put(name, new FilePara(type, fileName, content)); + } + return (C) this; + } + + class FilePara { + + String type; + String fileName; + byte[] content; + File file; + + FilePara(String type, String fileName, byte[] content) { + this.type = type; + this.fileName = fileName; + this.content = content; + } + + FilePara(String type, String fileName, File file) { + this.type = type; + this.fileName = fileName; + this.file = file; + } + + } + + protected Call prepareCall(String method) { + assertNotConflict("GET".equals(method)); + Request.Builder builder = new Request.Builder() + .url(buildUrlPath()); + buildHeaders(builder); + RequestBody reqBody = null; + if (!"GET".equals(method)) { + reqBody = buildRequestBody(); + if (onProcess != null) { + long contentLength = contentLength(reqBody); + if (stepRate > 0 && stepRate <= 1) { + stepBytes = (long) (contentLength * stepRate); + } + if (stepBytes <= 0) { + stepBytes = Process.DEFAULT_STEP_BYTES; + } + reqBody = new ProcessRequestBody(reqBody, onProcess, + httpClient.getExecutor().getExecutor(pOnIO), + contentLength, stepBytes); + } + } + switch (method) { + case "GET": + builder.get(); + break; + case "POST": + builder.post(reqBody); + break; + case "PUT": + builder.put(reqBody); + break; + case "DELETE": + builder.delete(reqBody); + break; + } + return httpClient.request(builder.build()); + } + + private long contentLength(RequestBody reqBody) { + try { + return reqBody.contentLength(); + } catch (IOException e) { + throw new HttpException("无法获取请求体长度", e); + } + } + + + private void buildHeaders(Request.Builder builder) { + if (headers != null) { + for (String name : headers.keySet()) { + String value = headers.get(name); + if (value != null) { + builder.addHeader(name, value); + } + } + } + } + + + protected String toString(Response response) throws IOException { + String body = null; + ResponseBody rbody = response.body(); + if (rbody != null) { + body = rbody.string(); + } + return body; + } + + protected State toState(IOException e) { + if (e instanceof SocketTimeoutException) { + return State.TIMEOUT; + } else if (e instanceof UnknownHostException || e instanceof ConnectException) { + return State.NETWORK_ERROR; + } else if ("Canceled".equals(e.getMessage())) { + return State.CANCELED; + } + return State.EXCEPTION; + } + + + private RequestBody buildRequestBody() { + if (jsonParams != null) { + requestJson = JSON.toJSONString(jsonParams); + } + if (files != null) { + MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM); + if (bodyParams != null) { + for (String name : bodyParams.keySet()) { + String value = bodyParams.get(name); + builder.addFormDataPart(name, value); + } + } + for (String name : files.keySet()) { + FilePara file = files.get(name); + MediaType type = httpClient.getMediaType(file.type); + RequestBody bodyPart; + if (file.file != null) { + bodyPart = RequestBody.create(type, file.file); + } else { + bodyPart = RequestBody.create(type, file.content); + } + builder.addFormDataPart(name, file.fileName, bodyPart); + } + return builder.build(); + } else if (requestJson != null) { + return RequestBody.create(TYPE_JSON, requestJson); + } else { + FormBody.Builder builder = new FormBody.Builder(); + if (bodyParams != null) { + for (String name : bodyParams.keySet()) { + String value = bodyParams.get(name); + builder.add(name, value); + } + } + return builder.build(); + } + } + + + + private String buildUrlPath() { + String url = urlPath; + if (url == null || url.trim().isEmpty()) { + throw new HttpException("url 不能为空!"); + } + if (pathParams != null) { + for (String name : pathParams.keySet()) { + String target = "{" + name + "}"; + if (url.contains(target)) { + url = url.replace(target, pathParams.get(name)); + } else { + throw new HttpException("pathParameter [ " + name + " ] 不存在于 url [ " + urlPath + " ]"); + } + } + } + if (url.matches(PATH_PARAM_REGEX)) { + throw new HttpException("url 里有 pathParameter 没有设置,你必须先调用 addPathParam 为其设置!"); + } + if (urlParams != null) { + if (url.contains("?")) { + if (!url.endsWith("?")) { + url = url.trim(); + if (url.lastIndexOf("=") < url.lastIndexOf("?") + 2) { + throw new HttpException("url 格式错误,'?' 后没有发现 '='"); + } + if (!url.endsWith("&")) { + url += "&"; + } + } + } else { + url += "?"; + } + for (String name : urlParams.keySet()) { + url += name + "=" + urlParams.get(name) + "&"; + } + url = url.substring(0, url.length() - 1); + } + return url; + } + + + protected void assertNotConflict(boolean isGetRequest) { + if (isGetRequest) { + if (requestJson != null) { + throw new HttpException("GET 请求 不能调用 setRequestJson 方法!"); + } + if (jsonParams != null) { + throw new HttpException("GET 请求 不能调用 addJsonParam 方法!"); + } + if (bodyParams != null) { + throw new HttpException("GET 请求 不能调用 addBodyParam 方法!"); + } + if (files != null) { + throw new HttpException("GET 请求 不能调用 addFileParam 方法!"); + } + } + if (requestJson != null) { + if (jsonParams != null) { + throw new HttpException("方法 addJsonParam 与 setRequestJson 不能同时调用!"); + } + if (bodyParams != null) { + throw new HttpException("方法 addBodyParam 与 setRequestJson 不能同时调用!"); + } + if (files != null) { + throw new HttpException("方法 addFileParam 与 setRequestJson 不能同时调用!"); + } + } + if (jsonParams != null) { + if (bodyParams != null) { + throw new HttpException("方法 addBodyParam 与 addJsonParam 不能同时调用!"); + } + if (files != null) { + throw new HttpException("方法 addFileParam 与 addJsonParam 不能同时调用!"); + } + } + } + +} diff --git a/src/main/java/com/ejlchina/http/HttpUtils.java b/src/main/java/com/ejlchina/http/HttpUtils.java new file mode 100644 index 00000000..f7b7ccb5 --- /dev/null +++ b/src/main/java/com/ejlchina/http/HttpUtils.java @@ -0,0 +1,105 @@ +package com.ejlchina.http; + +import com.ejlchina.http.internal.AsyncHttpTask; +import com.ejlchina.http.internal.SyncHttpTask; +import com.ejlchina.http.internal.TaskExecutor; + +import okhttp3.Call; +import okhttp3.Request; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +/** + * Http 工具类,封装 OkHttp + + * 特性: + * 同步请求 + * 异步请求 + * Restfull路径 + * 文件上传 + * JSON自动解析 + * TCP连接池 + * Http2 + * + * @author Troy.Zhou + */ +public class HttpUtils { + + + private static HTTP http; + + + /** + * 配置HttpUtils持有的HTTP实例(不调用此方法前默认使用一个没有没有经过任何配置的HTTP懒实例) + * @param http HTTP实例 + */ + public static void of(HTTP http) { + if (http != null) { + HttpUtils.http = http; + } + } + + + static synchronized HTTP getHttp() { + if (http != null) { + return http; + } + http = HTTP.builder().build(); + return http; + } + + /** + * 异步请求 + * @param url 请求地址 + * @return 异步 HttpTask + */ + public static AsyncHttpTask async(String url) { + return getHttp().async(url); + } + + /** + * 同步请求 + * @param url 请求地址 + * @return 同步 HttpTask + */ + public static SyncHttpTask sync(String url) { + return getHttp().sync(url); + } + + /** + * 根据标签取消HTTP任务,只要任务的标签包含指定的Tag就会被取消 + * @param tag 标签 + * @return 被取消的任务数量 + */ + public static int cancel(String tag) { + return getHttp().cancel(tag); + } + + /** + * OkHttp 原生请求 (该请求不经过 预处理器) + * @param request 请求 + * @return Call + */ + public static Call request(Request request) { + return getHttp().request(request); + } + + /** + * Websocket(该请求不经过 预处理器) + * @param request 请求 + * @param listener 监听器 + * @return + */ + public static WebSocket webSocket(Request request, WebSocketListener listener) { + return getHttp().webSocket(request, listener); + } + + /** + * 获取任务执行器 + * @return TaskExecutor + */ + public static TaskExecutor getExecutor() { + return getHttp().getExecutor(); + } + +} diff --git a/src/main/java/com/ejlchina/http/OnCallback.java b/src/main/java/com/ejlchina/http/OnCallback.java new file mode 100644 index 00000000..32dd81e8 --- /dev/null +++ b/src/main/java/com/ejlchina/http/OnCallback.java @@ -0,0 +1,11 @@ +package com.ejlchina.http; + +/** + * 数据回调接口 + * Created by 15735 on 2017/1/3. + */ +public interface OnCallback { + + void on(T data); + +} diff --git a/src/main/java/com/ejlchina/http/Preprocessor.java b/src/main/java/com/ejlchina/http/Preprocessor.java new file mode 100644 index 00000000..ee2f757c --- /dev/null +++ b/src/main/java/com/ejlchina/http/Preprocessor.java @@ -0,0 +1,36 @@ +package com.ejlchina.http; + +/** + * 预处理器,支持异步 + * 在HTTP请求任务正式开始之前执行 + * @author Troy.Zhou + */ +public interface Preprocessor { + + /** + * 在HTTP请求开始之前执行 + * @param chain 预处理器链 + */ + void doProcess(PreChain chain); + + + interface PreChain { + + /** + * @return 当前的请求任务 + */ + HttpTask getTask(); + + /** + * @return HTTP + */ + HTTP getHttp(); + + /** + * 继续HTTP请求任务 + */ + void proceed(); + + } + +} diff --git a/src/main/java/com/ejlchina/http/Process.java b/src/main/java/com/ejlchina/http/Process.java new file mode 100644 index 00000000..aff09bd3 --- /dev/null +++ b/src/main/java/com/ejlchina/http/Process.java @@ -0,0 +1,31 @@ +package com.ejlchina.http; + +/** + * 进度(上传或下载) + * @since 2.2.0 + */ +public interface Process { + + static final int DEFAULT_STEP_BYTES = 8192; + + /** + * @return 完成比例 + */ + double getRate(); + + /** + * @return 总 + */ + long getTotalBytes(); + + /** + * @return 已完成任务量 + */ + long getDoneBytes(); + + /** + * @return 任务是否完成 + */ + boolean isDone(); + +} diff --git a/src/main/java/com/ejlchina/http/TaskListener.java b/src/main/java/com/ejlchina/http/TaskListener.java new file mode 100644 index 00000000..90c2ada2 --- /dev/null +++ b/src/main/java/com/ejlchina/http/TaskListener.java @@ -0,0 +1,20 @@ +package com.ejlchina.http; + +/** + * 任务监听接口 + * @author 15735 + * @since 2.3.0 + */ +public interface TaskListener { + + + /** + * 全局任务监听 + * @param task 所属的 HttpTask + * @param data 监听内容 + * @return 是否继续执行 task 对应的回调函数 + */ + boolean listen(HttpTask task, T data); + + +} diff --git a/src/main/java/com/ejlchina/http/internal/AsyncHttpTask.java b/src/main/java/com/ejlchina/http/internal/AsyncHttpTask.java new file mode 100644 index 00000000..c55985a8 --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/AsyncHttpTask.java @@ -0,0 +1,269 @@ +package com.ejlchina.http.internal; + +import java.io.IOException; + +import com.ejlchina.http.HttpCall; +import com.ejlchina.http.HttpResult; +import com.ejlchina.http.HttpResult.State; +import com.ejlchina.http.HttpTask; +import com.ejlchina.http.OnCallback; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Response; + + +/** + * 异步 Http 请求任务 + * + * @author Troy.Zhou + * + */ +public class AsyncHttpTask extends HttpTask { + + + private OnCallback onResponse; + private OnCallback onException; + private OnCallback onComplete; + private boolean rOnIO; + private boolean eOnIO; + private boolean cOnIO; + + + public AsyncHttpTask(HttpClient client, String url) { + super(client, url); + } + + + /** + * 设置请求执行异常后的回调函数,设置后,相关异常将不再向上抛出 + * @param onException 请求异常回调 + * @return HttpTask 实例 + */ + public AsyncHttpTask setOnException(OnCallback onException) { + this.onException = onException; + eOnIO = nextOnIO; + nextOnIO = false; + return this; + } + + /** + * 设置请求执行完成后的回调函数,无论成功|失败|异常 都会被执行 + * @param onComplete 请求完成回调 + * @return HttpTask 实例 + */ + public AsyncHttpTask setOnComplete(OnCallback onComplete) { + this.onComplete = onComplete; + cOnIO = nextOnIO; + nextOnIO = false; + return this; + } + + /** + * 设置请求得到响应后的回调函数 + * @param onResponse 请求响应回调 + * @return HttpTask 实例 + */ + public AsyncHttpTask setOnResponse(OnCallback onResponse) { + this.onResponse = onResponse; + rOnIO = nextOnIO; + nextOnIO = false; + return this; + } + + /** + * 发起 GET 请求 + * @return HttpCall + */ + public HttpCall get() { + return request("GET"); + } + + /** + * 发起 POST 请求 + * @return HttpCall + */ + public HttpCall post() { + return request("POST"); + } + + /** + * 发起 PUT 请求 + * @return HttpCall + */ + public HttpCall put() { + return request("PUT"); + } + + /** + * 发起 DELETE 请求 + * @return HttpCall + */ + public HttpCall delete() { + return request("DELETE"); + } + + private HttpCall request(String method) { + PreHttpCall call = new PreHttpCall(); + httpClient.preprocess(this, () -> { + synchronized (call) { + if (!call.isCanceled()) { + call.setCall(executeCall(prepareCall(method))); + } + } + }); + if (tag != null) { + httpClient.addTagCall(tag, call, this); + } + return call; + } + + + class PreHttpCall implements HttpCall { + + private boolean canceled = false; + private HttpCall call; + + @Override + public boolean cancel() { + boolean res = true; + synchronized (this) { + if (call != null) { + res = call.cancel(); + } else { + canceled = true; + } + notify(); + } + if (tag != null && call == null) { + httpClient.removeTagCall(AsyncHttpTask.this); + } + return res; + } + + @Override + public boolean isDone() { + if (call != null) { + return call.isDone(); + } + return canceled; + } + + @Override + public boolean isCanceled() { + if (call != null) { + return call.isCanceled(); + } + return canceled; + } + + public void setCall(HttpCall call) { + this.call = call; + notify(); + } + + @Override + public synchronized HttpResult getResult() { + if (canceled) { + return new RealHttpResult(AsyncHttpTask.this, State.CANCELED); + } + if (call == null) { + try { + wait(); + } catch (InterruptedException e) { + throw new HttpException(e.getMessage(), e); + } + } + if (call != null) { + return call.getResult(); + } + return new RealHttpResult(AsyncHttpTask.this, State.CANCELED); + } + + } + + class OkHttpCall implements HttpCall { + + private Call call; + private HttpResult result; + + public OkHttpCall(Call call) { + this.call = call; + } + + @Override + public boolean cancel() { + if (result == null) { + call.cancel(); + return true; + } + return false; + } + + @Override + public boolean isDone() { + return result != null; + } + + @Override + public boolean isCanceled() { + return call.isCanceled(); + } + + @Override + public synchronized HttpResult getResult() { + if (result == null) { + try { + wait(); + } catch (InterruptedException e) { + throw new HttpException(e.getMessage(), e); + } + } + return result; + } + + public void setResult(HttpResult result) { + synchronized (this) { + this.result = result; + notify(); + } + if (tag != null) { + httpClient.removeTagCall(AsyncHttpTask.this); + } + } + + } + + private HttpCall executeCall(Call call) { + OkHttpCall httpCall = new OkHttpCall(call); + call.enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException error) { + State state = toState(error); + if (state == State.CANCELED) { + httpCall.setResult(new RealHttpResult(AsyncHttpTask.this, state)); + } else { + TaskExecutor executor = httpClient.getExecutor(); + executor.executeOnComplete(AsyncHttpTask.this, onComplete, state, cOnIO); + if (!executor.executeOnException(AsyncHttpTask.this, onException, error, eOnIO) + && !nothrow) { + throw new HttpException(error.getMessage(), error); + } + httpCall.setResult(new RealHttpResult(AsyncHttpTask.this, state, error)); + } + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + TaskExecutor executor = httpClient.getExecutor(); + HttpResult result = new RealHttpResult(AsyncHttpTask.this, response, executor); + executor.executeOnComplete(AsyncHttpTask.this, onComplete, State.RESPONSED, cOnIO); + executor.executeOnResponse(AsyncHttpTask.this, onResponse, result, rOnIO); + httpCall.setResult(result); + } + + }); + return httpCall; + } + +} diff --git a/src/main/java/com/ejlchina/http/internal/HttpClient.java b/src/main/java/com/ejlchina/http/internal/HttpClient.java new file mode 100644 index 00000000..6d224c45 --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/HttpClient.java @@ -0,0 +1,445 @@ +package com.ejlchina.http.internal; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.Executor; + +import com.ejlchina.http.Configurator; +import com.ejlchina.http.DownListener; +import com.ejlchina.http.HTTP; +import com.ejlchina.http.HttpCall; +import com.ejlchina.http.HttpResult; +import com.ejlchina.http.HttpResult.State; +import com.ejlchina.http.HttpTask; +import com.ejlchina.http.Preprocessor; +import com.ejlchina.http.TaskListener; + +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; + +public class HttpClient implements HTTP { + + + private OkHttpClient client; + + private String baseUrl; + // 媒体类型 + private Map mediaTypes; + // 执行器 + private TaskExecutor executor; + // 预处理器 + private Preprocessor[] preprocessors; + + private List tagCalls; + + + private HttpClient(Builder builder) { + this.client = builder.client; + this.baseUrl = builder.baseUrl; + this.mediaTypes = builder.mediaTypes; + this.executor = new TaskExecutor(client.dispatcher().executorService(), + builder.mainExecutor, builder.downloadListener, + builder.responseListener, builder.exceptionListener, + builder.completeListener); + this.preprocessors = builder.preprocessors.toArray(new Preprocessor[builder.preprocessors.size()]); + this.tagCalls = Collections.synchronizedList(new LinkedList<>()); + } + + @Override + public AsyncHttpTask async(String url) { + return new AsyncHttpTask(this, urlPath(url)); + } + + @Override + public SyncHttpTask sync(String url) { + return new SyncHttpTask(this, urlPath(url)); + } + + @Override + public int cancel(String tag) { + if (tag == null) { + return 0; + } + int count = 0; + Iterator it = tagCalls.iterator(); + while (it.hasNext()) { + TagCall tagCall = it.next(); + // 只要任务的标签包含指定的Tag就会被取消 + if (tagCall.tag.contains(tag)) { + if (tagCall.call.cancel()) { + count++; + } + it.remove(); + } + } + return count; + } + + @Override + public Call request(Request request) { + return client.newCall(request); + } + + @Override + public WebSocket webSocket(Request request, WebSocketListener listener) { + return client.newWebSocket(request, listener); + } + + public OkHttpClient getOkClient() { + return client; + } + + public int getTagCallCount() { + return tagCalls.size(); + } + + public void addTagCall(String tag, HttpCall call, HttpTask task) { + tagCalls.add(new TagCall(tag, call, task)); + } + + public void removeTagCall(HttpTask task) { + Iterator it = tagCalls.iterator(); + while (it.hasNext()) { + TagCall tagCall = it.next(); + if (tagCall.task == task) { + it.remove(); + break; + } + } + } + + class TagCall { + + String tag; + HttpCall call; + HttpTask task; + + public TagCall(String tag, HttpCall call, HttpTask task) { + this.tag = tag; + this.call = call; + this.task = task; + } + + } + + public MediaType getMediaType(String type) { + String mediaType = mediaTypes.get(type); + if (mediaType != null) { + return MediaType.parse(mediaType); + } + return MediaType.parse("application/octet-stream"); + } + + public TaskExecutor getExecutor() { + return executor; + } + + public void preprocess(HttpTask> httpTask, Runnable request) { + if (preprocessors.length > 0) { + RealPreChain process = new RealPreChain(preprocessors, + httpTask, request); + preprocessors[0].doProcess(process); + } else { + request.run(); + } + } + + /** + * 串行预处理器 + * @author Troy.Zhou + */ + static class SerialPreprocessor implements Preprocessor { + + // 预处理器 + private Preprocessor preprocessor; + // 待处理的任务队列 + private Queue pendings; + // 是否有任务正在执行 + private boolean running = false; + + SerialPreprocessor(Preprocessor preprocessor) { + this.preprocessor = preprocessor; + this.pendings = new LinkedList<>(); + } + + @Override + public void doProcess(PreChain process) { + boolean should = true; + synchronized (this) { + if (running) { + pendings.add(process); + should = false; + } else { + running = true; + } + } + if (should) { + preprocessor.doProcess(process); + } + } + + public void afterProcess() { + PreChain process = null; + synchronized (this) { + if (pendings.size() > 0) { + process = pendings.poll(); + } else { + running = false; + } + } + if (process != null) { + preprocessor.doProcess(process); + } + } + + } + + + class RealPreChain implements Preprocessor.PreChain { + + private int index; + + private Preprocessor[] preprocessors; + + private HttpTask httpTask; + + private Runnable request; + + public RealPreChain(Preprocessor[] preprocessors, + HttpTask httpTask, + Runnable request) { + this.index = 1; + this.preprocessors = preprocessors; + this.httpTask = httpTask; + this.request = request; + } + + @Override + public HttpTask getTask() { + return httpTask; + } + + @Override + public HTTP getHttp() { + return HttpClient.this; + } + + @Override + public void proceed() { + if (index > 0) { + Preprocessor last = preprocessors[index - 1]; + if (last instanceof SerialPreprocessor) { + ((SerialPreprocessor) last).afterProcess(); + } + } + if (index < preprocessors.length) { + preprocessors[index++].doProcess(this); + } else { + request.run(); + } + } + + } + + public Builder newBuilder() { + return new Builder(this); + } + + private String urlPath(String urlPath) { + boolean isFullPath = urlPath.startsWith("https://") + || urlPath.startsWith("http://"); + if (isFullPath) { + return urlPath; + } + if (baseUrl != null) { + return baseUrl + urlPath; + } + throw new HttpException("在设置 BaseUrl 之前,您必须使用全路径URL发起请求,当前URL为:" + urlPath); + } + + + public static class Builder { + + private OkHttpClient client; + + private String baseUrl; + + private Map mediaTypes; + + private Configurator configurator; + + private Executor mainExecutor; + + private List preprocessors; + + private DownListener downloadListener; + + private TaskListener responseListener; + + private TaskListener exceptionListener; + + private TaskListener completeListener; + + public Builder() { + mediaTypes = new HashMap<>(); + mediaTypes.put("*", "application/octet-stream"); + mediaTypes.put("png", "image/png"); + mediaTypes.put("jpg", "image/jpeg"); + mediaTypes.put("jpeg", "image/jpeg"); + mediaTypes.put("wav", "audio/wav"); + mediaTypes.put("mp3", "audio/mp3"); + mediaTypes.put("mp4", "video/mpeg4"); + mediaTypes.put("txt", "text/plain"); + mediaTypes.put("xls", "application/x-xls"); + mediaTypes.put("xml", "text/xml"); + mediaTypes.put("apk", "application/vnd.android.package-archive"); + mediaTypes.put("doc", "application/msword"); + mediaTypes.put("pdf", "application/pdf"); + mediaTypes.put("html", "text/html"); + preprocessors = new ArrayList<>(); + } + + private Builder(HttpClient hc) { + this.client = hc.client; + this.baseUrl = hc.baseUrl; + this.mediaTypes = hc.mediaTypes; + this.preprocessors = new ArrayList<>(); + Collections.addAll(this.preprocessors, hc.preprocessors); + } + + /** + * 配置 OkHttpClient + * @param configurator 配置器 + * @return Builder + */ + public Builder config(Configurator configurator) { + this.configurator = configurator; + return this; + } + + /** + * 设置 baseUrl + * @param baseUrl 全局URL前缀 + * @return Builder + */ + public Builder baseUrl(String baseUrl) { + this.baseUrl = baseUrl; + return this; + } + + /** + * 配置媒体类型 + * @param mediaTypes 媒体类型 + * @return Builder + */ + public Builder mediaTypes(Map mediaTypes) { + this.mediaTypes.putAll(mediaTypes); + return this; + } + + /** + * 配置媒体类型 + * @param key 媒体类型KEY + * @param value 媒体类型VALUE + * @return Builder + */ + public Builder mediaTypes(String key, String value) { + this.mediaTypes.put(key, value); + return this; + } + + /** + * 设置回调执行器,例如实现切换线程功能,只对异步请求有效 + * @param executor 回调执行器 + * @return Builder + */ + public Builder callbackExecutor(Executor executor) { + this.mainExecutor = executor; + return this; + } + + /** + * 添加可并行处理请求任务的预处理器 + * @param preprocessor 预处理器 + * @return Builder + */ + public Builder addPreprocessor(Preprocessor preprocessor) { + preprocessors.add(preprocessor); + return this; + } + + /** + * 添加预处理器 + * @param preprocessor 预处理器 + * @return Builder + */ + public Builder addSerialPreprocessor(Preprocessor preprocessor) { + preprocessors.add(new SerialPreprocessor(preprocessor)); + return this; + } + + /** + * 设置全局响应监听 + * @param listener 监听器 + * @return Builder + */ + public Builder responseListener(TaskListener listener) { + this.responseListener = listener; + return this; + } + + /** + * 设置全局异常监听 + * @param listener 监听器 + * @return Builder + */ + public Builder exceptionListener(TaskListener listener) { + this.exceptionListener = listener; + return this; + } + + /** + * 设置全局完成监听 + * @param listener 监听器 + * @return Builder + */ + public Builder completeListener(TaskListener listener) { + this.completeListener = listener; + return this; + } + + /** + * 设置下载监听器 + * @param listener 监听器 + * @return Builder + */ + public Builder downloadListener(DownListener listener) { + this.downloadListener = listener; + return this; + } + + public HTTP build() { + if (configurator != null || client == null) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + if (configurator != null) { + configurator.config(builder); + } + client = builder.build(); + } + return new HttpClient(this); + } + + } + +} diff --git a/src/main/java/com/ejlchina/http/internal/HttpException.java b/src/main/java/com/ejlchina/http/internal/HttpException.java new file mode 100644 index 00000000..f78606cf --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/HttpException.java @@ -0,0 +1,21 @@ +package com.ejlchina.http.internal; + +/** + * Created by 周旭(Troy.Zhou) on 2016/8/30. + */ +public class HttpException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 8223484833265657324L; + + public HttpException(String detailMessage) { + super(detailMessage); + } + + public HttpException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } + +} diff --git a/src/main/java/com/ejlchina/http/internal/ProcessInputStream.java b/src/main/java/com/ejlchina/http/internal/ProcessInputStream.java new file mode 100644 index 00000000..779ddc30 --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/ProcessInputStream.java @@ -0,0 +1,53 @@ +package com.ejlchina.http.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.Executor; + +import com.ejlchina.http.OnCallback; +import com.ejlchina.http.Process; + +public class ProcessInputStream extends InputStream { + + private InputStream input; + private OnCallback onProcess; + private Executor callbackExecutor; + private long stepBytes; + private long step = 0; + private RealProcess process; + private boolean doneCalled = false; + + public ProcessInputStream(InputStream input, OnCallback onProcess, long totalBytes, long stepBytes, + long doneBytes, Executor callbackExecutor) { + this.input = input; + this.onProcess = onProcess; + this.stepBytes = stepBytes; + this.callbackExecutor = callbackExecutor; + this.process = new RealProcess(totalBytes, doneBytes); + this.step = doneBytes / stepBytes; + } + + + @Override + public int read() throws IOException { + int data = input.read(); + if (data > -1) { + process.increaseDoneBytes(); + } + if (process.notDoneOrReached(step * stepBytes)) { + return data; + } + if (process.isDone()) { + if (doneCalled) { + return data; + } + doneCalled = true; + } + step++; + callbackExecutor.execute(() -> { + onProcess.on(process); + }); + return data; + } + +} diff --git a/src/main/java/com/ejlchina/http/internal/ProcessRequestBody.java b/src/main/java/com/ejlchina/http/internal/ProcessRequestBody.java new file mode 100644 index 00000000..6f406b54 --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/ProcessRequestBody.java @@ -0,0 +1,91 @@ +package com.ejlchina.http.internal; + +import java.io.IOException; +import java.util.concurrent.Executor; + +import com.ejlchina.http.OnCallback; +import com.ejlchina.http.Process; + +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okio.Buffer; +import okio.BufferedSink; +import okio.ForwardingSink; +import okio.Okio; + +public class ProcessRequestBody extends RequestBody { + + private RequestBody requestBody; + private OnCallback onProcess; + private Executor callbackExecutor; + private long stepBytes; + private long step = 0; + private RealProcess process; + private boolean doneCalled = false; + + public ProcessRequestBody(RequestBody requestBody, OnCallback onProcess, Executor callbackExecutor, + long contentLength, long stepBytes) { + this.requestBody = requestBody; + this.onProcess = onProcess; + this.callbackExecutor = callbackExecutor; + this.stepBytes = stepBytes; + this.process = new RealProcess(contentLength, 0); + } + + @Override + public long contentLength() throws IOException { + return process.getTotalBytes(); + } + + @Override + public boolean isDuplex() { + return requestBody.isDuplex(); + } + + @Override + public boolean isOneShot() { + return requestBody.isOneShot(); + } + + @Override + public MediaType contentType() { + return requestBody.contentType(); + } + + private BufferedSink bufferedSink; + + @Override + public void writeTo(BufferedSink sink) throws IOException { + if (bufferedSink == null) { + bufferedSink = Okio.buffer(new ForwardingSink(sink) { + + @Override + public void write(Buffer source, long byteCount) throws IOException { + //这个方法会循环调用,byteCount 是每次调用上传的字节数。 + super.write(source, byteCount); + process.addDoneBytes(byteCount); + if (process.notDoneOrReached(step * stepBytes)) { + return; + } + if (process.isDone()) { + if (doneCalled) { + return; + } + doneCalled = true; + } + step++; + callbackExecutor.execute(() -> { + onProcess.on(process); + }); + } + + }); + } + requestBody.writeTo(bufferedSink); + bufferedSink.flush(); + } + + + +} + diff --git a/src/main/java/com/ejlchina/http/internal/RealHttpResult.java b/src/main/java/com/ejlchina/http/internal/RealHttpResult.java new file mode 100644 index 00000000..697fd4a6 --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/RealHttpResult.java @@ -0,0 +1,134 @@ +package com.ejlchina.http.internal; + +import java.util.List; + +import com.ejlchina.http.HttpResult; +import com.ejlchina.http.HttpTask; + +import okhttp3.Headers; +import okhttp3.Response; + + +public class RealHttpResult implements HttpResult { + + private State state; + private Response response; + private Exception error; + private TaskExecutor taskExecutor; + private HttpTask httpTask; + + + public RealHttpResult(HttpTask httpTask, State state) { + this.httpTask = httpTask; + this.state = state; + } + + public RealHttpResult(HttpTask httpTask, Response response, TaskExecutor taskExecutor) { + this(httpTask, taskExecutor); + response(response); + } + + public RealHttpResult(HttpTask httpTask, TaskExecutor taskExecutor) { + this.httpTask = httpTask; + this.taskExecutor = taskExecutor; + } + + public RealHttpResult(HttpTask httpTask, State state, Exception error) { + this.httpTask = httpTask; + exception(state, error); + } + + + public void exception(State state, Exception error) { + this.state = state; + this.error = error; + } + + public void response(Response response) { + this.state = State.RESPONSED; + this.response = response; + } + + @Override + public HttpTask getTask() { + return httpTask; + } + + @Override + public State getState() { + return state; + } + + @Override + public int getStatus() { + if (response != null) { + return response.code(); + } + return 0; + } + + @Override + public boolean isSuccessful() { + if (response != null) { + return response.isSuccessful(); + } + return false; + } + + @Override + public Headers getHeaders() { + if (response != null) { + return response.headers(); + } + return null; + } + + @Override + public List getHeaders(String name) { + return response.headers(name); + } + + @Override + public String getHeader(String name) { + return response.header(name); + } + + @Override + public Body getBody() { + if (response != null) { + return new ResultBody(httpTask, response, taskExecutor); + } + return null; + } + + @Override + public Exception getError() { + return error; + } + + public Response getResponse() { + return response; + } + + + @Override + public String toString() { + Body body = getBody(); + String str = "HttpResult [\n state: " + state + ",\n status: " + getStatus() + + ",\n headers: " + getHeaders(); + if (body != null) { + str += ",\n contentType: " + body.getContentType() + + ",\n body: " + body.toString(); + } + return str + ",\n error: " + error + "\n]"; + } + + @Override + public HttpResult close() { + if (response != null) { + response.close(); + } + return this; + } + +} diff --git a/src/main/java/com/ejlchina/http/internal/RealProcess.java b/src/main/java/com/ejlchina/http/internal/RealProcess.java new file mode 100644 index 00000000..700d2895 --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/RealProcess.java @@ -0,0 +1,50 @@ +package com.ejlchina.http.internal; + +import com.ejlchina.http.Process; + +public class RealProcess implements Process { + + // 总字节数 + private long totalBytes; + // 已经完成字节数 + private long doneBytes; + + + public RealProcess(long totalBytes, long doneBytes) { + this.totalBytes = totalBytes; + this.doneBytes = doneBytes; + } + + @Override + public double getRate() { + return (double) doneBytes / totalBytes; + } + + @Override + public long getTotalBytes() { + return totalBytes; + } + + @Override + public long getDoneBytes() { + return doneBytes; + } + + @Override + public boolean isDone() { + return doneBytes >= totalBytes; + } + + public void addDoneBytes(long delt) { + doneBytes += delt; + } + + public void increaseDoneBytes() { + doneBytes++; + } + + public boolean notDoneOrReached(long bytes) { + return doneBytes < bytes && doneBytes < totalBytes; + } + +} diff --git a/src/main/java/com/ejlchina/http/internal/ResultBody.java b/src/main/java/com/ejlchina/http/internal/ResultBody.java new file mode 100644 index 00000000..aa827e75 --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/ResultBody.java @@ -0,0 +1,323 @@ +package com.ejlchina.http.internal; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URLDecoder; +import java.nio.charset.Charset; +import java.util.List; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ejlchina.http.Download; +import com.ejlchina.http.HttpTask; +import com.ejlchina.http.HttpResult.Body; +import com.ejlchina.http.OnCallback; +import com.ejlchina.http.Process; + +import okhttp3.MediaType; +import okhttp3.Response; +import okio.Buffer; + +public class ResultBody implements Body { + + private Response response; + private TaskExecutor taskExecutor; + private boolean onIO = false; + private OnCallback onProcess; + private long stepBytes = 0; + private double stepRate = -1; + private boolean rangeIgnored = false; + private HttpTask httpTask; + private boolean cached = false; + private byte[] data; + + ResultBody(HttpTask httpTask, Response response, TaskExecutor taskExecutor) { + this.httpTask = httpTask; + this.response = response; + this.taskExecutor = taskExecutor; + } + + @Override + public MediaType getContentType() { + return response.body().contentType(); + } + + @Override + public long getContentLength() { + return response.body().contentLength(); + } + + @Override + public Body nextOnIO() { + onIO = true; + return this; + } + + @Override + public Body setOnProcess(OnCallback onProcess) { + if (taskExecutor == null) { + throw new IllegalStateException("没有 taskExecutor, 不可设置下载进度回调!"); + } + if (cached) { + throw new IllegalStateException("开启缓存后,不可设置下载进度回调!"); + } + this.onProcess = onProcess; + return this; + } + + @Override + public Body setStepBytes(long stepBytes) { + this.stepBytes = stepBytes; + return this; + } + + @Override + public Body setStepRate(double stepRate) { + this.stepRate = stepRate; + return this; + } + + @Override + public Body setRangeIgnored() { + this.rangeIgnored =true; + return this; + } + + @Override + public InputStream toByteStream() { + InputStream input; + if (cached) { + input = new ByteArrayInputStream(cacheBytes()); + } else { + input = response.body().byteStream(); + } + if (onProcess != null) { + long rangeStart = getRangeStart(); + long totalBytes = getContentLength(); + if (!rangeIgnored) { + totalBytes += rangeStart; + } + if (stepRate > 0 && stepRate <= 1) { + stepBytes = (long) (totalBytes * stepRate); + } + if (stepBytes <= 0) { + stepBytes = Process.DEFAULT_STEP_BYTES; + } + return new ProcessInputStream(input, onProcess, totalBytes, stepBytes, + rangeIgnored ? 0 : rangeStart, taskExecutor.getExecutor(onIO)); + } + return input; + } + + @Override + public byte[] toBytes() { + if (cached) { + return cacheBytes(); + } + return bodyToBytes(); + } + + @Override + public Reader toCharStream() { + if (cached || onProcess != null) { + return new InputStreamReader(toByteStream()); + } + return response.body().charStream(); + } + + @Override + public String toString() { + if (cached || onProcess != null) { + MediaType contentType = getContentType(); + Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8; + return new String(toBytes(), charset); + } + try { + return response.body().string(); + } catch (IOException e) { + throw new HttpException("报文体转化字符串出错", e); + } + } + + @Override + public JSONObject toJsonObject() { + return JSON.parseObject(toString()); + } + + @Override + public JSONArray toJsonArray() { + return JSON.parseArray(toString()); + } + + @Override + public T toBean(Class type) { + return JSON.parseObject(toString(), type); + } + + @Override + public List toList(Class type) { + return JSON.parseArray(toString(), type); + } + + @Override + public Download toFile(String filePath) { + return toFile(new File(filePath)); + } + + @Override + public Download toFile(File file) { + if (taskExecutor == null) { + throw new IllegalStateException("没有 taskExecutor, 不可进行下载操作!"); + } + if (!file.exists()) { + try { + File parent = file.getParentFile(); + if (!parent.exists()) { + parent.mkdirs(); + } + file.createNewFile(); + } catch (IOException e) { + response.close(); + throw new HttpException( + "Cannot create file [" + file.getAbsolutePath() + "]", e); + } + } + return taskExecutor.download(httpTask, file, toByteStream(), + getRangeStart()); + } + + @Override + public Download toFolder(String dirPath) { + String fileName = resolveFileName(); + String filePath = resolveFilePath(dirPath, fileName); + int index = 0; + File file = new File(filePath); + while (file.exists()) { + String indexFileName = indexFileName(fileName, index++); + filePath = resolveFilePath(dirPath, indexFileName); + file = new File(filePath); + } + return toFile(file); + } + + @Override + public Download toFolder(File dir) { + if (dir.exists() && !dir.isDirectory()) { + response.close(); + throw new HttpException("文件下载失败:文件[" + dir.getAbsolutePath() + "]已存在,并且不是一个目录!"); + } + if (!dir.exists()) { + dir.mkdirs(); + } + return toFolder(dir.getAbsolutePath()); + } + + @Override + public Body cache() { + if (onProcess != null) { + throw new IllegalStateException("设置了下载进度回调,不可再开启缓存!"); + } + cached = true; + return this; + } + + @Override + public Body close() { + response.close(); + data = null; + return this; + } + + private byte[] cacheBytes() { + if (data == null) { + data = bodyToBytes(); + } + return data; + } + + private byte[] bodyToBytes() { + if (onProcess != null) { + Buffer buffer = new Buffer(); + try { + return buffer.readFrom(toByteStream()).readByteArray(); + } catch (IOException e) { + throw new HttpException("报文体转化字节数组出错", e); + } finally { + response.close(); + buffer.close(); + } + } + try { + return response.body().bytes(); + } catch (IOException e) { + throw new HttpException("报文体转化字节数组出错", e); + } + } + + private long getRangeStart() { + long rangeStart = 0; + if (response.code() != HttpURLConnection.HTTP_PARTIAL) { + return rangeStart; + } + String range = response.header("Content-Range"); + if (range != null && range.startsWith("bytes")) { + int index = range.indexOf('-'); + if (index > 5) { + String start = range.substring(5, index).trim(); + try { + rangeStart = Long.parseLong(start); + } catch (Exception ignore) {} + } + } + return rangeStart; + } + + private String resolveFilePath(String dirPath, String fileName) { + if (dirPath.endsWith("\\") || dirPath.endsWith("/")) { + return dirPath + fileName; + } + return dirPath + "\\" + fileName; + } + + private String indexFileName(String fileName, int index) { + int i = fileName.lastIndexOf('.'); + if (i < 0) { + return fileName + "(" + index + ")"; + } + String ext = fileName.substring(i); + if (i > 0) { + String name = fileName.substring(0, i); + return name + "(" + index + ")" + ext; + } + return "(" + index + ")" + ext; + } + + private String resolveFileName() { + String fileName = response.header("Content-Disposition"); + // 通过Content-Disposition获取文件名,这点跟服务器有关,需要灵活变通 + if (fileName == null || fileName.length() < 1) { + fileName = response.request().url().encodedPath(); + fileName = fileName.substring(fileName.lastIndexOf("/") + 1); + } else { + try { + fileName = URLDecoder.decode(fileName.substring( + fileName.indexOf("filename=") + 9), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new HttpException("解码文件名失败", e); + } + // 有些文件名会被包含在""里面,所以要去掉,不然无法读取文件后缀 + fileName = fileName.replaceAll("\"", ""); + } + return fileName; + } + +} diff --git a/src/main/java/com/ejlchina/http/internal/SyncHttpTask.java b/src/main/java/com/ejlchina/http/internal/SyncHttpTask.java new file mode 100644 index 00000000..8ff98b5b --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/SyncHttpTask.java @@ -0,0 +1,92 @@ +package com.ejlchina.http.internal; + +import java.io.IOException; + +import com.ejlchina.http.HttpResult; +import com.ejlchina.http.HttpResult.State; +import com.ejlchina.http.HttpTask; + +import okhttp3.Call; +import okhttp3.Response; + + +/** + * 同步 Http 请求任务 + * + * @author Troy.Zhou + * + */ +public class SyncHttpTask extends HttpTask { + + public SyncHttpTask(HttpClient client, String url) { + super(client, url); + } + + /** + * 发起 GET 请求 + * @return 请求结果 + */ + public HttpResult get() { + return request("GET"); + } + + /** + * 发起 POST 请求 + * @return 请求结果 + */ + public HttpResult post() { + return request("POST"); + } + + /** + * 发起 PUT 请求 + * @return 请求结果 + */ + public HttpResult put() { + return request("PUT"); + } + + /** + * 发起 DELETE 请求 + * @return 请求结果 + */ + public HttpResult delete() { + return request("DELETE"); + } + + private HttpResult request(String method) { + RealHttpResult result = new RealHttpResult(this, httpClient.getExecutor()); + httpClient.preprocess(this, () -> { + Call call = prepareCall(method); + try { + Response response = call.execute(); + synchronized (SyncHttpTask.this) { + result.response(response); + SyncHttpTask.this.notify(); + } + } catch (IOException e) { + State state = toState(e); + synchronized (SyncHttpTask.this) { + result.exception(state, e); + SyncHttpTask.this.notify(); + } + } + }); + synchronized (this) { + if (result.getState() == null) { + try { + SyncHttpTask.this.wait(); + } catch (InterruptedException e) { + throw new HttpException("等待异常", e); + } + } + } + Exception e = result.getError(); + if (e != null && result.getState() != State.CANCELED + && !nothrow) { + throw new HttpException("请求执行异常", e); + } + return result; + } + +} diff --git a/src/main/java/com/ejlchina/http/internal/TaskExecutor.java b/src/main/java/com/ejlchina/http/internal/TaskExecutor.java new file mode 100644 index 00000000..419bc56c --- /dev/null +++ b/src/main/java/com/ejlchina/http/internal/TaskExecutor.java @@ -0,0 +1,98 @@ +package com.ejlchina.http.internal; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.Executor; + +import com.ejlchina.http.DownListener; +import com.ejlchina.http.Download; +import com.ejlchina.http.HttpResult; +import com.ejlchina.http.HttpResult.State; +import com.ejlchina.http.HttpTask; +import com.ejlchina.http.OnCallback; +import com.ejlchina.http.TaskListener; + +public class TaskExecutor { + + private Executor ioExecutor; + private Executor mainExecutor; + private DownListener downloadListener; + private TaskListener responseListener; + private TaskListener exceptionListener; + private TaskListener completeListener; + + public TaskExecutor(Executor ioExecutor, Executor mainExecutor, DownListener downloadListener, + TaskListener responseListener, TaskListener exceptionListener, + TaskListener completeListener) { + this.ioExecutor = ioExecutor; + this.mainExecutor = mainExecutor; + this.downloadListener = downloadListener; + this.responseListener = responseListener; + this.exceptionListener = exceptionListener; + this.completeListener = completeListener; + } + + public Executor getExecutor(boolean onIoThread) { + if (onIoThread || mainExecutor == null) { + return ioExecutor; + } + return mainExecutor; + } + + public Download download(HttpTask httpTask, File file, InputStream input, long skipBytes) { + Download download = new Download(file, input, this, skipBytes); + if (downloadListener != null) { + downloadListener.listen(httpTask, download); + } + return download; + } + + public void execute(Runnable command, boolean onIoThread) { + Executor executor = ioExecutor; + if (mainExecutor != null && !onIoThread) { + executor = mainExecutor; + } + executor.execute(command); + } + + public void executeOnResponse(HttpTask task, OnCallback onResponse, HttpResult result, boolean onIoThread) { + if (responseListener != null) { + execute(() -> { + if (responseListener.listen(task, result) && onResponse != null) { + onResponse.on(result); + } + }, onIoThread); + } else if (onResponse != null) { + execute(() -> { onResponse.on(result); }, onIoThread); + } + } + + public boolean executeOnException(HttpTask task, OnCallback onException, IOException error, boolean onIoThread) { + if (exceptionListener != null) { + execute(() -> { + if (exceptionListener.listen(task, error) && onException != null) { + onException.on(error); + } + }, onIoThread); + return true; + } else if (onException != null) { + execute(() -> { onException.on(error); }, onIoThread); + return true; + } + return false; + } + + public void executeOnComplete(HttpTask task, OnCallback onComplete, State state, boolean onIoThread) { + if (completeListener != null) { + execute(() -> { + if (completeListener.listen(task, state) && onComplete != null) { + onComplete.on(state); + } + }, onIoThread); + } else if (onComplete != null) { + execute(() -> { onComplete.on(state); }, onIoThread); + } + } + +} diff --git a/src/test/java/com/ejlchina/test/HttpTest.java b/src/test/java/com/ejlchina/test/HttpTest.java new file mode 100644 index 00000000..6573da97 --- /dev/null +++ b/src/test/java/com/ejlchina/test/HttpTest.java @@ -0,0 +1,611 @@ +package com.ejlchina.test; + + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +import com.ejlchina.http.Download; +import com.ejlchina.http.Download.Ctrl; +import com.ejlchina.http.Download.Failure; +import com.ejlchina.http.HTTP; +import com.ejlchina.http.HttpCall; +import com.ejlchina.http.HttpResult; +import com.ejlchina.http.HttpResult.Body; +import com.ejlchina.http.HttpResult.State; +import com.ejlchina.http.HttpTask; +import com.ejlchina.http.HttpUtils; +import com.ejlchina.http.Preprocessor.PreChain; +import com.ejlchina.http.Process; +import com.ejlchina.http.internal.HttpClient; + +import okhttp3.ConnectionPool; +import okhttp3.Interceptor.Chain; +import okhttp3.OkHttpClient.Builder; +import okhttp3.Request; + + +public class HttpTest { + + + @Test + public void testCache() { + HTTP http = HTTP.builder().build(); + + Body body = http.sync("http://xxx.cdyun.vip/comm/provinces") + .get() + .getBody() + .cache() + ; + + + System.out.println("result = " + body.toString()); + System.out.println("result = " + body.toJsonArray()); + System.out.println("result = " + body.toBytes()); + + body.close(); + + System.out.println("result = " + body.toString()); + System.out.println("result = " + body.toJsonArray()); + System.out.println("result = " + body.toBytes()); + } + + + @Test + public void testGlobalCallback() { + HTTP http = HTTP.builder() + .responseListener((HttpTask task, HttpResult result) -> { + System.out.println("全局 onResponse: " + result.getStatus()); + return true; + }) + .completeListener((HttpTask task, State state) -> { + System.out.println("全局 onComplete: " + state); + return true; + }) + .exceptionListener((HttpTask task, IOException error) -> { + System.out.println("全局 onException: " + error.getMessage()); + return false; + }) + .build(); + + http.async("http://www.baidu.com") + .setOnResponse((HttpResult result) -> { + System.out.println("单例 onResponse: " + result.getStatus()); + }) + .setOnComplete((State state) -> { + System.out.println("单例 onComplete: " + state); + }) + .setOnException((IOException error) -> { + System.out.println("单例 onException: " + error.getMessage()); + }) + .get(); + + sleep(2000); + } + + @Test + public void testDownloadListener() { + HTTP http = HTTP.builder() + .downloadListener((HttpTask task, Download download) -> { + System.out.println("URL = " + task.getUrl()); + System.out.println("TAG = " + task.getTag()); + Ctrl ctrl = download.getCtrl(); + + new Thread(() -> { + sleep(3000); + ctrl.pause(); + sleep(3000); + ctrl.resume(); + sleep(3000); + ctrl.cancel(); + }).start(); + }) + .build(); + + String url = "https://download.cocos.com/CocosDashboard/v1.0.1/CocosDashboard-v1.0.1-win32-031816.exe"; + + long t0 = System.currentTimeMillis(); + + + http.sync(url) + .setTag("ASD") + .get() + .getBody() + .setOnProcess((Process process) -> { + print(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); + }) + .setStepRate(0.01) + .toFolder("D:/download/") + .nextOnIO() + .setOnSuccess((File file) -> { + print(t0, "下载成功:" + file.getAbsolutePath(), true); + }) + .setOnFailure((Failure failure) -> { + print(t0, "下载失败:" + failure.getDoneBytes() + ", path = " + failure.getFile().getAbsolutePath(), true); + }) + .start(); + + sleep(20000); + } + + @Test + public void testExecutor() { + HTTP http = HTTP.builder() + .callbackExecutor((Runnable command) -> { + System.out.println("主线程执行"); + command.run(); + }).build(); + + http.async("http://47.100.7.202/ejl-test.zip") + .addBodyParam("name", "Jack") +// .nextOnIO() + .setOnProcess((Process process) -> { + System.out.println("process: " + process.getRate()); + }) +// .nextOnIO() + .setOnResponse((HttpResult result) -> { + System.out.println("status: " + result.close().getStatus()); + }) + .nextOnIO() + .setOnComplete((State state) -> { + System.out.println("state: " + state); + }) + .post(); + + sleep(3000); + } + + + public static void main(String[] args) { + long totalSize = HttpUtils.sync("/download/test.zip").get().getBody() + .close() // 因为这次请求只是为了获得文件大小,不消费报文体,所以直接关闭 + .getContentLength(); + download(totalSize, 0); // 从第 0 块开始下载 + sleep(50000); // 等待下载完成 + } + + static void download(long totalSize, int index) { + long size = 3 * 1024 * 1024; // 每块下载 3M + long start = index * size; + long end = Math.min(start + size, totalSize); + HttpUtils.sync("/download/test.zip") + .setRange(start, end) // 设置本次下载的范围 + .get().getBody() + .toFile("D:/download/test.zip") // 下载到同一个文件里 + .setAppended() // 开启文件追加模式 + .setOnSuccess((File file) -> { + if (end < totalSize) { // 若未下载完,则继续下载下一块 + download(totalSize, index + 1); + } else { + System.out.println("下载完成"); + } + }) + .start(); + } + + + @Test + public void testD() { + System.out.println(Long.parseLong("12")); + } + + + @Test + public void testUpload() { + String data = "0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz"; + + HTTP http = HTTP.builder().build(); + + long t0 = System.currentTimeMillis(); + + String res = http.sync("http://localhost:8080/test/index") + .addBodyParam("data", data) + .addFileParam("file", "D:\\WorkSpace\\download\\CocosDashboard-v1.0.1-win32-031816.exe") + .setStepRate(0.01) + .setOnProcess((Process process) -> { + print(t0, "上传:" + process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); + }) + .post() + .getBody() + .setStepBytes(5) + .setOnProcess((Process process) -> { + print(t0, "下载:" + process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); + }) + .toString(); + + System.out.println("响应:" + res); + } + + @Test + public void testDownload() { + HTTP http = HTTP.builder() + .config((Builder builder) -> { + builder.readTimeout(300, TimeUnit.MILLISECONDS); + }) + .build(); + + String url = "https://download.cocos.com/CocosDashboard/v1.0.1/CocosDashboard-v1.0.1-win32-031816.exe"; +// String url = "http://47.100.7.202/ejl-test.zip"; + + long t0 = System.currentTimeMillis(); + +// Ctrl ctrl = + http.sync(url) +// .setRange(24771214) + .bind(this) + .get() + .getBody() + .setOnProcess((Process process) -> { + print(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); + }) + .setStepRate(0.1) + .toFolder("D:/WorkSpace/download/") +// .toFile("D:\\WorkSpace\\download\\CocosDashboard-v1.0.1-win32-031816(9).exe") +// .setAppended() // 启用 断点续传 + .nextOnIO() + .setOnSuccess((File file) -> { + print(t0, "下载成功:" + file.getAbsolutePath(), true); + }) + .setOnFailure((Failure failure) -> { + print(t0, "下载失败:" + failure.getDoneBytes() + ", path = " + failure.getFile().getAbsolutePath(), true); + }) + .start(); + + sleep(10000); + +// ctrl.status(); +// ctrl.pause(); +// System.out.println("暂停"); +// sleep(5000); +// +// ctrl.resume(); +// System.out.println("继续"); +// sleep(5000); +// +// ctrl.cancel(); +// System.out.println("取消"); + + +// + +// +// ctrl.pause(); +// System.out.println("暂停"); +// sleep(5000); +// +// ctrl.resume(); +// System.out.println("继续"); + sleep(5000); + + } + + void print(long t0, String str, boolean ln) { + long now = System.currentTimeMillis() - t0; + System.out.println((now) + "\t" + str); + if (ln) { + System.out.println(); + } + } + + long now(long t0) { + return System.currentTimeMillis() - t0; + } + + + @Test + public void testToList() { + long t0 = System.currentTimeMillis(); + + HTTP http = HTTP.builder() + .baseUrl("http://xxx.cdyun.vip/ejlchina") + .build(); + + HttpResult result = http.sync("/comm/provinces") + .setRange(0) + .get(); + + print(t0, "status: " + result.getStatus(), true); + print(t0, "headers: " + result.getHeaders(), true); + + Body body = result.getBody(); + + print(t0, "total: " + body.getContentLength(), true); + + List list = body.setStepRate(0.1) + .setOnProcess((Process process) -> { + print(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); + }) + .toList(User.class); + + print(t0, list.toString(), false); + } + + + @Test + public void testPreprocessor() { + + HTTP http = HTTP.builder() + .baseUrl("http://localhost:8080") + .addPreprocessor((PreChain chain) -> { + System.out.println("并行预处理-开始"); +// new Thread(() -> { + sleep(2000); + System.out.println("并行预处理-结束"); + chain.proceed(); +// }).start(); + }) + .addSerialPreprocessor((PreChain chain) -> { + System.out.println("串行预处理-开始"); + new Thread(() -> { + sleep(3000); + System.out.println("串行预处理-结束"); + chain.proceed(); + }).start(); + }) + .build(); + + new Thread(() -> { + System.out.println(http.sync("/user/show/1").get()); + }).start(); + + new Thread(() -> { + System.out.println(http.sync("/user/show/2").get()); + }).start(); + +// new Thread(() -> { +// http.async("/user/show/1") +// .setOnResponse((HttpResult result) -> { +// System.out.println(result); +// }) +// .get(); +// }).start(); +// +// new Thread(() -> { +// http.async("/user/show/2") +// .setOnResponse((HttpResult result) -> { +// System.out.println(result); +// }) +// .get(); +// }).start(); + + sleep(10000); + } + + + + @Test + public void testCancel() { + + HTTP http = HTTP.builder() + .baseUrl("http://localhost:8080") + .build(); + + http.async("/user/show/1") + .setOnResponse((HttpResult result) -> { + System.out.println(result); + }) + .setOnException((IOException e) -> { + System.out.println("异常捕获:" + e.getMessage()); + }) + .setOnComplete((State state) -> { + System.out.println(state); + }) + .setTag("A") + .get(); + + System.out.println(((HttpClient) http).getTagCallCount()); + + http.async("/user/show/2") + .setOnResponse((HttpResult result) -> { + System.out.println(result); + }) + .setTag("A.B") + .get(); + + System.out.println(((HttpClient) http).getTagCallCount()); + + http.async("/user/show/3") + .setOnResponse((HttpResult result) -> { + System.out.println(result); + }) + .setTag("B.C") + .get(); + + System.out.println(((HttpClient) http).getTagCallCount()); + + http.async("/user/show/4") + .setOnResponse((HttpResult result) -> { + System.out.println(result); + }) + .setTag("C") + .get(); + + System.out.println(((HttpClient) http).getTagCallCount()); + + + System.out.println("标签取消:" + http.cancel("B")); + + System.out.println(((HttpClient) http).getTagCallCount()); + + sleep(5000); + + System.out.println(((HttpClient) http).getTagCallCount()); + + sleep(5000); + + System.out.println(((HttpClient) http).getTagCallCount()); + + sleep(5000); + + System.out.println(((HttpClient) http).getTagCallCount()); + +// System.out.println("isDone = " + call.isDone()); +// System.out.println("isCanceled = " + call.isCanceled()); +// +// System.out.println("取消结果 = " + call.cancel()); +// +// System.out.println("isDone = " + call.isDone()); +// System.out.println("isCanceled = " + call.isCanceled()); +// +// sleep(100); +// System.out.println("++++++++"); +// +// System.out.println("isDone = " + call.isDone()); +// System.out.println("isCanceled = " + call.isCanceled()); + } + + + + + private HTTP buildHttp() { + + return HTTP.builder() + .config((Builder builder) -> { + + // 配置连接池 最小10个连接(不配置默认为 5) + builder.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES)); + + // 配置连接超时时间 + builder.connectTimeout(20, TimeUnit.SECONDS); + + builder.addInterceptor((Chain chain) -> { + + Request request = chain.request(); + + return chain.proceed(request); + }); + }) + .baseUrl("http://localhost:8080") + .callbackExecutor((Runnable run) -> { + runOnUiThread(run); + }) + .addPreprocessor((PreChain chain) -> { + new Thread(() -> { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + chain.getTask().addHeader("Token", "yyyyy"); + + chain.proceed(); + + }).start(); + }) + .addPreprocessor((PreChain chain) -> { + new Thread(() -> { + + chain.getTask().addUrlParam("actor", "Alice"); + + chain.proceed(); + + }).start(); + }) + .build(); + + } + + + @Test + public void syncHttpExample() { + + HTTP http = buildHttp(); + // 同步请求 + HttpResult result = http.sync("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584365400942&di=d9e7890f13b7bc4b76080fdd490ed5d5&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D1484500186%2C1503043093%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853") + .get(); + + result.getBody().toFile("E:/3.jpg"); +// // 得到状态码 +// int status = result.getStatus(); +// +// // 得到返回头 +// Headers headers = result.getHeaders(); +// +// User user = result.getBody().toBean(User.class); +// // 得到目标数据 +// +// +// System.out.println("status = " + status); +// System.out.println("headers = " + headers); +// System.out.println("user = " + user); + + System.out.println("status = " + result.getStatus()); + + } + + + @Test + public void syncJsonExample() { + HTTP http = buildHttp(); + // 同步请求 + HttpResult result = http.sync("/user/save") + .addJsonParam("name", "Tom") + .addJsonParam("age", 23) + .post(); + + System.out.println("result = " + result); + + result = http.sync("/user/show/1").get(); + + System.out.println("result = " + result); + + System.out.println("isSuccessful = " + result.isSuccessful()); + } + + @Test + public void asyncHttpExample() throws InterruptedException { + HTTP http = buildHttp(); + // 异步请求 + // 最终路径 http://api.demo.com/users/2 + HttpCall call = http.async("/user/show/{id}") + // 设置路径参数 + .addPathParam("id", 2) + // 设置回调函数 + .setOnResponse((HttpResult result) -> { + System.out.println("000"); +// User user = result.getBody().toBean(User.class); +// System.out.println("user = " + user); + }) + .setOnException((IOException e) -> { + e.printStackTrace(); + }) + // 发起 GET 请求 + .get(); + + Thread.sleep(150); + + System.out.println("是否完成: " + call.isDone()); + System.out.println("是否取消: " + call.isCanceled()); + + call.cancel(); // 取消请求 + + System.out.println("是否取消: " + call.isCanceled()); + System.out.println("执行结果: " + call.getResult()); + System.out.println("是否完成: " + call.isDone()); +// + Thread.sleep(100); +// +// System.out.println("是否完成: " + call.isDone()); +// System.out.println("是否取消: " + call.isCanceled()); +// System.out.println("执行状态: " + call.getResult()); + + } + + + private static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + + static void runOnUiThread(Runnable run) { + run.run(); + } + +} diff --git a/src/test/java/com/ejlchina/test/User.java b/src/test/java/com/ejlchina/test/User.java new file mode 100644 index 00000000..bdf75c9d --- /dev/null +++ b/src/test/java/com/ejlchina/test/User.java @@ -0,0 +1,27 @@ +package com.ejlchina.test; + +public class User { + + int id; + String name; + + public void setId(int id) { + this.id = id; + } + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + public String getName() { + return name; + } + + @Override + public String toString() { + return "User [id=" + id + ", name=" + name + "]"; + } + +} From b77323a58c6dfe593a0203ac2dc15018a220c6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Tue, 31 Mar 2020 14:27:16 +0800 Subject: [PATCH 03/94] =?UTF-8?q?=E6=8A=A5=E5=90=8D=E9=87=8D=E6=9E=84?= =?UTF-8?q?=EF=BC=9Acom.ejlchina.http=20->=20com.ejlchina.okhttps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{http => okhttps}/Configurator.java | 2 +- .../{http => okhttps}/DownListener.java | 2 +- .../ejlchina/{http => okhttps}/Download.java | 6 ++--- .../com/ejlchina/{http => okhttps}/HTTP.java | 10 +++---- .../ejlchina/{http => okhttps}/HttpCall.java | 2 +- .../{http => okhttps}/HttpResult.java | 6 ++--- .../ejlchina/{http => okhttps}/HttpTask.java | 10 +++---- .../ejlchina/{http => okhttps}/HttpUtils.java | 8 +++--- .../{http => okhttps}/OnCallback.java | 2 +- .../{http => okhttps}/Preprocessor.java | 2 +- .../ejlchina/{http => okhttps}/Process.java | 2 +- .../{http => okhttps}/TaskListener.java | 2 +- .../internal/AsyncHttpTask.java | 12 ++++----- .../internal/HttpClient.java | 20 +++++++------- .../internal/HttpException.java | 2 +- .../internal/ProcessInputStream.java | 6 ++--- .../internal/ProcessRequestBody.java | 6 ++--- .../internal/RealHttpResult.java | 6 ++--- .../internal/RealProcess.java | 4 +-- .../internal/ResultBody.java | 12 ++++----- .../internal/SyncHttpTask.java | 8 +++--- .../internal/TaskExecutor.java | 16 ++++++------ src/test/java/com/ejlchina/test/HttpTest.java | 26 +++++++++---------- 23 files changed, 86 insertions(+), 86 deletions(-) rename src/main/java/com/ejlchina/{http => okhttps}/Configurator.java (88%) rename src/main/java/com/ejlchina/{http => okhttps}/DownListener.java (89%) rename src/main/java/com/ejlchina/{http => okhttps}/Download.java (97%) rename src/main/java/com/ejlchina/{http => okhttps}/HTTP.java (84%) rename src/main/java/com/ejlchina/{http => okhttps}/HttpCall.java (94%) rename src/main/java/com/ejlchina/{http => okhttps}/HttpResult.java (97%) rename src/main/java/com/ejlchina/{http => okhttps}/HttpTask.java (98%) rename src/main/java/com/ejlchina/{http => okhttps}/HttpUtils.java (92%) rename src/main/java/com/ejlchina/{http => okhttps}/OnCallback.java (80%) rename src/main/java/com/ejlchina/{http => okhttps}/Preprocessor.java (94%) rename src/main/java/com/ejlchina/{http => okhttps}/Process.java (92%) rename src/main/java/com/ejlchina/{http => okhttps}/TaskListener.java (91%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/AsyncHttpTask.java (96%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/HttpClient.java (96%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/HttpException.java (91%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/ProcessInputStream.java (90%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/ProcessRequestBody.java (95%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/RealHttpResult.java (95%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/RealProcess.java (91%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/ResultBody.java (97%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/SyncHttpTask.java (92%) rename src/main/java/com/ejlchina/{http => okhttps}/internal/TaskExecutor.java (89%) diff --git a/src/main/java/com/ejlchina/http/Configurator.java b/src/main/java/com/ejlchina/okhttps/Configurator.java similarity index 88% rename from src/main/java/com/ejlchina/http/Configurator.java rename to src/main/java/com/ejlchina/okhttps/Configurator.java index c76a5a85..54f954ad 100644 --- a/src/main/java/com/ejlchina/http/Configurator.java +++ b/src/main/java/com/ejlchina/okhttps/Configurator.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; import okhttp3.OkHttpClient.Builder; diff --git a/src/main/java/com/ejlchina/http/DownListener.java b/src/main/java/com/ejlchina/okhttps/DownListener.java similarity index 89% rename from src/main/java/com/ejlchina/http/DownListener.java rename to src/main/java/com/ejlchina/okhttps/DownListener.java index 7802013a..53cbebed 100644 --- a/src/main/java/com/ejlchina/http/DownListener.java +++ b/src/main/java/com/ejlchina/okhttps/DownListener.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; /** * 下载监听接口 diff --git a/src/main/java/com/ejlchina/http/Download.java b/src/main/java/com/ejlchina/okhttps/Download.java similarity index 97% rename from src/main/java/com/ejlchina/http/Download.java rename to src/main/java/com/ejlchina/okhttps/Download.java index c7ef89bd..87764862 100644 --- a/src/main/java/com/ejlchina/http/Download.java +++ b/src/main/java/com/ejlchina/okhttps/Download.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; import java.io.File; import java.io.FileNotFoundException; @@ -6,8 +6,8 @@ import java.io.InputStream; import java.io.RandomAccessFile; -import com.ejlchina.http.internal.HttpException; -import com.ejlchina.http.internal.TaskExecutor; +import com.ejlchina.okhttps.internal.HttpException; +import com.ejlchina.okhttps.internal.TaskExecutor; import okhttp3.internal.Util; diff --git a/src/main/java/com/ejlchina/http/HTTP.java b/src/main/java/com/ejlchina/okhttps/HTTP.java similarity index 84% rename from src/main/java/com/ejlchina/http/HTTP.java rename to src/main/java/com/ejlchina/okhttps/HTTP.java index b8fc7c4b..2f6a54df 100644 --- a/src/main/java/com/ejlchina/http/HTTP.java +++ b/src/main/java/com/ejlchina/okhttps/HTTP.java @@ -1,9 +1,9 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; -import com.ejlchina.http.internal.AsyncHttpTask; -import com.ejlchina.http.internal.SyncHttpTask; -import com.ejlchina.http.internal.TaskExecutor; -import com.ejlchina.http.internal.HttpClient.Builder; +import com.ejlchina.okhttps.internal.AsyncHttpTask; +import com.ejlchina.okhttps.internal.SyncHttpTask; +import com.ejlchina.okhttps.internal.TaskExecutor; +import com.ejlchina.okhttps.internal.HttpClient.Builder; import okhttp3.Call; import okhttp3.Request; diff --git a/src/main/java/com/ejlchina/http/HttpCall.java b/src/main/java/com/ejlchina/okhttps/HttpCall.java similarity index 94% rename from src/main/java/com/ejlchina/http/HttpCall.java rename to src/main/java/com/ejlchina/okhttps/HttpCall.java index 940d5749..8a59cd14 100644 --- a/src/main/java/com/ejlchina/http/HttpCall.java +++ b/src/main/java/com/ejlchina/okhttps/HttpCall.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; public interface HttpCall { diff --git a/src/main/java/com/ejlchina/http/HttpResult.java b/src/main/java/com/ejlchina/okhttps/HttpResult.java similarity index 97% rename from src/main/java/com/ejlchina/http/HttpResult.java rename to src/main/java/com/ejlchina/okhttps/HttpResult.java index f1d67b8a..c3d51d96 100644 --- a/src/main/java/com/ejlchina/http/HttpResult.java +++ b/src/main/java/com/ejlchina/okhttps/HttpResult.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; import java.io.File; import java.io.InputStream; @@ -7,8 +7,8 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.ejlchina.http.internal.RealHttpResult; -import com.ejlchina.http.internal.TaskExecutor; +import com.ejlchina.okhttps.internal.RealHttpResult; +import com.ejlchina.okhttps.internal.TaskExecutor; import okhttp3.Headers; import okhttp3.MediaType; diff --git a/src/main/java/com/ejlchina/http/HttpTask.java b/src/main/java/com/ejlchina/okhttps/HttpTask.java similarity index 98% rename from src/main/java/com/ejlchina/http/HttpTask.java rename to src/main/java/com/ejlchina/okhttps/HttpTask.java index 04ccf72c..ae6067be 100644 --- a/src/main/java/com/ejlchina/http/HttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/HttpTask.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; import java.io.File; import java.io.IOException; @@ -10,10 +10,10 @@ import java.util.Map; import com.alibaba.fastjson.JSON; -import com.ejlchina.http.HttpResult.State; -import com.ejlchina.http.internal.HttpClient; -import com.ejlchina.http.internal.HttpException; -import com.ejlchina.http.internal.ProcessRequestBody; +import com.ejlchina.okhttps.HttpResult.State; +import com.ejlchina.okhttps.internal.HttpClient; +import com.ejlchina.okhttps.internal.HttpException; +import com.ejlchina.okhttps.internal.ProcessRequestBody; import okhttp3.Call; import okhttp3.FormBody; diff --git a/src/main/java/com/ejlchina/http/HttpUtils.java b/src/main/java/com/ejlchina/okhttps/HttpUtils.java similarity index 92% rename from src/main/java/com/ejlchina/http/HttpUtils.java rename to src/main/java/com/ejlchina/okhttps/HttpUtils.java index f7b7ccb5..96dca13d 100644 --- a/src/main/java/com/ejlchina/http/HttpUtils.java +++ b/src/main/java/com/ejlchina/okhttps/HttpUtils.java @@ -1,8 +1,8 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; -import com.ejlchina.http.internal.AsyncHttpTask; -import com.ejlchina.http.internal.SyncHttpTask; -import com.ejlchina.http.internal.TaskExecutor; +import com.ejlchina.okhttps.internal.AsyncHttpTask; +import com.ejlchina.okhttps.internal.SyncHttpTask; +import com.ejlchina.okhttps.internal.TaskExecutor; import okhttp3.Call; import okhttp3.Request; diff --git a/src/main/java/com/ejlchina/http/OnCallback.java b/src/main/java/com/ejlchina/okhttps/OnCallback.java similarity index 80% rename from src/main/java/com/ejlchina/http/OnCallback.java rename to src/main/java/com/ejlchina/okhttps/OnCallback.java index 32dd81e8..bfeeaa3e 100644 --- a/src/main/java/com/ejlchina/http/OnCallback.java +++ b/src/main/java/com/ejlchina/okhttps/OnCallback.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; /** * 数据回调接口 diff --git a/src/main/java/com/ejlchina/http/Preprocessor.java b/src/main/java/com/ejlchina/okhttps/Preprocessor.java similarity index 94% rename from src/main/java/com/ejlchina/http/Preprocessor.java rename to src/main/java/com/ejlchina/okhttps/Preprocessor.java index ee2f757c..d0f6f96c 100644 --- a/src/main/java/com/ejlchina/http/Preprocessor.java +++ b/src/main/java/com/ejlchina/okhttps/Preprocessor.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; /** * 预处理器,支持异步 diff --git a/src/main/java/com/ejlchina/http/Process.java b/src/main/java/com/ejlchina/okhttps/Process.java similarity index 92% rename from src/main/java/com/ejlchina/http/Process.java rename to src/main/java/com/ejlchina/okhttps/Process.java index aff09bd3..6f36f827 100644 --- a/src/main/java/com/ejlchina/http/Process.java +++ b/src/main/java/com/ejlchina/okhttps/Process.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; /** * 进度(上传或下载) diff --git a/src/main/java/com/ejlchina/http/TaskListener.java b/src/main/java/com/ejlchina/okhttps/TaskListener.java similarity index 91% rename from src/main/java/com/ejlchina/http/TaskListener.java rename to src/main/java/com/ejlchina/okhttps/TaskListener.java index 90c2ada2..54f734d1 100644 --- a/src/main/java/com/ejlchina/http/TaskListener.java +++ b/src/main/java/com/ejlchina/okhttps/TaskListener.java @@ -1,4 +1,4 @@ -package com.ejlchina.http; +package com.ejlchina.okhttps; /** * 任务监听接口 diff --git a/src/main/java/com/ejlchina/http/internal/AsyncHttpTask.java b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java similarity index 96% rename from src/main/java/com/ejlchina/http/internal/AsyncHttpTask.java rename to src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java index c55985a8..cadc760c 100644 --- a/src/main/java/com/ejlchina/http/internal/AsyncHttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java @@ -1,12 +1,12 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; import java.io.IOException; -import com.ejlchina.http.HttpCall; -import com.ejlchina.http.HttpResult; -import com.ejlchina.http.HttpResult.State; -import com.ejlchina.http.HttpTask; -import com.ejlchina.http.OnCallback; +import com.ejlchina.okhttps.HttpCall; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpTask; +import com.ejlchina.okhttps.OnCallback; +import com.ejlchina.okhttps.HttpResult.State; import okhttp3.Call; import okhttp3.Callback; diff --git a/src/main/java/com/ejlchina/http/internal/HttpClient.java b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java similarity index 96% rename from src/main/java/com/ejlchina/http/internal/HttpClient.java rename to src/main/java/com/ejlchina/okhttps/internal/HttpClient.java index 6d224c45..cdc0c85e 100644 --- a/src/main/java/com/ejlchina/http/internal/HttpClient.java +++ b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java @@ -1,4 +1,4 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; import java.io.IOException; import java.util.ArrayList; @@ -11,15 +11,15 @@ import java.util.Queue; import java.util.concurrent.Executor; -import com.ejlchina.http.Configurator; -import com.ejlchina.http.DownListener; -import com.ejlchina.http.HTTP; -import com.ejlchina.http.HttpCall; -import com.ejlchina.http.HttpResult; -import com.ejlchina.http.HttpResult.State; -import com.ejlchina.http.HttpTask; -import com.ejlchina.http.Preprocessor; -import com.ejlchina.http.TaskListener; +import com.ejlchina.okhttps.Configurator; +import com.ejlchina.okhttps.DownListener; +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpCall; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpTask; +import com.ejlchina.okhttps.Preprocessor; +import com.ejlchina.okhttps.TaskListener; +import com.ejlchina.okhttps.HttpResult.State; import okhttp3.Call; import okhttp3.MediaType; diff --git a/src/main/java/com/ejlchina/http/internal/HttpException.java b/src/main/java/com/ejlchina/okhttps/internal/HttpException.java similarity index 91% rename from src/main/java/com/ejlchina/http/internal/HttpException.java rename to src/main/java/com/ejlchina/okhttps/internal/HttpException.java index f78606cf..c2bf3f37 100644 --- a/src/main/java/com/ejlchina/http/internal/HttpException.java +++ b/src/main/java/com/ejlchina/okhttps/internal/HttpException.java @@ -1,4 +1,4 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; /** * Created by 周旭(Troy.Zhou) on 2016/8/30. diff --git a/src/main/java/com/ejlchina/http/internal/ProcessInputStream.java b/src/main/java/com/ejlchina/okhttps/internal/ProcessInputStream.java similarity index 90% rename from src/main/java/com/ejlchina/http/internal/ProcessInputStream.java rename to src/main/java/com/ejlchina/okhttps/internal/ProcessInputStream.java index 779ddc30..77cf2e91 100644 --- a/src/main/java/com/ejlchina/http/internal/ProcessInputStream.java +++ b/src/main/java/com/ejlchina/okhttps/internal/ProcessInputStream.java @@ -1,11 +1,11 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Executor; -import com.ejlchina.http.OnCallback; -import com.ejlchina.http.Process; +import com.ejlchina.okhttps.OnCallback; +import com.ejlchina.okhttps.Process; public class ProcessInputStream extends InputStream { diff --git a/src/main/java/com/ejlchina/http/internal/ProcessRequestBody.java b/src/main/java/com/ejlchina/okhttps/internal/ProcessRequestBody.java similarity index 95% rename from src/main/java/com/ejlchina/http/internal/ProcessRequestBody.java rename to src/main/java/com/ejlchina/okhttps/internal/ProcessRequestBody.java index 6f406b54..c1f50f55 100644 --- a/src/main/java/com/ejlchina/http/internal/ProcessRequestBody.java +++ b/src/main/java/com/ejlchina/okhttps/internal/ProcessRequestBody.java @@ -1,10 +1,10 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; import java.io.IOException; import java.util.concurrent.Executor; -import com.ejlchina.http.OnCallback; -import com.ejlchina.http.Process; +import com.ejlchina.okhttps.OnCallback; +import com.ejlchina.okhttps.Process; import okhttp3.MediaType; import okhttp3.RequestBody; diff --git a/src/main/java/com/ejlchina/http/internal/RealHttpResult.java b/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java similarity index 95% rename from src/main/java/com/ejlchina/http/internal/RealHttpResult.java rename to src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java index 697fd4a6..1218e4a0 100644 --- a/src/main/java/com/ejlchina/http/internal/RealHttpResult.java +++ b/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java @@ -1,9 +1,9 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; import java.util.List; -import com.ejlchina.http.HttpResult; -import com.ejlchina.http.HttpTask; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpTask; import okhttp3.Headers; import okhttp3.Response; diff --git a/src/main/java/com/ejlchina/http/internal/RealProcess.java b/src/main/java/com/ejlchina/okhttps/internal/RealProcess.java similarity index 91% rename from src/main/java/com/ejlchina/http/internal/RealProcess.java rename to src/main/java/com/ejlchina/okhttps/internal/RealProcess.java index 700d2895..895583e3 100644 --- a/src/main/java/com/ejlchina/http/internal/RealProcess.java +++ b/src/main/java/com/ejlchina/okhttps/internal/RealProcess.java @@ -1,6 +1,6 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; -import com.ejlchina.http.Process; +import com.ejlchina.okhttps.Process; public class RealProcess implements Process { diff --git a/src/main/java/com/ejlchina/http/internal/ResultBody.java b/src/main/java/com/ejlchina/okhttps/internal/ResultBody.java similarity index 97% rename from src/main/java/com/ejlchina/http/internal/ResultBody.java rename to src/main/java/com/ejlchina/okhttps/internal/ResultBody.java index aa827e75..1123668e 100644 --- a/src/main/java/com/ejlchina/http/internal/ResultBody.java +++ b/src/main/java/com/ejlchina/okhttps/internal/ResultBody.java @@ -1,4 +1,4 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; import static java.nio.charset.StandardCharsets.UTF_8; @@ -17,11 +17,11 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.ejlchina.http.Download; -import com.ejlchina.http.HttpTask; -import com.ejlchina.http.HttpResult.Body; -import com.ejlchina.http.OnCallback; -import com.ejlchina.http.Process; +import com.ejlchina.okhttps.Download; +import com.ejlchina.okhttps.HttpTask; +import com.ejlchina.okhttps.OnCallback; +import com.ejlchina.okhttps.Process; +import com.ejlchina.okhttps.HttpResult.Body; import okhttp3.MediaType; import okhttp3.Response; diff --git a/src/main/java/com/ejlchina/http/internal/SyncHttpTask.java b/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java similarity index 92% rename from src/main/java/com/ejlchina/http/internal/SyncHttpTask.java rename to src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java index 8ff98b5b..648c8357 100644 --- a/src/main/java/com/ejlchina/http/internal/SyncHttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java @@ -1,10 +1,10 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; import java.io.IOException; -import com.ejlchina.http.HttpResult; -import com.ejlchina.http.HttpResult.State; -import com.ejlchina.http.HttpTask; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpTask; +import com.ejlchina.okhttps.HttpResult.State; import okhttp3.Call; import okhttp3.Response; diff --git a/src/main/java/com/ejlchina/http/internal/TaskExecutor.java b/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java similarity index 89% rename from src/main/java/com/ejlchina/http/internal/TaskExecutor.java rename to src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java index 419bc56c..fbb164b1 100644 --- a/src/main/java/com/ejlchina/http/internal/TaskExecutor.java +++ b/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java @@ -1,17 +1,17 @@ -package com.ejlchina.http.internal; +package com.ejlchina.okhttps.internal; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Executor; -import com.ejlchina.http.DownListener; -import com.ejlchina.http.Download; -import com.ejlchina.http.HttpResult; -import com.ejlchina.http.HttpResult.State; -import com.ejlchina.http.HttpTask; -import com.ejlchina.http.OnCallback; -import com.ejlchina.http.TaskListener; +import com.ejlchina.okhttps.DownListener; +import com.ejlchina.okhttps.Download; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpTask; +import com.ejlchina.okhttps.OnCallback; +import com.ejlchina.okhttps.TaskListener; +import com.ejlchina.okhttps.HttpResult.State; public class TaskExecutor { diff --git a/src/test/java/com/ejlchina/test/HttpTest.java b/src/test/java/com/ejlchina/test/HttpTest.java index 6573da97..e8951dab 100644 --- a/src/test/java/com/ejlchina/test/HttpTest.java +++ b/src/test/java/com/ejlchina/test/HttpTest.java @@ -8,19 +8,19 @@ import org.junit.Test; -import com.ejlchina.http.Download; -import com.ejlchina.http.Download.Ctrl; -import com.ejlchina.http.Download.Failure; -import com.ejlchina.http.HTTP; -import com.ejlchina.http.HttpCall; -import com.ejlchina.http.HttpResult; -import com.ejlchina.http.HttpResult.Body; -import com.ejlchina.http.HttpResult.State; -import com.ejlchina.http.HttpTask; -import com.ejlchina.http.HttpUtils; -import com.ejlchina.http.Preprocessor.PreChain; -import com.ejlchina.http.Process; -import com.ejlchina.http.internal.HttpClient; +import com.ejlchina.okhttps.Download; +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpCall; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpTask; +import com.ejlchina.okhttps.HttpUtils; +import com.ejlchina.okhttps.Process; +import com.ejlchina.okhttps.Download.Ctrl; +import com.ejlchina.okhttps.Download.Failure; +import com.ejlchina.okhttps.HttpResult.Body; +import com.ejlchina.okhttps.HttpResult.State; +import com.ejlchina.okhttps.Preprocessor.PreChain; +import com.ejlchina.okhttps.internal.HttpClient; import okhttp3.ConnectionPool; import okhttp3.Interceptor.Chain; From 09b68905fe90b686572c5f7ed268062439663d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Tue, 31 Mar 2020 14:28:05 +0800 Subject: [PATCH 04/94] name: HttpUtils -> OkHttps, version: 1.0.0 --- pom.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index f434d4c5..c322c04d 100644 --- a/pom.xml +++ b/pom.xml @@ -2,9 +2,9 @@ 4.0.0 com.ejlchina - httputils - 2.3.0 - HttpUtils + okhttps + 1.0.0 + OkHttps 1.8 @@ -29,9 +29,9 @@ - scm:git://gitee.com/ejlchina-zhxu/httputils.git - scm:http://gitee.com/ejlchina-zhxu/httputils - https://gitee.com/ejlchina-zhxu/httputils + scm:git://gitee.com/ejlchina-zhxu/okhttps.git + scm:http://gitee.com/ejlchina-zhxu/okhttps + https://gitee.com/ejlchina-zhxu/okhttps v2.2.0 @@ -86,7 +86,7 @@ - httputils + okhttps @@ -157,13 +157,13 @@ - https://gitee.com/ejlchina-zhxu/httputils + https://gitee.com/ejlchina-zhxu/okhttps Http工具包,封装 OkHttp,链式用法、支持异步同步、响应JSON自动解析,回调线程切换、配置灵活、支持URL占位符、支持BaseUrl、外部依赖少、适用 JavaWeb 和 Android,教程全面、简单易用 江苏一键联信息科技有限公司 http://www.ejlchina.com/ - https://gitee.com/ejlchina-zhxu/httputils/issues + https://gitee.com/ejlchina-zhxu/okhttps/issues \ No newline at end of file From 5e2a49147a4924b39fdcc842d35bdac00d6d7eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Tue, 31 Mar 2020 14:32:38 +0800 Subject: [PATCH 05/94] =?UTF-8?q?HttpUtils=20of=20=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E4=B8=BA=E5=BC=82=E5=B8=B8=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/HttpUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/ejlchina/okhttps/HttpUtils.java b/src/main/java/com/ejlchina/okhttps/HttpUtils.java index 96dca13d..a03e365f 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpUtils.java +++ b/src/main/java/com/ejlchina/okhttps/HttpUtils.java @@ -36,6 +36,8 @@ public class HttpUtils { public static void of(HTTP http) { if (http != null) { HttpUtils.http = http; + } else { + throw new IllegalArgumentException("Parameter http can not be null!"); } } From 2940751fa88647554f9a5775409ad67266008e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Tue, 31 Mar 2020 15:13:42 +0800 Subject: [PATCH 06/94] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index b83e8387..fb708986 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -# HttpUtils +# OkHttps -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.ejlchina/httputils/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.ejlchina/httputils/) -[![License](https://img.shields.io/hexpm/l/plug.svg)](https://gitee.com/ejlchina-zhxu/httputils/blob/master/LICENSE) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.ejlchina/okhttps/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.ejlchina/okhttps/) +[![License](https://img.shields.io/hexpm/l/plug.svg)](https://gitee.com/ejlchina-zhxu/okhttps/blob/master/LICENSE) [![Troy.Zhou](https://img.shields.io/badge/%E4%BD%9C%E8%80%85-ejlchina-orange.svg)](https://github.com/ejlchina) ## 介绍 -  HttpUtils 是近期开源的对 OkHttp 轻量封装的框架,它独创的异步预处理器,特色的标签,灵活的上传下载进度监听与过程控制功能,在轻松解决很多原本另人头疼问题的同时,设计上也力求纯粹与优雅。 +  OkHttps 是近期开源的对 OkHttp3 轻量封装的框架,它独创的异步预处理器,特色的标签,灵活的上传下载进度监听与过程控制功能,在轻松解决很多原本另人头疼问题的同时,设计上也力求纯粹与优雅。 * 链式调用,一点到底 * BaseURL、URL占位符、JSON自动封装与解析 @@ -14,8 +14,7 @@ * 文件上传下载(过程控制、进度监听) * TCP连接池、Http2 -### 当前文档版本[2.3.0](近期发布到中央仓库) -#### [查阅[2.1.x]点我](https://gitee.com/ejlchina-zhxu/httputils/blob/master/README-2.1.2.md) | [查阅[2.0.x]点我](https://gitee.com/ejlchina-zhxu/httputils/blob/master/README-2.0.0.md) | [查阅[1.0.x]点我](https://gitee.com/ejlchina-zhxu/httputils/blob/1.x/README.md) +### 当前文档版本[1.0.0](近期发布到中央仓库) ## 目录 @@ -62,13 +61,13 @@ ``` com.ejlchina - httputils - 2.3.0 + okhttps + 1.0.0 ``` ### Gradle -`compile 'com.ejlchina:httputils:2.3.0'` +`compile 'com.ejlchina:okhttps:1.0.0'` ## 使用说明 @@ -345,7 +344,7 @@ HTTP http = HTTP.builder() #### 6.3 配置 OkHttpClient -  与其他封装 OkHttp 的框架不同,HttpUtils 并不会遮蔽 OkHttp 本身就很好用的功能,如下: +  与其他封装 OkHttp3 的框架不同,OkHttps 并不会遮蔽 OkHttp3 本身就很好用的功能,如下: ```java HTTP http = HTTP.builder() @@ -391,7 +390,7 @@ HTTP http = HTTP.builder()   普通预处理器都是可并行处理的,然而有时我们希望某个预处理器同时只处理一个任务。比如 当`Token`过期时我们需要去刷新获取新`Token`,而刷新`Token`这个操作只能有一个任务去执行,因为如果`n`个任务同时执行的话,那么必有`n-1`个任务刚刷新得到的`Token`可能就立马失效了,而这是我们所不希望的。 -  为了解决这个问题,HttpUtils 提供了串行预处理器,它可以让 HTTP 任务排好队,一个一个地进入预处理器: +  为了解决这个问题,OkHttps 提供了串行预处理器,它可以让 HTTP 任务排好队,一个一个地进入预处理器: ```java HTTP http = HTTP.builder() @@ -474,7 +473,7 @@ List users = HttpUtils.sync("/users") ### 8 文件下载 -  HttpUtils 并没有把文件的下载排除在常规的请求之外,同一套API,它优雅的设计使得下载与常规请求融合的毫无违和感,一个最简单的示例: +  OkHttps 并没有把文件的下载排除在常规的请求之外,同一套API,它优雅的设计使得下载与常规请求融合的毫无违和感,一个最简单的示例: ```java http.sync("/download/test.zip") @@ -517,7 +516,7 @@ http.sync("/download/test.zip") }) .start(); ``` -  值得一提的是:由于 HttpUtils 并没有把下载做的很特别,这里设置的进度回调不只对下载文件起用作,即使对响应JSON的常规请求,只要设置了进度回调,它也会告诉你报文接收的进度(提前是服务器响应的报文有`Content-Length`头),例如: +  值得一提的是:由于 OkHttps 并没有把下载做的很特别,这里设置的进度回调不只对下载文件起用作,即使对响应JSON的常规请求,只要设置了进度回调,它也会告诉你报文接收的进度(提前是服务器响应的报文有`Content-Length`头),例如: ```java List users = http.sync("/users") @@ -562,7 +561,7 @@ http.async("/download/test.zip") #### 8.3 实现断点续传 -  HttpUtils 对断点续传并没有再做更高层次的封装,因为这是app该去做的事情,它在设计上使各种网络问题的处理变简单的同时力求纯粹。下面的例子可以看到,HttpUtils 通过一个失败回调拿到**断点**,便将复杂的问题变得简单: +  OkHttps 对断点续传并没有再做更高层次的封装,因为这是app该去做的事情,它在设计上使各种网络问题的处理变简单的同时力求纯粹。下面的例子可以看到,OkHttps 通过一个失败回调拿到**断点**,便将复杂的问题变得简单: ```java http.sync("/download/test.zip") @@ -651,7 +650,7 @@ http.async("/upload") #### 9.1 上传进度监听 -  HttpUtils 的上传进度监听,监听的是所有请求报文体的发送进度,示例代码: +  OkHttps 的上传进度监听,监听的是所有请求报文体的发送进度,示例代码: ```java http.sync("/upload") @@ -668,7 +667,7 @@ http.sync("/upload") }) .post() ``` -  咦!怎么感觉和下载的进度回调的一样?没错!HttpUtils 还是使用同一套API处理上传和下载的进度回调,区别只在于上传是在`get/post`方法之前使用这些API,下载是在`getBody`方法之后使用。很好理解:`get/post`之前是准备发送请求时段,有上传的含义,而`getBody`之后,已是报文响应的时段,当然是下载。 +  咦!怎么感觉和下载的进度回调的一样?没错!OkHttps 还是使用同一套API处理上传和下载的进度回调,区别只在于上传是在`get/post`方法之前使用这些API,下载是在`getBody`方法之后使用。很好理解:`get/post`之前是准备发送请求时段,有上传的含义,而`getBody`之后,已是报文响应的时段,当然是下载。 #### 9.2 上传过程控制 @@ -688,11 +687,11 @@ call.cancel(); // 取消上传 ### 10 执行线程自由切换(for Android) -  在 Android 开发中,经常会把某些代码放到特点的线程去执行,比如网络请求响应后的页面更新在主线程(UI线程)执行,而保存文件则在IO线程操作。HttpUtils 为这类问题提供了良好的方案。 +  在 Android 开发中,经常会把某些代码放到特点的线程去执行,比如网络请求响应后的页面更新在主线程(UI线程)执行,而保存文件则在IO线程操作。OkHttps 为这类问题提供了良好的方案。 -  在 **默认** 情况下,**所有回调** 函数都会 **在 IO 线程** 执行。为什么会设计如此呢?这是因为 HttpUtils 只是纯粹的 Java 领域 Http工具包,本身对 Android 不会有任何依赖,因此也不知 Android 的 UI 线程为何物。这么设计也让它在 Android 之外有更多的可能性。 +  在 **默认** 情况下,**所有回调** 函数都会 **在 IO 线程** 执行。为什么会设计如此呢?这是因为 OkHttps 只是纯粹的 Java 领域 Http工具包,本身对 Android 不会有任何依赖,因此也不知 Android 的 UI 线程为何物。这么设计也让它在 Android 之外有更多的可能性。 -  但是在 Android 里使用 HttpUtils 的话,UI线程的问题能否优雅的解决呢?当然可以!简单粗暴的方法就是配置一个 回调执行器: +  但是在 Android 里使用 OkHttps 的话,UI线程的问题能否优雅的解决呢?当然可以!简单粗暴的方法就是配置一个 回调执行器: ```java HTTP http = HTTP.builder() @@ -721,7 +720,7 @@ http.async("/users") }) .post(); ``` -  但是,如果同时还想让某些回调放在IO线程,实现 **自由切换**,怎么办呢?HttpUtils 给出了非常灵活的方法,如下: +  但是,如果同时还想让某些回调放在IO线程,实现 **自由切换**,怎么办呢?OkHttps 给出了非常灵活的方法,如下: ```java http.async("/users") From 869c2917f8366a61b996a09dc2f4aba18816201f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Tue, 31 Mar 2020 16:13:15 +0800 Subject: [PATCH 07/94] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fb708986..975d597d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ * 文件上传下载(过程控制、进度监听) * TCP连接池、Http2 +### 前身:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.0](近期发布到中央仓库) ## 目录 From c37f31fa75868fb4ca5424a08431ffb0cfe6cc39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Tue, 31 Mar 2020 16:14:57 +0800 Subject: [PATCH 08/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 975d597d..6a00fcec 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ * 文件上传下载(过程控制、进度监听) * TCP连接池、Http2 -### 前身:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) +### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.0](近期发布到中央仓库) ## 目录 From e53ca139c42b4252ab6232c9dfe1f6656b833bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Tue, 31 Mar 2020 16:19:23 +0800 Subject: [PATCH 09/94] . --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a00fcec..bb89e92d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ * TCP连接池、Http2 ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) -### 当前文档版本[1.0.0](近期发布到中央仓库) +### 当前文档版本[1.0.0] ## 目录 From 3f0cfca90433208ae0deed9f3670533e0f64b4e3 Mon Sep 17 00:00:00 2001 From: ejlchina Date: Tue, 7 Apr 2020 16:36:43 +0800 Subject: [PATCH 10/94] update README.md. --- README.md | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index bb89e92d..a8531e9d 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ http.async("/users/1") // http://api.demo.com/users/1 ```java HttpResult res1 = http.sync("/users").get(); // 同步 GET -HttpResult res2 = http.sync("/users")post(); // 同步 POST +HttpResult res2 = http.sync("/users").post(); // 同步 POST HttpResult res3 = http.sync("/users/1").put(); // 同步 PUT HttpResult res4 = http.sync("/users/1").delete();// 同步 DELETE HttpCall call1 = http.async("/users").get(); // 异步 GET @@ -202,21 +202,15 @@ List orders = http.sync("/orders") .get().getBody().toList(Order.class); ``` -  示例,下载文件到指定目录: +  示例,使用 cache 方法: ```java -String path = "D:/reports/2020-03-01.xlsx"; // 文件保存目录 +Body body = http.sync("/orders").get().getBody().cache(); -// 同步下载 -http.sync("/reports/2020-03-01.xlsx") - .get().getBody().toFile(path); - -// 异步下载 -http.async("/reports/2020-03-01.xlsx") - .setOnResponse((HttpResult result) -> { - result.getBody().toFile(path); - }) - .get(); +// 使用 cache 后,可以多次使用 toXXX() 方法 +System.out.println(body.toString()); +System.out.println(body.toJsonArray()); +System.out.println(body.toList(Order.class)); ``` #### 3.3 HttpCall From f56b83679bca4fbb4e9153e13c21a4da7d47784e Mon Sep 17 00:00:00 2001 From: ejlchina Date: Tue, 7 Apr 2020 16:45:34 +0800 Subject: [PATCH 11/94] update README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a8531e9d..80223ac7 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ http.async("/users/{id}") // http://api.demo.com/users/1 * `getHeaders()` 得到HTTP响应头 * `getHeaders(String name)` 得到HTTP响应头 * `getHeader(String name)` 得到HTTP响应头 -* `getBody()` 得到响应报文体`Body`实例,它定义了如下方法(对同一个`Body`实例,以下的`toXXX()`类方法只能使用一个且仅能调用一次): +* `getBody()` 得到响应报文体`Body`实例,它定义了如下方法(对同一个`Body`实例,以下的`toXXX()`类方法只能使用一个且仅能调用一次,除非先使用 cache 方法): * `toBytes()` 返回字节数组 * `toByteStream()` 返回字节输入流 * `toCharStream()` 返回字符输入流 From 7c75812ad7aa0853e7e08f84bc8fd3280d96071b Mon Sep 17 00:00:00 2001 From: ejlchina Date: Tue, 7 Apr 2020 16:53:36 +0800 Subject: [PATCH 12/94] update README.md. --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 80223ac7..57e94c78 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,20 @@ System.out.println(body.toJsonArray()); System.out.println(body.toList(Order.class)); ``` +  示例,获取下载文件的大小: + +```java +long size = http.sync("http://api.demo.com/download/test.zip") + .get().getBody() + .close() // 只是想获得文件大小,不消费报文体,所以直接关闭 + .getContentLength(); // 获得待下载文件的大小 + +// 由于未消费报文体,所以本次请求不会消耗下载报文体的时间和网络流量) + +System.out.println("size = " + size); +``` + + #### 3.3 HttpCall   `HttpCall`对象是异步请求方法(`get`、`post`、`put`、`delete`)的返回值,与`java`的`Future`接口很像,它有如下方法: From 1a7b8db91141e3273a10ee1ed4779a1daefdbb36 Mon Sep 17 00:00:00 2001 From: ejlchina Date: Tue, 7 Apr 2020 17:02:41 +0800 Subject: [PATCH 13/94] update README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 57e94c78..7a9c954b 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ - [6.2 回调执行器](#62-回调执行器) - [6.3 配置 OkHttpClient](#63-配置-okhttpclient) - [6.4 并行预处理器](#64-并行预处理器) - - [6.5 串行预处理器](#65-串行预处理器) + - [6.5 串行预处理器(Token问题最优雅的解决方案)](#65-串行预处理器) - [6.6 全局回调处理](#66-全局回调处理) - [6.7 全局下载监听](#67-全局下载监听) + [7 使用 HttpUtils 类](#7-使用-httputils-类) From 0e2b9cd443d8c26de48a424a12fb54c5a07161f5 Mon Sep 17 00:00:00 2001 From: ejlchina Date: Tue, 7 Apr 2020 22:37:21 +0800 Subject: [PATCH 14/94] update README.md. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7a9c954b..0b86c262 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.0] +### API文档:[https://apidoc.gitee.com/ejlchina-zhxu/okhttps](https://apidoc.gitee.com/ejlchina-zhxu/okhttps) ## 目录 From 0c14c2dfba96883de1069a27107e4e6e25469ce0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= <15735559897@qq.com> Date: Tue, 7 Apr 2020 23:32:23 +0800 Subject: [PATCH 15/94] =?UTF-8?q?=E4=BC=98=E5=8C=96=20HttpResult=20?= =?UTF-8?q?=E7=9A=84=E5=AE=9E=E7=8E=B0=EF=BC=8C=201=E3=80=81=E5=BD=93?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E7=8A=B6=E6=80=81=20!=3D=20State.RESPONSED?= =?UTF-8?q?=20=E6=97=B6=EF=BC=8C=E8=B0=83=E7=94=A8=20getHeaders=20?= =?UTF-8?q?=E7=AD=89=E6=96=B9=E6=B3=95=E6=97=B6=E4=B8=8D=E5=86=8D=E6=8A=9B?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=202=E3=80=81HttpResult.toString=20=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E7=A7=BB=E9=99=A4=E5=AF=B9=20Body.toString=20?= =?UTF-8?q?=E7=9A=84=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../okhttps/internal/RealHttpResult.java | 274 +++++++++--------- 1 file changed, 140 insertions(+), 134 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java b/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java index 1218e4a0..c9326d36 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java +++ b/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java @@ -1,134 +1,140 @@ -package com.ejlchina.okhttps.internal; - -import java.util.List; - -import com.ejlchina.okhttps.HttpResult; -import com.ejlchina.okhttps.HttpTask; - -import okhttp3.Headers; -import okhttp3.Response; - - -public class RealHttpResult implements HttpResult { - - private State state; - private Response response; - private Exception error; - private TaskExecutor taskExecutor; - private HttpTask httpTask; - - - public RealHttpResult(HttpTask httpTask, State state) { - this.httpTask = httpTask; - this.state = state; - } - - public RealHttpResult(HttpTask httpTask, Response response, TaskExecutor taskExecutor) { - this(httpTask, taskExecutor); - response(response); - } - - public RealHttpResult(HttpTask httpTask, TaskExecutor taskExecutor) { - this.httpTask = httpTask; - this.taskExecutor = taskExecutor; - } - - public RealHttpResult(HttpTask httpTask, State state, Exception error) { - this.httpTask = httpTask; - exception(state, error); - } - - - public void exception(State state, Exception error) { - this.state = state; - this.error = error; - } - - public void response(Response response) { - this.state = State.RESPONSED; - this.response = response; - } - - @Override - public HttpTask getTask() { - return httpTask; - } - - @Override - public State getState() { - return state; - } - - @Override - public int getStatus() { - if (response != null) { - return response.code(); - } - return 0; - } - - @Override - public boolean isSuccessful() { - if (response != null) { - return response.isSuccessful(); - } - return false; - } - - @Override - public Headers getHeaders() { - if (response != null) { - return response.headers(); - } - return null; - } - - @Override - public List getHeaders(String name) { - return response.headers(name); - } - - @Override - public String getHeader(String name) { - return response.header(name); - } - - @Override - public Body getBody() { - if (response != null) { - return new ResultBody(httpTask, response, taskExecutor); - } - return null; - } - - @Override - public Exception getError() { - return error; - } - - public Response getResponse() { - return response; - } - - - @Override - public String toString() { - Body body = getBody(); - String str = "HttpResult [\n state: " + state + ",\n status: " + getStatus() - + ",\n headers: " + getHeaders(); - if (body != null) { - str += ",\n contentType: " + body.getContentType() - + ",\n body: " + body.toString(); - } - return str + ",\n error: " + error + "\n]"; - } - - @Override - public HttpResult close() { - if (response != null) { - response.close(); - } - return this; - } - -} +package com.ejlchina.okhttps.internal; + +import java.util.Collections; +import java.util.List; + +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpTask; + +import okhttp3.Headers; +import okhttp3.Response; + + +public class RealHttpResult implements HttpResult { + + private State state; + private Response response; + private Exception error; + private TaskExecutor taskExecutor; + private HttpTask httpTask; + private Body body; + + public RealHttpResult(HttpTask httpTask, State state) { + this.httpTask = httpTask; + this.state = state; + } + + public RealHttpResult(HttpTask httpTask, Response response, TaskExecutor taskExecutor) { + this(httpTask, taskExecutor); + response(response); + } + + public RealHttpResult(HttpTask httpTask, TaskExecutor taskExecutor) { + this.httpTask = httpTask; + this.taskExecutor = taskExecutor; + } + + public RealHttpResult(HttpTask httpTask, State state, Exception error) { + this.httpTask = httpTask; + exception(state, error); + } + + + public void exception(State state, Exception error) { + this.state = state; + this.error = error; + } + + public void response(Response response) { + this.state = State.RESPONSED; + this.response = response; + } + + @Override + public HttpTask getTask() { + return httpTask; + } + + @Override + public State getState() { + return state; + } + + @Override + public int getStatus() { + if (response != null) { + return response.code(); + } + return 0; + } + + @Override + public boolean isSuccessful() { + if (response != null) { + return response.isSuccessful(); + } + return false; + } + + @Override + public Headers getHeaders() { + if (response != null) { + return response.headers(); + } + return null; + } + + @Override + public List getHeaders(String name) { + if (response != null) { + return response.headers(name); + } + return Collections.emptyList(); + } + + @Override + public String getHeader(String name) { + if (response != null) { + return response.header(name); + } + return null; + } + + @Override + public Body getBody() { + if (body == null && response != null) { + body = new ResultBody(httpTask, response, taskExecutor); + } + return body; + } + + @Override + public Exception getError() { + return error; + } + + public Response getResponse() { + return response; + } + + + @Override + public String toString() { + Body body = getBody(); + String str = "HttpResult [\n state: " + state + ",\n status: " + getStatus() + + ",\n headers: " + getHeaders(); + if (body != null) { + str += ",\n contentType: " + body.getContentType(); + } + return str + ",\n error: " + error + "\n]"; + } + + @Override + public HttpResult close() { + if (response != null) { + response.close(); + } + return this; + } + +} From ed80e855c4a4c82ce4e289d23d6fc037910ac3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= <15735559897@qq.com> Date: Tue, 7 Apr 2020 23:45:30 +0800 Subject: [PATCH 16/94] =?UTF-8?q?=E7=A7=BB=E9=99=A4=20HttpResult.getTask()?= =?UTF-8?q?=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ejlchina/okhttps/HttpResult.java | 558 +++++++++--------- .../okhttps/internal/RealHttpResult.java | 5 - 2 files changed, 276 insertions(+), 287 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HttpResult.java b/src/main/java/com/ejlchina/okhttps/HttpResult.java index c3d51d96..63424db2 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpResult.java +++ b/src/main/java/com/ejlchina/okhttps/HttpResult.java @@ -1,282 +1,276 @@ -package com.ejlchina.okhttps; - -import java.io.File; -import java.io.InputStream; -import java.io.Reader; -import java.util.List; - -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; -import com.ejlchina.okhttps.internal.RealHttpResult; -import com.ejlchina.okhttps.internal.TaskExecutor; - -import okhttp3.Headers; -import okhttp3.MediaType; -import okhttp3.Response; - - -/** - * Http 执行结果 - */ -public interface HttpResult { - - /** - * 构造一个 HttpResult - * 此方法构造的 HttpResult 不可设置进度回调,不可进行下载操作! - * 若需要,请使用方法: {@link #of(Response, TaskExecutor)} - * @param response Response - * @return HttpResult - */ - static HttpResult of(Response response) { - return of(response, null); - } - - /** - * 构造一个 HttpResult - * @param response Response - * @param taskExecutor 任务执行器, 可通过方法 {@link HTTP#getExecutor()} 获得 - * @return HttpResult - */ - static HttpResult of(Response response, TaskExecutor taskExecutor) { - if (response != null) { - return new RealHttpResult(null, response, taskExecutor); - } - throw new IllegalArgumentException("response 不能为空"); - } - - /** - * 获取所属的 Http请求任务 - * @return HttpTask - */ - HttpTask getTask(); - - - public enum State { - - /** - * 执行异常 - */ - EXCEPTION, - - /** - * 请求被取消 - */ - CANCELED, - - /** - * 请求已响应 - */ - RESPONSED, - - /** - * 网络超时 - */ - TIMEOUT, - - /** - * 网络出错 - */ - NETWORK_ERROR - - } - - /** - * HTTP响应报文体 - */ - public interface Body { - - /** - * @return 媒体类型 - */ - MediaType getContentType(); - - /** - * @return 报文体字节长度 - */ - long getContentLength(); - - /** - * 在IO线程执行 - * @return Body - */ - Body nextOnIO(); - - /** - * 设置报文体接收进度回调 - * @param onProcess 进度回调函数 - * @return Body - */ - Body setOnProcess(OnCallback onProcess); - - /** - * 设置进度回调的步进字节,默认 8K(8192) - * 表示每接收 stepBytes 个字节,执行一次进度回调 - * @param stepBytes 步进字节 - * @return Body - */ - Body setStepBytes(long stepBytes); - - /** - * 设置进度回调的步进比例 - * 表示每接收 stepRate 比例,执行一次进度回调 - * @param stepRate 步进比例 - * @return Body - */ - Body setStepRate(double stepRate); - - /** - * 设置进度回调忽略响应的Range头信息,即进度回调会从0开始 - * @return Body - */ - Body setRangeIgnored(); - - /** - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @return 报文体转字节流 - */ - InputStream toByteStream(); - - /** - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @return 报文体转字节数组 - */ - byte[] toBytes(); - - /** - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @return 报文体转字符流 - */ - Reader toCharStream(); - - /** - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @return 报文体转字符串 - */ - String toString(); - - /** - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @return 报文体转Json对象 - */ - JSONObject toJsonObject(); - - /** - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @return 报文体转Json数组 - */ - JSONArray toJsonArray(); - - /** - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @param 目标泛型 - * @param type 目标类型 - * @return 报文体Json文本转JavaBean - */ - T toBean(Class type); - - /** - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @param 目标泛型 - * @param type 目标类型 - * @return 报文体Json文本转JavaBean列表 - */ - List toList(Class type); - - /** - * 下载到指定路径 - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @param filePath 目标路径 - * @return 下载过程 #Download - */ - Download toFile(String filePath); - - /** - * 下载到指定文件 - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @param file 目标文件 - * @return 下载过程 #Download - */ - Download toFile(File file); - - /** - * 下载到指定文件夹 - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @param dirPath 目标目录 - * @return 下载过程 #Download - */ - Download toFolder(String dirPath); - - /** - * 下载到指定文件夹 - * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 - * @param dir 目标目录 - * @return 下载过程 #Download - */ - Download toFolder(File dir); - - /** - * 缓存自己,缓存后可 重复使用 toXXX 类方法 - * @return Body - */ - Body cache(); - - /** - * 关闭报文体 - * 未对报文体做任何消费时使用,比如只读取长度 - * @return Body - */ - Body close(); - - } - - - /** - * @return 执行状态 - */ - State getState(); - - /** - * @return HTTP状态码 - */ - int getStatus(); - - /** - * @return 是否响应成功,状态码在 [200..300) 之间 - */ - boolean isSuccessful(); - - /** - * @return 响应头 - */ - Headers getHeaders(); - - /** - * @param name 头名称 - * @return 响应头 - */ - List getHeaders(String name); - - /** - * @param name 头名称 - * @return 响应头 - */ - String getHeader(String name); - - /** - * @return 响应报文体 - */ - Body getBody(); - - /** - * @return 执行中发生的异常 - */ - Exception getError(); - - /** - * 关闭报文 - * 未对报文体做任何消费时使用,比如只读取报文头 - * @return HttpResult - */ - HttpResult close(); - -} +package com.ejlchina.okhttps; + +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.util.List; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ejlchina.okhttps.internal.RealHttpResult; +import com.ejlchina.okhttps.internal.TaskExecutor; + +import okhttp3.Headers; +import okhttp3.MediaType; +import okhttp3.Response; + + +/** + * Http 执行结果 + */ +public interface HttpResult { + + /** + * 构造一个 HttpResult + * 此方法构造的 HttpResult 不可设置进度回调,不可进行下载操作! + * 若需要,请使用方法: {@link #of(Response, TaskExecutor)} + * @param response Response + * @return HttpResult + */ + static HttpResult of(Response response) { + return of(response, null); + } + + /** + * 构造一个 HttpResult + * @param response Response + * @param taskExecutor 任务执行器, 可通过方法 {@link HTTP#getExecutor()} 获得 + * @return HttpResult + */ + static HttpResult of(Response response, TaskExecutor taskExecutor) { + if (response != null) { + return new RealHttpResult(null, response, taskExecutor); + } + throw new IllegalArgumentException("response 不能为空"); + } + + + public enum State { + + /** + * 执行异常 + */ + EXCEPTION, + + /** + * 请求被取消 + */ + CANCELED, + + /** + * 请求已响应 + */ + RESPONSED, + + /** + * 网络超时 + */ + TIMEOUT, + + /** + * 网络出错 + */ + NETWORK_ERROR + + } + + /** + * HTTP响应报文体 + */ + public interface Body { + + /** + * @return 媒体类型 + */ + MediaType getContentType(); + + /** + * @return 报文体字节长度 + */ + long getContentLength(); + + /** + * 在IO线程执行 + * @return Body + */ + Body nextOnIO(); + + /** + * 设置报文体接收进度回调 + * @param onProcess 进度回调函数 + * @return Body + */ + Body setOnProcess(OnCallback onProcess); + + /** + * 设置进度回调的步进字节,默认 8K(8192) + * 表示每接收 stepBytes 个字节,执行一次进度回调 + * @param stepBytes 步进字节 + * @return Body + */ + Body setStepBytes(long stepBytes); + + /** + * 设置进度回调的步进比例 + * 表示每接收 stepRate 比例,执行一次进度回调 + * @param stepRate 步进比例 + * @return Body + */ + Body setStepRate(double stepRate); + + /** + * 设置进度回调忽略响应的Range头信息,即进度回调会从0开始 + * @return Body + */ + Body setRangeIgnored(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转字节流 + */ + InputStream toByteStream(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转字节数组 + */ + byte[] toBytes(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转字符流 + */ + Reader toCharStream(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转字符串 + */ + String toString(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转Json对象 + */ + JSONObject toJsonObject(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @return 报文体转Json数组 + */ + JSONArray toJsonArray(); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param 目标泛型 + * @param type 目标类型 + * @return 报文体Json文本转JavaBean + */ + T toBean(Class type); + + /** + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param 目标泛型 + * @param type 目标类型 + * @return 报文体Json文本转JavaBean列表 + */ + List toList(Class type); + + /** + * 下载到指定路径 + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param filePath 目标路径 + * @return 下载过程 #Download + */ + Download toFile(String filePath); + + /** + * 下载到指定文件 + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param file 目标文件 + * @return 下载过程 #Download + */ + Download toFile(File file); + + /** + * 下载到指定文件夹 + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param dirPath 目标目录 + * @return 下载过程 #Download + */ + Download toFolder(String dirPath); + + /** + * 下载到指定文件夹 + * 同一个 Body 对象的 toXXX 类方法只可使用一个并且只能调用一次 + * @param dir 目标目录 + * @return 下载过程 #Download + */ + Download toFolder(File dir); + + /** + * 缓存自己,缓存后可 重复使用 toXXX 类方法 + * @return Body + */ + Body cache(); + + /** + * 关闭报文体 + * 未对报文体做任何消费时使用,比如只读取长度 + * @return Body + */ + Body close(); + + } + + + /** + * @return 执行状态 + */ + State getState(); + + /** + * @return HTTP状态码 + */ + int getStatus(); + + /** + * @return 是否响应成功,状态码在 [200..300) 之间 + */ + boolean isSuccessful(); + + /** + * @return 响应头 + */ + Headers getHeaders(); + + /** + * @param name 头名称 + * @return 响应头 + */ + List getHeaders(String name); + + /** + * @param name 头名称 + * @return 响应头 + */ + String getHeader(String name); + + /** + * @return 响应报文体 + */ + Body getBody(); + + /** + * @return 执行中发生的异常 + */ + Exception getError(); + + /** + * 关闭报文 + * 未对报文体做任何消费时使用,比如只读取报文头 + * @return HttpResult + */ + HttpResult close(); + +} diff --git a/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java b/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java index c9326d36..4de88afd 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java +++ b/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java @@ -50,11 +50,6 @@ public void response(Response response) { this.response = response; } - @Override - public HttpTask getTask() { - return httpTask; - } - @Override public State getState() { return state; From 7d0eba0676addbe57849a81063123126506e2354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= <15735559897@qq.com> Date: Tue, 7 Apr 2020 23:58:22 +0800 Subject: [PATCH 17/94] =?UTF-8?q?=E4=BC=98=E5=8C=96=20HttpCall.getResult()?= =?UTF-8?q?=20=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ejlchina/okhttps/internal/AsyncHttpTask.java | 4 ++-- src/main/java/com/ejlchina/okhttps/internal/HttpClient.java | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java index cadc760c..44389f03 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java @@ -169,7 +169,7 @@ public synchronized HttpResult getResult() { } if (call == null) { try { - wait(); + wait(httpClient.totalTimeoutMillis()); } catch (InterruptedException e) { throw new HttpException(e.getMessage(), e); } @@ -214,7 +214,7 @@ public boolean isCanceled() { public synchronized HttpResult getResult() { if (result == null) { try { - wait(); + wait(httpClient.totalTimeoutMillis()); } catch (InterruptedException e) { throw new HttpException(e.getMessage(), e); } diff --git a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java index cdc0c85e..15ab2fae 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java +++ b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java @@ -99,7 +99,11 @@ public WebSocket webSocket(Request request, WebSocketListener listener) { public OkHttpClient getOkClient() { return client; } - + + public int totalTimeoutMillis() { + return client.connectTimeoutMillis() + client.writeTimeoutMillis() + client.readTimeoutMillis(); + } + public int getTagCallCount() { return tagCalls.size(); } From 3c9c6bdbf48c1c6239fd70b662983bda2c985e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 09:10:37 +0800 Subject: [PATCH 18/94] README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b86c262..d9644e66 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ - [6.2 回调执行器](#62-回调执行器) - [6.3 配置 OkHttpClient](#63-配置-okhttpclient) - [6.4 并行预处理器](#64-并行预处理器) - - [6.5 串行预处理器(Token问题最优雅的解决方案)](#65-串行预处理器) + - [6.5 串行预处理器(TOKEN问题最佳解决方案)](#65-串行预处理器) - [6.6 全局回调处理](#66-全局回调处理) - [6.7 全局下载监听](#67-全局下载监听) + [7 使用 HttpUtils 类](#7-使用-httputils-类) @@ -396,7 +396,7 @@ HTTP http = HTTP.builder() ```   和`Interceptor`一样,`Preprocessor`也可以添加多个。 -#### 6.5 串行预处理器 +#### 6.5 串行预处理器(TOKEN问题最佳解决方案)   普通预处理器都是可并行处理的,然而有时我们希望某个预处理器同时只处理一个任务。比如 当`Token`过期时我们需要去刷新获取新`Token`,而刷新`Token`这个操作只能有一个任务去执行,因为如果`n`个任务同时执行的话,那么必有`n-1`个任务刚刷新得到的`Token`可能就立马失效了,而这是我们所不希望的。 From b15d21f266751f9b191e475b1e3d74ee732415b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 09:11:47 +0800 Subject: [PATCH 19/94] README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9644e66..2fd5790a 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ - [6.2 回调执行器](#62-回调执行器) - [6.3 配置 OkHttpClient](#63-配置-okhttpclient) - [6.4 并行预处理器](#64-并行预处理器) - - [6.5 串行预处理器(TOKEN问题最佳解决方案)](#65-串行预处理器) + - [6.5 串行预处理器(TOKEN问题最佳解决方案)](#65-串行预处理器token问题最佳解决方案) - [6.6 全局回调处理](#66-全局回调处理) - [6.7 全局下载监听](#67-全局下载监听) + [7 使用 HttpUtils 类](#7-使用-httputils-类) From c530520a77ceab6e39a6f021f73f21773cee0d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 09:26:46 +0800 Subject: [PATCH 20/94] README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2fd5790a..8f51efab 100644 --- a/README.md +++ b/README.md @@ -396,6 +396,11 @@ HTTP http = HTTP.builder() ```   和`Interceptor`一样,`Preprocessor`也可以添加多个。 +> 预处理器(Preprocessor)与拦截器(Interceptor)的区别: +> * 拦截器只能处理同步操作,预处理器支持处理异步操作 +> * 拦截器都是并行处理请求,预处理支持串行处理(详见6.5章节) +> * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且预处理器先于拦截器执行 + #### 6.5 串行预处理器(TOKEN问题最佳解决方案)   普通预处理器都是可并行处理的,然而有时我们希望某个预处理器同时只处理一个任务。比如 当`Token`过期时我们需要去刷新获取新`Token`,而刷新`Token`这个操作只能有一个任务去执行,因为如果`n`个任务同时执行的话,那么必有`n-1`个任务刚刷新得到的`Token`可能就立马失效了,而这是我们所不希望的。 From 321db867f4c770b67433096a6ed133a07273ae7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 09:29:16 +0800 Subject: [PATCH 21/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f51efab..23016116 100644 --- a/README.md +++ b/README.md @@ -398,7 +398,7 @@ HTTP http = HTTP.builder() > 预处理器(Preprocessor)与拦截器(Interceptor)的区别: > * 拦截器只能处理同步操作,预处理器支持处理异步操作 -> * 拦截器都是并行处理请求,预处理支持串行处理(详见6.5章节) +> * 拦截器都是并行处理请求,预处理器支持串行处理(详见6.5章节) > * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且预处理器先于拦截器执行 #### 6.5 串行预处理器(TOKEN问题最佳解决方案) From 084ce950ceac8ee5ff49ee545f65cab944b9cfc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 09:31:43 +0800 Subject: [PATCH 22/94] readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 23016116..e676ff36 100644 --- a/README.md +++ b/README.md @@ -394,9 +394,8 @@ HTTP http = HTTP.builder() }) .build(); ``` -  和`Interceptor`一样,`Preprocessor`也可以添加多个。 +  和`Interceptor`一样,`Preprocessor`也可以添加多个。他们之前的区别如下: -> 预处理器(Preprocessor)与拦截器(Interceptor)的区别: > * 拦截器只能处理同步操作,预处理器支持处理异步操作 > * 拦截器都是并行处理请求,预处理器支持串行处理(详见6.5章节) > * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且预处理器先于拦截器执行 From 018bca1154087d99dd568b7760498e42bb6b46ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 09:43:54 +0800 Subject: [PATCH 23/94] readme --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e676ff36..d20d2cb2 100644 --- a/README.md +++ b/README.md @@ -398,7 +398,7 @@ HTTP http = HTTP.builder() > * 拦截器只能处理同步操作,预处理器支持处理异步操作 > * 拦截器都是并行处理请求,预处理器支持串行处理(详见6.5章节) -> * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且预处理器先于拦截器执行 +> * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且预处理器先于拦截器执行。关于响应后,OkHttps还提供了全局回调机制(详见6.6章节) #### 6.5 串行预处理器(TOKEN问题最佳解决方案) @@ -429,22 +429,27 @@ HTTP http = HTTP.builder() HTTP http = HTTP.builder() .responseListener((HttpTask task, HttpResult result) -> { // 所有请求响应后都会走这里 - + return true; // 返回 true 表示继续执行 task 的 OnResponse 回调,false 表示不再执行 }) .completeListener((HttpTask task, State state) -> { // 所有请求执行完都会走这里 - + return true; // 返回 true 表示继续执行 task 的 OnComplete 回调,false 表示不再执行 }) .exceptionListener((HttpTask task, IOException error) -> { // 所有请求发生异常都会走这里 - + return true; // 返回 true 表示继续执行 task 的 OnException 回调,false 表示不再执行 }) .build(); ``` +  全局回调与拦截器的异同: +> * 处理时机都在请求的响应后 +> * 拦截器可以添加多个,全局回调分三种,每种最多添加一个 +> * 全局回调可以阻断某个请求的具体回调(在全局回调里返回 false),而拦截器不能 + #### 6.7 全局下载监听 ```java From 64d3097464d766b1c1875e59e5e52001ebffbf75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 09:48:15 +0800 Subject: [PATCH 24/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d20d2cb2..f4f3b5e3 100644 --- a/README.md +++ b/README.md @@ -446,7 +446,7 @@ HTTP http = HTTP.builder() ```   全局回调与拦截器的异同: -> * 处理时机都在请求的响应后 +> * 拦截器处理时机在请求前和响应后,全局回调只在响应后 > * 拦截器可以添加多个,全局回调分三种,每种最多添加一个 > * 全局回调可以阻断某个请求的具体回调(在全局回调里返回 false),而拦截器不能 From 0c7ff3b73c796aaf7d40f8c4385e6fbe8ebee578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 09:53:33 +0800 Subject: [PATCH 25/94] readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f4f3b5e3..40740da6 100644 --- a/README.md +++ b/README.md @@ -398,7 +398,7 @@ HTTP http = HTTP.builder() > * 拦截器只能处理同步操作,预处理器支持处理异步操作 > * 拦截器都是并行处理请求,预处理器支持串行处理(详见6.5章节) -> * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且预处理器先于拦截器执行。关于响应后,OkHttps还提供了全局回调机制(详见6.6章节) +> * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且先于拦截器执行。关于响应后,OkHttps还提供了全局回调机制(详见6.6章节) #### 6.5 串行预处理器(TOKEN问题最佳解决方案) @@ -446,9 +446,9 @@ HTTP http = HTTP.builder() ```   全局回调与拦截器的异同: -> * 拦截器处理时机在请求前和响应后,全局回调只在响应后 > * 拦截器可以添加多个,全局回调分三种,每种最多添加一个 -> * 全局回调可以阻断某个请求的具体回调(在全局回调里返回 false),而拦截器不能 +> * 拦截器处的理时机在请求前和响应后,全局回调只在响应后,并且晚于拦截器 +> * 全局回调可以阻断(return false)某个请求的具体回调,而拦截器不能 #### 6.7 全局下载监听 From c38c79ad2c9bb0dfa48d9313cbace733ab3b6337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 10:55:57 +0800 Subject: [PATCH 26/94] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ejlchina/okhttps/internal/AsyncHttpTask.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java index 44389f03..a0d798dc 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java @@ -169,7 +169,7 @@ public synchronized HttpResult getResult() { } if (call == null) { try { - wait(httpClient.totalTimeoutMillis()); + wait(httpClient.totalTimeoutMillis() * 10); } catch (InterruptedException e) { throw new HttpException(e.getMessage(), e); } @@ -214,7 +214,7 @@ public boolean isCanceled() { public synchronized HttpResult getResult() { if (result == null) { try { - wait(httpClient.totalTimeoutMillis()); + wait(httpClient.totalTimeoutMillis() * 10); } catch (InterruptedException e) { throw new HttpException(e.getMessage(), e); } From 9c2674392741d5bfef2c1e728f74e6500a0da20c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 11:22:05 +0800 Subject: [PATCH 27/94] V1.0.1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c322c04d..1678b893 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.ejlchina okhttps - 1.0.0 + 1.0.1 OkHttps @@ -32,7 +32,7 @@ scm:git://gitee.com/ejlchina-zhxu/okhttps.git scm:http://gitee.com/ejlchina-zhxu/okhttps https://gitee.com/ejlchina-zhxu/okhttps - v2.2.0 + v1.0.1 From 1355d8e4aa8ac24600f5dbbb16999d6a0a9db749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 11:40:49 +0800 Subject: [PATCH 28/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 40740da6..79d44f92 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,7 @@ HTTP http = HTTP.builder() // 必须同步返回,拦截器内无法执行异步操作 return chain.proceed(request); }); - // 其它配置: SSL、缓存、代理、事件监听... + // 其它配置: CookieJar、SSL、缓存、代理、事件监听... }) .build(); ``` From 8b2de164bd9d4867949f5142fe372d4c65fd1d4c Mon Sep 17 00:00:00 2001 From: ejlchina Date: Wed, 8 Apr 2020 12:13:18 +0800 Subject: [PATCH 29/94] update README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79d44f92..0067f8db 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ ``` ### Gradle -`compile 'com.ejlchina:okhttps:1.0.0'` +`implementation 'com.ejlchina:okhttps:1.0.0'` ## 使用说明 From fd60a1c2f6bbbcd7b8d2a228259ad67e1bdafc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 12:14:23 +0800 Subject: [PATCH 30/94] readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0067f8db..27578d65 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ * TCP连接池、Http2 ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) -### 当前文档版本[1.0.0] +### 当前文档版本[1.0.1] ### API文档:[https://apidoc.gitee.com/ejlchina-zhxu/okhttps](https://apidoc.gitee.com/ejlchina-zhxu/okhttps) ## 目录 @@ -64,12 +64,12 @@ com.ejlchina okhttps - 1.0.0 + 1.0.1 ``` ### Gradle -`implementation 'com.ejlchina:okhttps:1.0.0'` +`implementation 'com.ejlchina:okhttps:1.0.1'` ## 使用说明 From e4f40b286e194f8e016959f9511a5367f07aef6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 12:36:02 +0800 Subject: [PATCH 31/94] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 27578d65..b2d77eea 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ * BaseURL、URL占位符、JSON自动封装与解析 * 同步拦截器、异步预处理器、回调执行器 * 文件上传下载(过程控制、进度监听) + * 单方法回调,充分利用 Lambda 表达式 * TCP连接池、Http2 ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) From 99200f3ccc04c49c54fffe8e476b1caf616e17e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 12:56:32 +0800 Subject: [PATCH 32/94] readme --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b2d77eea..f818d06e 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ HttpCall call4 = http.async("/users/1").delete();// 异步 DELETE #### 3.1 回调函数 -  只有异步请求才可以设置回调函数: +  OkHttps 的回调函数全部使用单方法模式,这样可以充分利用 Java8 或 Kotlin 中的 Lambda 表达式,使你的代码更加简洁优雅: ```java http.async("/users/{id}") // http://api.demo.com/users/1 @@ -157,6 +157,9 @@ http.async("/users/{id}") // http://api.demo.com/users/1 .get(); ``` +>* 只有异步请求才可以设置这三种(响应|异常|完成)回调函数 +>* 同步请求直接返回结果,无需使用回调 + #### 3.2 HttpResult   `HttpResult`是HTTP请求执行完后的结果,它是同步请求方法( `get`、`post`、`put`、`delete`)的返回值,也是异步请求响应回调(`OnResponse`)的参数,它定义了如下方法: From 0cec85b7a41021ade6e24393b1214e1e2dce7149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 12:57:31 +0800 Subject: [PATCH 33/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f818d06e..43b52cb3 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ http.async("/users/{id}") // http://api.demo.com/users/1 .get(); ``` ->* 只有异步请求才可以设置这三种(响应|异常|完成)回调函数 +>* 只有异步请求才可以设置这三种(响应|异常|完成)回调 >* 同步请求直接返回结果,无需使用回调 #### 3.2 HttpResult From b75664ed261a83c6292141b97952e3c204fa041a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 12:59:24 +0800 Subject: [PATCH 34/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43b52cb3..9362b94d 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ System.out.println(body.toList(Order.class));   示例,获取下载文件的大小: ```java -long size = http.sync("http://api.demo.com/download/test.zip") +long size = http.sync("/download/test.zip") .get().getBody() .close() // 只是想获得文件大小,不消费报文体,所以直接关闭 .getContentLength(); // 获得待下载文件的大小 From 05620fd8f94062039d289b0f4bcbee667cef466b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 13:11:21 +0800 Subject: [PATCH 35/94] readme --- README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9362b94d..35c76a5e 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ * 同步拦截器、异步预处理器、回调执行器 * 文件上传下载(过程控制、进度监听) * 单方法回调,充分利用 Lambda 表达式 + * 全局监听、回调阻断 * TCP连接池、Http2 ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) @@ -42,7 +43,7 @@ - [6.3 配置 OkHttpClient](#63-配置-okhttpclient) - [6.4 并行预处理器](#64-并行预处理器) - [6.5 串行预处理器(TOKEN问题最佳解决方案)](#65-串行预处理器token问题最佳解决方案) - - [6.6 全局回调处理](#66-全局回调处理) + - [6.6 全局回调监听](#66-全局回调监听) - [6.7 全局下载监听](#67-全局下载监听) + [7 使用 HttpUtils 类](#7-使用-httputils-类) + [8 文件下载](#8-文件下载) @@ -402,7 +403,7 @@ HTTP http = HTTP.builder() > * 拦截器只能处理同步操作,预处理器支持处理异步操作 > * 拦截器都是并行处理请求,预处理器支持串行处理(详见6.5章节) -> * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且先于拦截器执行。关于响应后,OkHttps还提供了全局回调机制(详见6.6章节) +> * 拦截器处理时机在请求前和响应后,预处理器只在请求前,并且先于拦截器执行。关于响应后,OkHttps还提供了全局回调监听(详见6.6章节) #### 6.5 串行预处理器(TOKEN问题最佳解决方案) @@ -427,7 +428,7 @@ HTTP http = HTTP.builder() ```   串行预处理器实现了让HTTP任务排队串行处理的功能,但值得一提的是:它并没有因此而阻塞任何线程! -#### 6.6 全局回调处理 +#### 6.6 全局回调监听 ```java HTTP http = HTTP.builder() @@ -449,10 +450,10 @@ HTTP http = HTTP.builder() .build(); ``` -  全局回调与拦截器的异同: -> * 拦截器可以添加多个,全局回调分三种,每种最多添加一个 -> * 拦截器处的理时机在请求前和响应后,全局回调只在响应后,并且晚于拦截器 -> * 全局回调可以阻断(return false)某个请求的具体回调,而拦截器不能 +  全局回调监听与拦截器的异同: +> * 拦截器可以添加多个,全局回调监听分三种,每种最多添加一个 +> * 拦截器处的理时机在请求前和响应后,全局回调监听只在响应后,并且晚于拦截器 +> * 全局回调监听可以阻断(return false)某个请求的具体回调,而拦截器不能 #### 6.7 全局下载监听 From adf76e83aa4b6ae1feb726a75af81c75632c3597 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 13:20:08 +0800 Subject: [PATCH 36/94] readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 35c76a5e..e2b9d6f7 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,9 @@ * 链式调用,一点到底 * BaseURL、URL占位符、JSON自动封装与解析 - * 同步拦截器、异步预处理器、回调执行器 + * 同步拦截器、异步预处理器、回调执行器、全局监听、回调阻断 * 文件上传下载(过程控制、进度监听) * 单方法回调,充分利用 Lambda 表达式 - * 全局监听、回调阻断 * TCP连接池、Http2 ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) From f6db912aad78d9576aa633f9f5578eeca5632721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 13:50:42 +0800 Subject: [PATCH 37/94] =?UTF-8?q?TaskExecutor=20=E6=96=B0=E5=A2=9E=20shutd?= =?UTF-8?q?own=20=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ejlchina/okhttps/internal/TaskExecutor.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java b/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java index fbb164b1..3abcc054 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java +++ b/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import com.ejlchina.okhttps.DownListener; import com.ejlchina.okhttps.Download; @@ -95,4 +96,13 @@ public void executeOnComplete(HttpTask task, OnCallback onComplete, St } } + public void shutdown() { + if (ioExecutor != null && ioExecutor instanceof ExecutorService) { + ((ExecutorService) ioExecutor).shutdown(); + } + if (mainExecutor != null && mainExecutor instanceof ExecutorService) { + ((ExecutorService) mainExecutor).shutdown(); + } + } + } From e8f8c21f95f4dce4dd70e2fd472c266edff1271d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 13:54:21 +0800 Subject: [PATCH 38/94] =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java b/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java index 3abcc054..66ed7a85 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java +++ b/src/main/java/com/ejlchina/okhttps/internal/TaskExecutor.java @@ -96,6 +96,10 @@ public void executeOnComplete(HttpTask task, OnCallback onComplete, St } } + /** + * 关闭线程池 + * @since OkHttps V1.0.2 + */ public void shutdown() { if (ioExecutor != null && ioExecutor instanceof ExecutorService) { ((ExecutorService) ioExecutor).shutdown(); From f027d65d0b97b7b01a635e0ca76b5cb754b677d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 16:53:37 +0800 Subject: [PATCH 39/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e2b9d6f7..3563ed0a 100644 --- a/README.md +++ b/README.md @@ -502,7 +502,7 @@ List users = HttpUtils.sync("/users") http.sync("/download/test.zip") .get() // 使用 GET 方法(其它方法也可以,看服务器支持) .getBody() // 得到报文体 - .toFile("D:/download/test.zip") // 指定下载的目录,文件名将根据下载信息自动生成 + .toFile("D:/download/test.zip") // 下载到指定的路径 .start(); // 启动下载 ```   或使用异步连接方式: From 9ba2d77b3710d6bcac171dec5db06f16a9212604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 16:55:37 +0800 Subject: [PATCH 40/94] readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3563ed0a..869edb2c 100644 --- a/README.md +++ b/README.md @@ -504,6 +504,10 @@ http.sync("/download/test.zip") .getBody() // 得到报文体 .toFile("D:/download/test.zip") // 下载到指定的路径 .start(); // 启动下载 + +http.sync("/download/test.zip").get().getBody() + .toFolder("D:/download/") // 下载到指定的目录,文件名将根据下载信息自动生成 + .start(); ```   或使用异步连接方式: From 3e08e59de5457d1930d9b014386c13282f766ae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 16:56:58 +0800 Subject: [PATCH 41/94] readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 869edb2c..354a69d2 100644 --- a/README.md +++ b/README.md @@ -506,7 +506,7 @@ http.sync("/download/test.zip") .start(); // 启动下载 http.sync("/download/test.zip").get().getBody() - .toFolder("D:/download/") // 下载到指定的目录,文件名将根据下载信息自动生成 + .toFolder("D:/download") // 下载到指定的目录,文件名将根据下载信息自动生成 .start(); ```   或使用异步连接方式: @@ -514,7 +514,7 @@ http.sync("/download/test.zip").get().getBody() ```java http.async("/download/test.zip") .setOnResponse((HttpResult result) -> { - result.getBody().toFile("D:/download/test.zip").start(); + result.getBody().toFolder("D:/download").start(); }) .get(); ``` From e3e3e54b30c8508ddff40f00f2d717f0344200f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Wed, 8 Apr 2020 16:57:37 +0800 Subject: [PATCH 42/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 354a69d2..f844d1dc 100644 --- a/README.md +++ b/README.md @@ -506,7 +506,7 @@ http.sync("/download/test.zip") .start(); // 启动下载 http.sync("/download/test.zip").get().getBody() - .toFolder("D:/download") // 下载到指定的目录,文件名将根据下载信息自动生成 + .toFolder("D:/download") // 下载到指定的目录,文件名将根据下载信息自动生成 .start(); ```   或使用异步连接方式: From 8794048f15cdbee2f7900b342096b2e524d64b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= <15735559897@qq.com> Date: Wed, 8 Apr 2020 23:36:12 +0800 Subject: [PATCH 43/94] SimpleTests --- .../java/com/ejlchina/test/SimpleTests.java | 73 +++++++++++++++++++ src/test/java/com/ejlchina/test/User.java | 27 ------- 2 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 src/test/java/com/ejlchina/test/SimpleTests.java delete mode 100644 src/test/java/com/ejlchina/test/User.java diff --git a/src/test/java/com/ejlchina/test/SimpleTests.java b/src/test/java/com/ejlchina/test/SimpleTests.java new file mode 100644 index 00000000..f1e92e7e --- /dev/null +++ b/src/test/java/com/ejlchina/test/SimpleTests.java @@ -0,0 +1,73 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpResult; +import org.junit.Test; + +import java.util.List; + +public class SimpleTests { + + + HTTP http = HTTP.builder() + .baseUrl("http://localhost:8080") + .build(); + + /** + * 同步请求示例 + */ + @Test + public void testSync() { + List users = http.sync("/users") // http://localhost:8080/users + .get() // GET请求 + .getBody() // 获取响应报文体 + .toList(User.class); // 得到目标数据 + System.out.println("users = " + users); + } + + /** + * 异步请求示例 + */ + @Test + public void testAsync() { + http.async("/users/1") // http://api.demo.com/users/1 + .setOnResponse((HttpResult result) -> { + // 得到目标数据 + User user = result.getBody().toBean(User.class); + System.out.println("user = " + user); + }) + .get(); + sleep(5000); + } + + + void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + +} + + +class User { + + private int id; + private String name; + + public void setId(int id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "User [id=" + id + ", name=" + name + "]"; + } + +} \ No newline at end of file diff --git a/src/test/java/com/ejlchina/test/User.java b/src/test/java/com/ejlchina/test/User.java deleted file mode 100644 index bdf75c9d..00000000 --- a/src/test/java/com/ejlchina/test/User.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.ejlchina.test; - -public class User { - - int id; - String name; - - public void setId(int id) { - this.id = id; - } - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - public String getName() { - return name; - } - - @Override - public String toString() { - return "User [id=" + id + ", name=" + name + "]"; - } - -} From d912448b9151afbe9690f6dc6abc87bf22850c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= <15735559897@qq.com> Date: Wed, 8 Apr 2020 23:57:35 +0800 Subject: [PATCH 44/94] SimpleTests --- .../java/com/ejlchina/test/SimpleTests.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/test/java/com/ejlchina/test/SimpleTests.java b/src/test/java/com/ejlchina/test/SimpleTests.java index f1e92e7e..78f41c29 100644 --- a/src/test/java/com/ejlchina/test/SimpleTests.java +++ b/src/test/java/com/ejlchina/test/SimpleTests.java @@ -2,6 +2,7 @@ import com.ejlchina.okhttps.HTTP; import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpResult.Body; import org.junit.Test; import java.util.List; @@ -15,6 +16,7 @@ public class SimpleTests { /** * 同步请求示例 + * 同步请求直接得到结果,无需设置回调 */ @Test public void testSync() { @@ -27,6 +29,7 @@ public void testSync() { /** * 异步请求示例 + * 异步请求在OnResponse里接收请求结果 */ @Test public void testAsync() { @@ -41,6 +44,40 @@ public void testAsync() { } + /** + * 启用 cache 示例 + */ + @Test + public void testCache() { + Body body = http.sync("/users").get().getBody() + .cache(); // 启用 cache + // 使用 cache 后,可以多次使用 toXXX() 方法 + System.out.println(body.toString()); + System.out.println(body.toJsonArray()); + System.out.println(body.toList(User.class)); + } + + + /** + * 获取文件大小示例 + */ + @Test + public void testContentLength() { + long t = System.currentTimeMillis(); + + long size = http.sync("https://download.cocos.com/CocosDashboard/v1.0.1/CocosDashboard-v1.0.1-win32-031816.exe") + .get().getBody() + .close() // 只是想获得文件大小,不消费报文体,所以直接关闭 + .getContentLength(); // 获得待下载文件的大小 + + t = System.currentTimeMillis() - t; + + // 由于未消费报文体,所以本次请求不会消耗下载报文体的时间和网络流量) + System.out.println("耗时:" + t + " 毫秒"); + System.out.println("size = " + (size / 1024) + " KB"); + } + + void sleep(int millis) { try { Thread.sleep(millis); From 062853b773871763317a195f1bcedaa368ae5cf7 Mon Sep 17 00:00:00 2001 From: ejlchina Date: Thu, 9 Apr 2020 09:59:55 +0800 Subject: [PATCH 45/94] update README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f844d1dc..82ebb80f 100644 --- a/README.md +++ b/README.md @@ -728,7 +728,7 @@ HTTP http = HTTP.builder() }) .build(); ``` -  上述代码便实现了让 **所有** 的 **回调函数** 都在 **主线程** 执行的目的,如: +  上述代码便实现了让 **所有** 的 **回调函数** 都在 **主线程(UI线程)** 执行的目的,如: ```java http.async("/users") From a8ea604e618aef7008ceee2a90ed7a76581c49ea Mon Sep 17 00:00:00 2001 From: ejlchina Date: Thu, 9 Apr 2020 10:00:52 +0800 Subject: [PATCH 46/94] update README.md. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 82ebb80f..8ad48ef1 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ + [9 文件上传](#9-文件上传) - [9.1 上传进度监听](#91-上传进度监听) - [9.2 上传过程控制](#92-上传过程控制) - + [10 执行线程自由切换(for Android)](#10-执行线程自由切换for-android) + + [10 回调线程自由切换(for Android)](#10-回调线程自由切换for-android) + [参考文档](#参考文档) + [联系方式](#联系方式) @@ -712,7 +712,7 @@ call.cancel(); // 取消上传 ```   上传就没有暂停和继续这个功能啦,应该没人有这个需求吧? -### 10 执行线程自由切换(for Android) +### 10 回调线程自由切换(for Android)   在 Android 开发中,经常会把某些代码放到特点的线程去执行,比如网络请求响应后的页面更新在主线程(UI线程)执行,而保存文件则在IO线程操作。OkHttps 为这类问题提供了良好的方案。 From 884a4d252ce90ef8704373380eacab2f5241569b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 10:12:40 +0800 Subject: [PATCH 47/94] readme --- README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8ad48ef1..8459860a 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,10 @@ + [9 文件上传](#9-文件上传) - [9.1 上传进度监听](#91-上传进度监听) - [9.2 上传过程控制](#92-上传过程控制) - + [10 回调线程自由切换(for Android)](#10-回调线程自由切换for-android) + + [10 异常处理](#10-异常处理) + - [10.1 同步方式](#101-同步方式) + - [10.2 异步方式](#102-异步方式) + + [11 回调线程自由切换(for Android)](#11-回调线程自由切换for-android) + [参考文档](#参考文档) + [联系方式](#联系方式) @@ -192,7 +195,7 @@ http.async("/users/{id}") // http://api.demo.com/users/1 * `getContentLength()` 返回报文体的字节长度 * `cache()` 缓存报文体,开启缓存后可重复使用`toXXX()`类方法 * `close()` 关闭报文体,未对报文体做任何消费时使用,比如只读取报文头 -* `getError()` 执行中发生的异常,自动捕获执行请求是发生的 网络超时、网络错误 和 其它请求异常 +* `getError()` 执行中发生的异常,自动捕获执行请求时发生的 网络超时、网络错误 和 其它IO异常 * `close()` 关闭报文,未对报文体做任何消费时使用,比如只读取长度   示例,请求结果自动转Bean和List: @@ -712,7 +715,17 @@ call.cancel(); // 取消上传 ```   上传就没有暂停和继续这个功能啦,应该没人有这个需求吧? -### 10 回调线程自由切换(for Android) +### 10 异常处理 + +#### 10.1 同步方式 + +文档完善中... + +#### 10.2 异步方式 + +文档完善中... + +### 11 回调线程自由切换(for Android)   在 Android 开发中,经常会把某些代码放到特点的线程去执行,比如网络请求响应后的页面更新在主线程(UI线程)执行,而保存文件则在IO线程操作。OkHttps 为这类问题提供了良好的方案。 From b74e2fe706b4d5e63c38e0243e7f99910c5c05bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 10:16:36 +0800 Subject: [PATCH 48/94] readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8459860a..a2214606 100644 --- a/README.md +++ b/README.md @@ -717,6 +717,8 @@ call.cancel(); // 取消上传 ### 10 异常处理 +  相比其它的 HTTP 开发包,OkHttps 提供了更多的异常处理方式,以满足不同的异常处理需求。 + #### 10.1 同步方式 文档完善中... From 73ae5e3cc282b01cc67f1541e0ff0b1be36b12ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 10:17:46 +0800 Subject: [PATCH 49/94] readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a2214606..ff0d0045 100644 --- a/README.md +++ b/README.md @@ -717,13 +717,13 @@ call.cancel(); // 取消上传 ### 10 异常处理 -  相比其它的 HTTP 开发包,OkHttps 提供了更多的异常处理方式,以满足不同的异常处理需求。 +  相比其它的 HTTP 开发包,OkHttps 提供了更多的异常处理方法,以满足不同的异常处理需求。 -#### 10.1 同步方式 +#### 10.1 同步请求的异常 文档完善中... -#### 10.2 异步方式 +#### 10.2 异步请求的异常 文档完善中... From 90ed6c0e352ed02824393daee79260c4e705e545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 11:22:37 +0800 Subject: [PATCH 50/94] =?UTF-8?q?=E4=BC=98=E5=8C=96=20HttpResult.getError(?= =?UTF-8?q?)=20=E6=96=B9=E6=B3=95=E7=9A=84=E8=BF=94=E5=9B=9E=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=20Exception=20=E5=85=B7=E4=BD=93=E5=88=B0=20IOExcepti?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/HttpResult.java | 3 ++- .../com/ejlchina/okhttps/internal/RealHttpResult.java | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HttpResult.java b/src/main/java/com/ejlchina/okhttps/HttpResult.java index 63424db2..88984b72 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpResult.java +++ b/src/main/java/com/ejlchina/okhttps/HttpResult.java @@ -1,6 +1,7 @@ package com.ejlchina.okhttps; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.List; @@ -264,7 +265,7 @@ public interface Body { /** * @return 执行中发生的异常 */ - Exception getError(); + IOException getError(); /** * 关闭报文 diff --git a/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java b/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java index 4de88afd..f6eab345 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java +++ b/src/main/java/com/ejlchina/okhttps/internal/RealHttpResult.java @@ -1,5 +1,6 @@ package com.ejlchina.okhttps.internal; +import java.io.IOException; import java.util.Collections; import java.util.List; @@ -14,7 +15,7 @@ public class RealHttpResult implements HttpResult { private State state; private Response response; - private Exception error; + private IOException error; private TaskExecutor taskExecutor; private HttpTask httpTask; private Body body; @@ -34,13 +35,13 @@ public RealHttpResult(HttpTask httpTask, TaskExecutor taskExecutor) { this.taskExecutor = taskExecutor; } - public RealHttpResult(HttpTask httpTask, State state, Exception error) { + public RealHttpResult(HttpTask httpTask, State state, IOException error) { this.httpTask = httpTask; exception(state, error); } - public void exception(State state, Exception error) { + public void exception(State state, IOException error) { this.state = state; this.error = error; } @@ -104,7 +105,7 @@ public Body getBody() { } @Override - public Exception getError() { + public IOException getError() { return error; } From e898b069d25c608ca6ccd3077e3c292df9b283b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 11:23:41 +0800 Subject: [PATCH 51/94] =?UTF-8?q?readme:=20=E5=90=8C=E6=AD=A5=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E7=9A=84=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ff0d0045..1ff3d618 100644 --- a/README.md +++ b/README.md @@ -721,7 +721,50 @@ call.cancel(); // 取消上传 #### 10.1 同步请求的异常 -文档完善中... +  默认情况下,当请求执行异常时,会直接向外抛出,我们可以用 `try catch` 来捕获,例如: + +```java +try { + HttpResult result = http.sync("/users/1").get(); +} catch (HttpException e) { + Throwable cause = e.getCause(); // 得到异常原因 + if (cause instanceof ConnectException) { + // 当没网络时,会抛出连接异常 + } + if (cause instanceof SocketTimeoutException) { + // 当接口长时间未响应,会抛出超时异常 + } + if (cause instanceof UnknownHostException) { + // 当把域名或IP写错,会抛出 UnknownHost 异常 + } + // ... +} +``` +  这种传统的异常处理方式,当然可以解决问题,但是 OkHttps 提供了更优雅的方案: + +``` +HttpResult result = http.sync("/users/1") + .nothrow() // 告诉 OkHttps 发生异常时不要直接向外抛出 + .get(); +State state = result.getState(); // 得到请求执行状态 +if (state == State.RESPONSED) { + // 请求已正常响应 +} +if (state == State.CANCELED) { + // 请求已被取消 +} +if (state == State.NETWORK_ERROR) { + // 网络错误,说明用户没网了 +} +if (state == State.TIMEOUT) { + // 请求超时 +} +if (state == State.EXCEPTION) { + // 其它请求异常 +} +// 还可以获得具体的异常信息 +Exception err = result.getError(); +``` #### 10.2 异步请求的异常 From c16276c1373c72794e2bd29042d24d6ba5c63c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 11:26:37 +0800 Subject: [PATCH 52/94] =?UTF-8?q?readme:=20=E5=90=8C=E6=AD=A5=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E7=9A=84=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1ff3d618..18be0c65 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,8 @@ - [9.1 上传进度监听](#91-上传进度监听) - [9.2 上传过程控制](#92-上传过程控制) + [10 异常处理](#10-异常处理) - - [10.1 同步方式](#101-同步方式) - - [10.2 异步方式](#102-异步方式) + - [10.1 同步请求的异常](#101-同步请求的异常) + - [10.2 异步请求的异常](#102-异步请求的异常) + [11 回调线程自由切换(for Android)](#11-回调线程自由切换for-android) + [参考文档](#参考文档) + [联系方式](#联系方式) @@ -742,7 +742,7 @@ try { ```   这种传统的异常处理方式,当然可以解决问题,但是 OkHttps 提供了更优雅的方案: -``` +```java HttpResult result = http.sync("/users/1") .nothrow() // 告诉 OkHttps 发生异常时不要直接向外抛出 .get(); From 999b12f137007aa985738fef8a45b716fff50d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 11:35:37 +0800 Subject: [PATCH 53/94] =?UTF-8?q?readme:=20=E5=90=8C=E6=AD=A5=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E7=9A=84=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 29 +++++++++---------- .../java/com/ejlchina/test/SimpleTests.java | 1 - 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 18be0c65..17d7a247 100644 --- a/README.md +++ b/README.md @@ -746,24 +746,21 @@ try { HttpResult result = http.sync("/users/1") .nothrow() // 告诉 OkHttps 发生异常时不要直接向外抛出 .get(); -State state = result.getState(); // 得到请求执行状态 -if (state == State.RESPONSED) { - // 请求已正常响应 -} -if (state == State.CANCELED) { - // 请求已被取消 -} -if (state == State.NETWORK_ERROR) { - // 网络错误,说明用户没网了 -} -if (state == State.TIMEOUT) { - // 请求超时 -} -if (state == State.EXCEPTION) { - // 其它请求异常 +// 判断执行状态 +switch (result.getState()) { + case RESPONSED: // 请求已正常响应 + break; + case CANCELED: // 请求已被取消 + break; + case NETWORK_ERROR: // 网络错误,说明用户没网了 + break; + case TIMEOUT: // 请求超时 + break; + case EXCEPTION: // 其它异常 + break; } // 还可以获得具体的异常信息 -Exception err = result.getError(); +Exception error = result.getError(); ``` #### 10.2 异步请求的异常 diff --git a/src/test/java/com/ejlchina/test/SimpleTests.java b/src/test/java/com/ejlchina/test/SimpleTests.java index 78f41c29..da3b3472 100644 --- a/src/test/java/com/ejlchina/test/SimpleTests.java +++ b/src/test/java/com/ejlchina/test/SimpleTests.java @@ -77,7 +77,6 @@ public void testContentLength() { System.out.println("size = " + (size / 1024) + " KB"); } - void sleep(int millis) { try { Thread.sleep(millis); From b14c3cbdbd628521e97e760ffb44b634de2ea25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 11:38:53 +0800 Subject: [PATCH 54/94] readme --- README.md | 5 +- .../com/ejlchina/test/ExceptionTests.java | 72 +++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/ejlchina/test/ExceptionTests.java diff --git a/README.md b/README.md index 17d7a247..7cdb00aa 100644 --- a/README.md +++ b/README.md @@ -743,9 +743,8 @@ try {   这种传统的异常处理方式,当然可以解决问题,但是 OkHttps 提供了更优雅的方案: ```java -HttpResult result = http.sync("/users/1") - .nothrow() // 告诉 OkHttps 发生异常时不要直接向外抛出 - .get(); +// 使用 nothrow 方法告诉 OkHttps 发生异常时不要直接向外抛出 +HttpResult result = http.sync("/users/1").nothrow().get(); // 判断执行状态 switch (result.getState()) { case RESPONSED: // 请求已正常响应 diff --git a/src/test/java/com/ejlchina/test/ExceptionTests.java b/src/test/java/com/ejlchina/test/ExceptionTests.java new file mode 100644 index 00000000..aeb31d5c --- /dev/null +++ b/src/test/java/com/ejlchina/test/ExceptionTests.java @@ -0,0 +1,72 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.internal.HttpException; +import org.junit.Test; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; + +public class ExceptionTests { + + HTTP http = HTTP.builder() + .baseUrl("http://localhost:8080") + .build(); + + @Test + public void testSync0() { + try { + HttpResult result = http.sync("/users/1").get(); + System.out.println("请求结果:" + result); + } catch (HttpException e) { + Throwable cause = e.getCause(); // 得到异常原因 + if (cause instanceof ConnectException) { + // 当没网络时,会抛出连接异常 + } + if (cause instanceof SocketTimeoutException) { + // 当接口长时间未响应,会抛出超时异常 + } + if (cause instanceof UnknownHostException) { + // 当把域名或IP写错,会抛出 UnknownHost 异常 + } + // ... + } + } + + @Test + public void testSync1() { + HttpResult result = http.sync("/users/1") + .nothrow() // 告诉 OkHttps 发生异常时不要直接向外抛出 + .get(); + // 判断执行状态 + switch (result.getState()) { + case RESPONSED: // 请求已正常响应 + + break; + case CANCELED: // 请求已被取消 + + break; + case NETWORK_ERROR: // 网络错误,说明用户没网了 + + break; + case TIMEOUT: // 请求超时 + + break; + case EXCEPTION: // 其它异常 + + break; + } + // 还可以获得具体的异常信息 + Exception error = result.getError(); + + + } + + @Test + public void testASync() { + + } + +} From 03fea6905fa627d2edebb7a102f2f3b7ca49bb28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 12:37:35 +0800 Subject: [PATCH 55/94] =?UTF-8?q?readme:=20=E5=BC=82=E6=AD=A5=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E7=9A=84=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 50 ++++++++++++++++++- .../com/ejlchina/test/ExceptionTests.java | 32 +++++++++++- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7cdb00aa..6cccea99 100644 --- a/README.md +++ b/README.md @@ -717,7 +717,7 @@ call.cancel(); // 取消上传 ### 10 异常处理 -  相比其它的 HTTP 开发包,OkHttps 提供了更多的异常处理方法,以满足不同的异常处理需求。 +  相比其它的 HTTP 开发包,OkHttps 额外提供了一个特别的异常处理方发(`nothrow()`),以满足不同的异常处理需求。 #### 10.1 同步请求的异常 @@ -764,7 +764,53 @@ Exception error = result.getError(); #### 10.2 异步请求的异常 -文档完善中... +  异步请求的使用最多的异常处理方式是设置一个异常回调: + +```java +http.async("/users/1") + .setOnResponse((HttpResult result) -> { + // 当发生异常时就不会走这里 + }) + .setOnException((IOException e) -> { + // 这里处理请求异常 + }) + .get(); +``` +  如果不设置`OnException`回调,发生异常时会在 **IO线程池** 中向上抛出,外层无法捕获: + +```java +try { + http.async("/users/1") + .setOnResponse((HttpResult result) -> { + // 当发生异常时就不会走这里 + }) + .get(); +} catch (HttpException e) { + // 这种方式是捕获不到异常的!!!! +} +``` +  但即使不设置`OnException`回调,发生异常时,依然会走`OnComplete`回调: + +```java +http.async("/users/1") + .setOnResponse((HttpResult result) -> { + // 当发生异常时就不会走这里 + }) + .setOnComplete((State state) -> { + // 发生异常,会先执行这里,但执行完后依然会在IO线程中向上抛出 + }) + .get(); +``` +  如果就是想 **不处理异常,也不向上抛出**,可以做到吗?可以!使用`nothrow()`方法: + +```java +http.async("/users/1") + .nothrow() // 告诉 OkHttps 发生异常时不向外抛出 + .setOnResponse((HttpResult result) -> { + // 当发生异常时就不会走这里 + }) + .get(); +``` ### 11 回调线程自由切换(for Android) diff --git a/src/test/java/com/ejlchina/test/ExceptionTests.java b/src/test/java/com/ejlchina/test/ExceptionTests.java index aeb31d5c..aafeedf1 100644 --- a/src/test/java/com/ejlchina/test/ExceptionTests.java +++ b/src/test/java/com/ejlchina/test/ExceptionTests.java @@ -3,16 +3,24 @@ import com.ejlchina.okhttps.HTTP; import com.ejlchina.okhttps.HttpResult; import com.ejlchina.okhttps.internal.HttpException; +import okhttp3.OkHttpClient; import org.junit.Test; +import java.io.IOException; import java.net.ConnectException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; +import java.util.concurrent.TimeUnit; public class ExceptionTests { HTTP http = HTTP.builder() - .baseUrl("http://localhost:8080") + .config((OkHttpClient.Builder builder) -> { + builder.connectTimeout(2, TimeUnit.SECONDS); + builder.writeTimeout(2, TimeUnit.SECONDS); + builder.readTimeout(2, TimeUnit.SECONDS); + }) + .baseUrl("http://localhost:8081") .build(); @Test @@ -67,6 +75,28 @@ public void testSync1() { @Test public void testASync() { + http.async("/users/1") + .setOnResponse((HttpResult result) -> { + // 当发生异常时就不会走这里 + System.out.println(result); + }) + .setOnException((IOException e) -> { + // 这里处理请求异常 + System.out.println(e); + }) + .get(); + + sleep(10000); + } + + + + void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } } } From 889968a04470c19d3dba64c272ab70a0be7db172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 12:44:13 +0800 Subject: [PATCH 56/94] =?UTF-8?q?=E3=80=82=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6cccea99..602688e2 100644 --- a/README.md +++ b/README.md @@ -717,7 +717,7 @@ call.cancel(); // 取消上传 ### 10 异常处理 -  相比其它的 HTTP 开发包,OkHttps 额外提供了一个特别的异常处理方发(`nothrow()`),以满足不同的异常处理需求。 +  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还是额外提供了一个特别的处理方发(`nothrow()`),以满足不同的异常处理需求。 #### 10.1 同步请求的异常 @@ -789,7 +789,7 @@ try { // 这种方式是捕获不到异常的!!!! } ``` -  但即使不设置`OnException`回调,发生异常时,依然会走`OnComplete`回调: +  即使不设置`OnException`回调,发生异常时,依然会走`OnComplete`回调: ```java http.async("/users/1") @@ -797,7 +797,8 @@ http.async("/users/1") // 当发生异常时就不会走这里 }) .setOnComplete((State state) -> { - // 发生异常,会先执行这里,但执行完后依然会在IO线程中向上抛出 + // 发生异常,会先执行这里,可以更加 state 判断发生了什么 + // 但执行完后依然会在IO线程中向上抛出 }) .get(); ``` From 5cad02590cc06cf3cb3f8f41695217e2c5dad4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 12:46:36 +0800 Subject: [PATCH 57/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 602688e2..ce385097 100644 --- a/README.md +++ b/README.md @@ -721,7 +721,7 @@ call.cancel(); // 取消上传 #### 10.1 同步请求的异常 -  默认情况下,当请求执行异常时,会直接向外抛出,我们可以用 `try catch` 来捕获,例如: +  默认情况下,当同步请求执行异常时,会直接向外抛出,我们可以用 `try catch` 来捕获,例如: ```java try { From ec54f5e680ccbebcd5e29376abbe2da6a215c27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:15:21 +0800 Subject: [PATCH 58/94] readme --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ce385097..9519bb9f 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ - [10.1 同步请求的异常](#101-同步请求的异常) - [10.2 异步请求的异常](#102-异步请求的异常) + [11 回调线程自由切换(for Android)](#11-回调线程自由切换for-android) + + [12 实现生命周期绑定(for Android)](#12-实现生命周期绑定for-android) + [参考文档](#参考文档) + [联系方式](#联系方式) @@ -357,7 +358,7 @@ HTTP http = HTTP.builder() }) .build(); ``` -  该配置影响所有回调。 +  该配置**影响所有回调**。 #### 6.3 配置 OkHttpClient @@ -888,9 +889,12 @@ http.sync("/download/test.zip") }) .start(); ``` +### 12 实现生命周期绑定(for Android) + +文档完善中,敬请期待... -## 参考文档 +## 参考文档 ## 联系方式 From f5a8569a3b66ceaaf475d37a596645961deb9154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:20:53 +0800 Subject: [PATCH 59/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9519bb9f..fd69be14 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,7 @@ HTTP http = HTTP.builder() }) .build(); ``` -  该配置**影响所有回调**。 +  该配置默认 **影响所有回调**。 #### 6.3 配置 OkHttpClient From 31eae6de662479b19ca12614bd896cb8eee1ef88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:23:32 +0800 Subject: [PATCH 60/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd69be14..d18d156f 100644 --- a/README.md +++ b/README.md @@ -349,7 +349,7 @@ http.sync("https://www.baidu.com").get() #### 6.2 回调执行器 -  如何想改变执行回调函数的线程时,可以配置回调执行器。例如在Android里,让所有的回调函数都在UI线程执行,则可以在构建`HTTP`时配置如下: +  OkHttps 默认所有回调都在 **IO线程** 执行,如何想改变执行回调的线程时,可以配置回调执行器。例如在Android里,让所有的回调函数都在UI线程执行,则可以在构建`HTTP`时配置如下: ```java HTTP http = HTTP.builder() From 462bb08338ec57446fc96af40b2fa36290bf2a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:32:51 +0800 Subject: [PATCH 61/94] readme --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d18d156f..78a25dbc 100644 --- a/README.md +++ b/README.md @@ -260,7 +260,7 @@ System.out.println(call.isCanceled()); // true ### 4 构建HTTP任务 -  `HTTP`对象的`sync`与`async`方法返回一个`HttpTask`对象,该对象提供了可链式调用的`addXXX`与`setXXX`系列方法用于构建任务本身。 +  `HTTP`对象的`sync`与`async`方法返回一个`HttpTask`对象,该对象提供了可链式调用的`addXXX`与`setXXX`等系列方法用于构建任务本身。 * `addHeader(String name, String value)` 添加请求头 * `addHeader(Map headers)` 添加请求头 @@ -384,7 +384,7 @@ HTTP http = HTTP.builder() #### 6.4 并行预处理器 -  预处理器(`Preprocessor`)可以让我们在请求发出之前对请求本身做一些改变,但与`OkHttp`的拦截器(`Interceptor`)不同:预处理器可以让我们**异步**处理这些问题。 +  预处理器(`Preprocessor`)可以让我们在请求发出之前对请求本身做一些改变,但与`OkHttp`的拦截器(`Interceptor`)不同:预处理器可以让我们 **异步** 处理这些问题。   例如,当我们想为请求任务自动添加`Token`头信息,而`Token`只能通过异步方法`requestToken`获取时,这时使用`Interceptor`就很难处理了,但可以使用预处理器轻松解决: @@ -592,7 +592,7 @@ http.async("/download/test.zip") #### 8.3 实现断点续传 -  OkHttps 对断点续传并没有再做更高层次的封装,因为这是app该去做的事情,它在设计上使各种网络问题的处理变简单的同时力求纯粹。下面的例子可以看到,OkHttps 通过一个失败回调拿到**断点**,便将复杂的问题变得简单: +  OkHttps 对断点续传并没有再做更高层次的封装,因为这是app该去做的事情,它在设计上使各种网络问题的处理变简单的同时力求纯粹。下面的例子可以看到,OkHttps 通过一个失败回调拿到 **断点**,便将复杂的问题变得简单: ```java http.sync("/download/test.zip") @@ -718,7 +718,7 @@ call.cancel(); // 取消上传 ### 10 异常处理 -  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还是额外提供了一个特别的处理方发(`nothrow()`),以满足不同的异常处理需求。 +  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还是额外提供了一个特别的处理方法(`nothrow()`),以满足不同的异常处理需求。 #### 10.1 同步请求的异常 @@ -777,7 +777,7 @@ http.async("/users/1") }) .get(); ``` -  如果不设置`OnException`回调,发生异常时会在 **IO线程池** 中向上抛出,外层无法捕获: +  如果不设置`OnException`回调,发生异常时会在 **IO 线程** 中向上抛出,外层无法捕获: ```java try { @@ -787,7 +787,7 @@ try { }) .get(); } catch (HttpException e) { - // 这种方式是捕获不到异常的!!!! + // 这种方式是捕获不到异常的!!!!!! } ```   即使不设置`OnException`回调,发生异常时,依然会走`OnComplete`回调: @@ -798,12 +798,12 @@ http.async("/users/1") // 当发生异常时就不会走这里 }) .setOnComplete((State state) -> { - // 发生异常,会先执行这里,可以更加 state 判断发生了什么 + // 发生异常,会先执行这里,可以根据 state 判断发生了什么 // 但执行完后依然会在IO线程中向上抛出 }) .get(); ``` -  如果就是想 **不处理异常,也不向上抛出**,可以做到吗?可以!使用`nothrow()`方法: +  如果就是想 **不处理异常,也不向上抛出**,可以做到吗?可以!还是使用`nothrow()`方法: ```java http.async("/users/1") From f6bee7c6017d80df398357ad8bdef0283207a90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:34:29 +0800 Subject: [PATCH 62/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78a25dbc..0d31e747 100644 --- a/README.md +++ b/README.md @@ -718,7 +718,7 @@ call.cancel(); // 取消上传 ### 10 异常处理 -  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还是额外提供了一个特别的处理方法(`nothrow()`),以满足不同的异常处理需求。 +  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还是提供了一个特别的处理方法:`nothrow()`,以满足不同的异常处理需求。 #### 10.1 同步请求的异常 From b34d4fdd43b315511ad26404294c72a92e5061c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:34:52 +0800 Subject: [PATCH 63/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0d31e747..83676cca 100644 --- a/README.md +++ b/README.md @@ -718,7 +718,7 @@ call.cancel(); // 取消上传 ### 10 异常处理 -  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还是提供了一个特别的处理方法:`nothrow()`,以满足不同的异常处理需求。 +  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还提供了一个特别的处理方法:`nothrow()`,以满足不同的异常处理需求。 #### 10.1 同步请求的异常 From 29f7472d2c351f7be021943c0433bf537ac7c2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:36:11 +0800 Subject: [PATCH 64/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83676cca..4453bbb4 100644 --- a/README.md +++ b/README.md @@ -741,7 +741,7 @@ try { // ... } ``` -  这种传统的异常处理方式,当然可以解决问题,但是 OkHttps 提供了更优雅的方案: +  这种传统的异常处理方式,当然可以解决问题,但是 OkHttps 有更佳的方案: ```java // 使用 nothrow 方法告诉 OkHttps 发生异常时不要直接向外抛出 From 77d4c3b58dc32c86bf4bea11ae093cd4b7bd73d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:39:02 +0800 Subject: [PATCH 65/94] readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4453bbb4..b27a2624 100644 --- a/README.md +++ b/README.md @@ -765,7 +765,7 @@ Exception error = result.getError(); #### 10.2 异步请求的异常 -  异步请求的使用最多的异常处理方式是设置一个异常回调: +  异步请求最常用的异常处理方式就是设置一个异常回调: ```java http.async("/users/1") @@ -803,7 +803,7 @@ http.async("/users/1") }) .get(); ``` -  如果就是想 **不处理异常,也不向上抛出**,可以做到吗?可以!还是使用`nothrow()`方法: +  如果就是想 **不处理异常,也不向上抛出**,发生错误完全无视,可以做到吗?可以!还是使用`nothrow()`方法: ```java http.async("/users/1") From c5303b41e6a97145f266bc837106a0f4be3c1f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:48:12 +0800 Subject: [PATCH 66/94] readme --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b27a2624..50c17a75 100644 --- a/README.md +++ b/README.md @@ -777,7 +777,18 @@ http.async("/users/1") }) .get(); ``` -  如果不设置`OnException`回调,发生异常时会在 **IO 线程** 中向上抛出,外层无法捕获: +  当然,还有一个全局异常监听(`ExceptionListener`): + +```java +HTTP http = HTTP.builder() + .exceptionListener((HttpTask task, IOException error) -> { + // 所有请求发生异常都会走这里 + + return true; // 返回 true 表示继续执行 task 的 OnException 回调,false 表示不再执行 + }) + .build(); +``` +  如果不设置`OnException`回调,也没有`ExceptionListener`,发生异常时会在 **IO 线程** 中向上抛出,外层无法捕获: ```java try { @@ -790,7 +801,7 @@ try { // 这种方式是捕获不到异常的!!!!!! } ``` -  即使不设置`OnException`回调,发生异常时,依然会走`OnComplete`回调: +  即使没有`OnException`回调,发生异常时,依然会走`OnComplete`回调,如果设置了的话: ```java http.async("/users/1") From 89d64a6adc5c9a367997591af86c053e9431d7c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 13:53:25 +0800 Subject: [PATCH 67/94] readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 50c17a75..11da4a26 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ http.async("/users/{id}") // http://api.demo.com/users/1 }) .setOnComplete((State state) -> { // 完成回调,无论成功失败都会执行 + // 并且在 响应|异常回调 之前执行 }) .get(); ``` From d4b6c4e5e9d577480983cbd1610e63fa0a8cd122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:02:17 +0800 Subject: [PATCH 68/94] .. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 11da4a26..54e390fc 100644 --- a/README.md +++ b/README.md @@ -719,7 +719,7 @@ call.cancel(); // 取消上传 ### 10 异常处理 -  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还提供了一个特别的处理方法:`nothrow()`,以满足不同的异常处理需求。 +  使用 OkHttps 时,**异常处理不是必须的**,但相比其它的 HTTP 开发包,它还提供一个特别的处理方法:`nothrow()`,以满足不同的异常处理需求。 #### 10.1 同步请求的异常 From c27b3ecb1b875eb4b01d2ee58ddbc20157d75000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:19:03 +0800 Subject: [PATCH 69/94] readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 54e390fc..621db2f4 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ * 单方法回调,充分利用 Lambda 表达式 * TCP连接池、Http2 +> 1. OkHttps 非常轻量,自身代码在 2000 行左右 +> 2. OkHttps 有较高的编码质量,sonarqube 结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) + ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.1] ### API文档:[https://apidoc.gitee.com/ejlchina-zhxu/okhttps](https://apidoc.gitee.com/ejlchina-zhxu/okhttps) @@ -58,8 +61,8 @@ - [10.2 异步请求的异常](#102-异步请求的异常) + [11 回调线程自由切换(for Android)](#11-回调线程自由切换for-android) + [12 实现生命周期绑定(for Android)](#12-实现生命周期绑定for-android) -+ [参考文档](#参考文档) + [联系方式](#联系方式) ++ [参与贡献](#参与贡献) ## 安装教程 @@ -906,8 +909,6 @@ http.sync("/download/test.zip") 文档完善中,敬请期待... -## 参考文档 - ## 联系方式 From ea7f08ac05a38cef32bc3b99b1a089a827a2887d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:19:43 +0800 Subject: [PATCH 70/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 621db2f4..a33f49ea 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ * TCP连接池、Http2 > 1. OkHttps 非常轻量,自身代码在 2000 行左右 -> 2. OkHttps 有较高的编码质量,sonarqube 结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) +> 2. OkHttps 有较高的编码质量,sonarqube 分析结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.1] From 1229d3a61e61c2a87076a1688e94e471355e7860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:21:49 +0800 Subject: [PATCH 71/94] readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a33f49ea..98ca90d1 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,7 @@ * 单方法回调,充分利用 Lambda 表达式 * TCP连接池、Http2 -> 1. OkHttps 非常轻量,自身代码在 2000 行左右 -> 2. OkHttps 有较高的编码质量,sonarqube 分析结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) +> OkHttps 非常轻量,自身代码在 2000 行左右,且有较高的编码质量,sonarqube 分析结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.1] From cf3cf94d230936aeef2ffe0e1249244264a1e960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:30:07 +0800 Subject: [PATCH 72/94] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98ca90d1..bdc34adf 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ * 单方法回调,充分利用 Lambda 表达式 * TCP连接池、Http2 -> OkHttps 非常轻量,自身代码在 2000 行左右,且有较高的编码质量,sonarqube 分析结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) +> OkHttps 非常轻量,自身代码在 2000 行左右,且有较高的编码质量,sonarqube 分析结果为 3A ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.1] From 311f24197307a5a877be9dd370d6d5bce5ec1d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:40:05 +0800 Subject: [PATCH 73/94] =?UTF-8?q?=E9=85=8D=E7=BD=AE=20sonar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pom.xml b/pom.xml index 1678b893..95b384e4 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,9 @@ 1.8 UTF-8 https://oss.sonatype.org/content/repositories/snapshots/ + + src/main/java/com/ejlchina/okhttps/Preprocessor + From 377a0e3ba4739a089f68605b3c1efa33e24d84ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:41:45 +0800 Subject: [PATCH 74/94] =?UTF-8?q?=E9=85=8D=E7=BD=AE=20sonar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95b384e4..c9d4cbcc 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ UTF-8 https://oss.sonatype.org/content/repositories/snapshots/ - src/main/java/com/ejlchina/okhttps/Preprocessor + src/main/java/com/ejlchina/okhttps/Preprocessor.java From 269f587dd8bbe349f75643afc495602401492b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:47:00 +0800 Subject: [PATCH 75/94] .. --- pom.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pom.xml b/pom.xml index c9d4cbcc..1678b893 100644 --- a/pom.xml +++ b/pom.xml @@ -10,9 +10,6 @@ 1.8 UTF-8 https://oss.sonatype.org/content/repositories/snapshots/ - - src/main/java/com/ejlchina/okhttps/Preprocessor.java - From dbff8b6d9dad9ad9cd9bca048170e019f55adedc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:55:53 +0800 Subject: [PATCH 76/94] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E4=BB=A3=E7=A0=81:=20HttpTask.toString(Response=20response)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/HttpTask.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HttpTask.java b/src/main/java/com/ejlchina/okhttps/HttpTask.java index ae6067be..7469ef9b 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/HttpTask.java @@ -568,16 +568,6 @@ private void buildHeaders(Request.Builder builder) { } } - - protected String toString(Response response) throws IOException { - String body = null; - ResponseBody rbody = response.body(); - if (rbody != null) { - body = rbody.string(); - } - return body; - } - protected State toState(IOException e) { if (e instanceof SocketTimeoutException) { return State.TIMEOUT; From 0e0f57b0f7bd977c20036d4e38ef4f930a7da5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 14:57:58 +0800 Subject: [PATCH 77/94] remove unused imports --- .../java/com/ejlchina/okhttps/HttpTask.java | 32 ++++++------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HttpTask.java b/src/main/java/com/ejlchina/okhttps/HttpTask.java index 7469ef9b..392484d5 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/HttpTask.java @@ -1,5 +1,14 @@ package com.ejlchina.okhttps; +import com.alibaba.fastjson.JSON; +import com.ejlchina.okhttps.HttpResult.State; +import com.ejlchina.okhttps.internal.HttpClient; +import com.ejlchina.okhttps.internal.HttpException; +import com.ejlchina.okhttps.internal.ProcessRequestBody; +import okhttp3.*; +import okhttp3.internal.Util; +import okio.Buffer; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -9,23 +18,6 @@ import java.util.HashMap; import java.util.Map; -import com.alibaba.fastjson.JSON; -import com.ejlchina.okhttps.HttpResult.State; -import com.ejlchina.okhttps.internal.HttpClient; -import com.ejlchina.okhttps.internal.HttpException; -import com.ejlchina.okhttps.internal.ProcessRequestBody; - -import okhttp3.Call; -import okhttp3.FormBody; -import okhttp3.MediaType; -import okhttp3.MultipartBody; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; -import okhttp3.internal.Util; -import okio.Buffer; - /** * Created by 周旭(Troy.Zhou) on 2020/3/11. @@ -555,8 +547,7 @@ private long contentLength(RequestBody reqBody) { throw new HttpException("无法获取请求体长度", e); } } - - + private void buildHeaders(Request.Builder builder) { if (headers != null) { for (String name : headers.keySet()) { @@ -579,7 +570,6 @@ protected State toState(IOException e) { return State.EXCEPTION; } - private RequestBody buildRequestBody() { if (jsonParams != null) { requestJson = JSON.toJSONString(jsonParams); @@ -618,8 +608,6 @@ private RequestBody buildRequestBody() { } } - - private String buildUrlPath() { String url = urlPath; if (url == null || url.trim().isEmpty()) { From e8436350146cf095fdf05b51fd96948155af899b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 15:13:13 +0800 Subject: [PATCH 78/94] =?UTF-8?q?HTTP=20=E6=8E=A5=E5=8F=A3=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=96=B9=E6=B3=95=EF=BC=9AcancelAll()=20=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E6=89=80=E6=9C=89=E8=AF=B7=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/HTTP.java | 7 ++++++- .../ejlchina/okhttps/internal/HttpClient.java | 21 ++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HTTP.java b/src/main/java/com/ejlchina/okhttps/HTTP.java index 2f6a54df..3ac7124b 100644 --- a/src/main/java/com/ejlchina/okhttps/HTTP.java +++ b/src/main/java/com/ejlchina/okhttps/HTTP.java @@ -37,7 +37,12 @@ public interface HTTP { * @return 被取消的任务数量 */ int cancel(String tag); - + + /** + * 取消所有HTTP任务,包括同步和异步 + */ + void cancelAll(); + /** * OkHttp 原生请求 (该请求不经过 预处理器) * @param request 请求 diff --git a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java index 15ab2fae..99660120 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java +++ b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java @@ -30,18 +30,18 @@ public class HttpClient implements HTTP { + + final OkHttpClient client; - private OkHttpClient client; - - private String baseUrl; + final String baseUrl; // 媒体类型 - private Map mediaTypes; + final Map mediaTypes; // 执行器 - private TaskExecutor executor; + final TaskExecutor executor; // 预处理器 - private Preprocessor[] preprocessors; + final Preprocessor[] preprocessors; - private List tagCalls; + final List tagCalls; private HttpClient(Builder builder) { @@ -85,7 +85,12 @@ public int cancel(String tag) { } return count; } - + + @Override + public void cancelAll() { + client.dispatcher().cancelAll(); + } + @Override public Call request(Request request) { return client.newCall(request); From f2f5cf13d2d585f935b98a5da383545217b9b503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 15:48:24 +0800 Subject: [PATCH 79/94] =?UTF-8?q?=E6=8A=BD=E8=B1=A1=20Cancelable=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=8C=E4=BD=BF=E5=BE=97=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E4=BB=BB=E5=8A=A1=20=E4=B9=9F=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E6=8C=89=20=E6=A0=87=E7=AD=BE=E5=8F=96=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ejlchina/okhttps/Cancelable.java | 11 ++++ src/main/java/com/ejlchina/okhttps/HTTP.java | 2 + .../java/com/ejlchina/okhttps/HttpCall.java | 2 +- .../okhttps/internal/AsyncHttpTask.java | 6 +-- .../ejlchina/okhttps/internal/HttpClient.java | 53 ++++++++----------- .../okhttps/internal/SyncHttpTask.java | 35 +++++++++++- src/test/java/com/ejlchina/test/HttpTest.java | 16 +++--- 7 files changed, 82 insertions(+), 43 deletions(-) create mode 100644 src/main/java/com/ejlchina/okhttps/Cancelable.java diff --git a/src/main/java/com/ejlchina/okhttps/Cancelable.java b/src/main/java/com/ejlchina/okhttps/Cancelable.java new file mode 100644 index 00000000..0c81a7ac --- /dev/null +++ b/src/main/java/com/ejlchina/okhttps/Cancelable.java @@ -0,0 +1,11 @@ +package com.ejlchina.okhttps; + +public interface Cancelable { + + /** + * 取消 + * @return 取消是否成功 + */ + boolean cancel(); + +} diff --git a/src/main/java/com/ejlchina/okhttps/HTTP.java b/src/main/java/com/ejlchina/okhttps/HTTP.java index 3ac7124b..5252ff53 100644 --- a/src/main/java/com/ejlchina/okhttps/HTTP.java +++ b/src/main/java/com/ejlchina/okhttps/HTTP.java @@ -33,12 +33,14 @@ public interface HTTP { /** * 根据标签取消HTTP任务,只要任务的标签包含指定的Tag就会被取消 + * 从 v1.0.2 开始支持取消同步请求 * @param tag 标签 * @return 被取消的任务数量 */ int cancel(String tag); /** + * @since 1.0.2 * 取消所有HTTP任务,包括同步和异步 */ void cancelAll(); diff --git a/src/main/java/com/ejlchina/okhttps/HttpCall.java b/src/main/java/com/ejlchina/okhttps/HttpCall.java index 8a59cd14..a7543a7b 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpCall.java +++ b/src/main/java/com/ejlchina/okhttps/HttpCall.java @@ -1,6 +1,6 @@ package com.ejlchina.okhttps; -public interface HttpCall { +public interface HttpCall extends Cancelable { /** * 取消 Http 请求 diff --git a/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java index a0d798dc..7969fc11 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java @@ -113,7 +113,7 @@ private HttpCall request(String method) { } }); if (tag != null) { - httpClient.addTagCall(tag, call, this); + httpClient.addTagTask(tag, call, this); } return call; } @@ -136,7 +136,7 @@ public boolean cancel() { notify(); } if (tag != null && call == null) { - httpClient.removeTagCall(AsyncHttpTask.this); + httpClient.removeTagTask(AsyncHttpTask.this); } return res; } @@ -228,7 +228,7 @@ public void setResult(HttpResult result) { notify(); } if (tag != null) { - httpClient.removeTagCall(AsyncHttpTask.this); + httpClient.removeTagTask(AsyncHttpTask.this); } } diff --git a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java index 99660120..15e69497 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java +++ b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java @@ -11,14 +11,7 @@ import java.util.Queue; import java.util.concurrent.Executor; -import com.ejlchina.okhttps.Configurator; -import com.ejlchina.okhttps.DownListener; -import com.ejlchina.okhttps.HTTP; -import com.ejlchina.okhttps.HttpCall; -import com.ejlchina.okhttps.HttpResult; -import com.ejlchina.okhttps.HttpTask; -import com.ejlchina.okhttps.Preprocessor; -import com.ejlchina.okhttps.TaskListener; +import com.ejlchina.okhttps.*; import com.ejlchina.okhttps.HttpResult.State; import okhttp3.Call; @@ -30,9 +23,9 @@ public class HttpClient implements HTTP { - + // OkHttpClient final OkHttpClient client; - + // 根URL final String baseUrl; // 媒体类型 final Map mediaTypes; @@ -40,8 +33,8 @@ public class HttpClient implements HTTP { final TaskExecutor executor; // 预处理器 final Preprocessor[] preprocessors; - - final List tagCalls; + // 持有标签的任务 + final List tagTasks; private HttpClient(Builder builder) { @@ -52,8 +45,8 @@ private HttpClient(Builder builder) { builder.mainExecutor, builder.downloadListener, builder.responseListener, builder.exceptionListener, builder.completeListener); - this.preprocessors = builder.preprocessors.toArray(new Preprocessor[builder.preprocessors.size()]); - this.tagCalls = Collections.synchronizedList(new LinkedList<>()); + this.preprocessors = builder.preprocessors.toArray(new Preprocessor[0]); + this.tagTasks = Collections.synchronizedList(new LinkedList<>()); } @Override @@ -72,12 +65,12 @@ public int cancel(String tag) { return 0; } int count = 0; - Iterator it = tagCalls.iterator(); + Iterator it = tagTasks.iterator(); while (it.hasNext()) { - TagCall tagCall = it.next(); + TagTask tagCall = it.next(); // 只要任务的标签包含指定的Tag就会被取消 if (tagCall.tag.contains(tag)) { - if (tagCall.call.cancel()) { + if (tagCall.canceler.cancel()) { count++; } it.remove(); @@ -109,18 +102,18 @@ public int totalTimeoutMillis() { return client.connectTimeoutMillis() + client.writeTimeoutMillis() + client.readTimeoutMillis(); } - public int getTagCallCount() { - return tagCalls.size(); + public int getTagTaskCount() { + return tagTasks.size(); } - public void addTagCall(String tag, HttpCall call, HttpTask task) { - tagCalls.add(new TagCall(tag, call, task)); + public void addTagTask(String tag, Cancelable canceler, HttpTask task) { + tagTasks.add(new TagTask(tag, canceler, task)); } - public void removeTagCall(HttpTask task) { - Iterator it = tagCalls.iterator(); + public void removeTagTask(HttpTask task) { + Iterator it = tagTasks.iterator(); while (it.hasNext()) { - TagCall tagCall = it.next(); + TagTask tagCall = it.next(); if (tagCall.task == task) { it.remove(); break; @@ -128,15 +121,15 @@ public void removeTagCall(HttpTask task) { } } - class TagCall { - + static class TagTask { + String tag; - HttpCall call; + Cancelable canceler; HttpTask task; - - public TagCall(String tag, HttpCall call, HttpTask task) { + + public TagTask(String tag, Cancelable canceler, HttpTask task) { this.tag = tag; - this.call = call; + this.canceler = canceler; this.task = task; } diff --git a/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java b/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java index 648c8357..cbb9cdd4 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java @@ -2,6 +2,7 @@ import java.io.IOException; +import com.ejlchina.okhttps.Cancelable; import com.ejlchina.okhttps.HttpResult; import com.ejlchina.okhttps.HttpTask; import com.ejlchina.okhttps.HttpResult.State; @@ -53,13 +54,45 @@ public HttpResult put() { public HttpResult delete() { return request("DELETE"); } - + + static class SyncCall implements Cancelable { + + final Call call; + boolean done = false; + + public SyncCall(Call call) { + this.call = call; + } + + @Override + public boolean cancel() { + if (done) { + return false; + } + call.cancel(); + return true; + } + + public void setDone(boolean done) { + this.done = done; + } + + } + private HttpResult request(String method) { RealHttpResult result = new RealHttpResult(this, httpClient.getExecutor()); httpClient.preprocess(this, () -> { Call call = prepareCall(method); + SyncCall syncCall = null; + if (tag != null) { + syncCall = new SyncCall(call); + httpClient.addTagTask(tag, syncCall, this); + } try { Response response = call.execute(); + if (syncCall != null) { + syncCall.setDone(true); + } synchronized (SyncHttpTask.this) { result.response(response); SyncHttpTask.this.notify(); diff --git a/src/test/java/com/ejlchina/test/HttpTest.java b/src/test/java/com/ejlchina/test/HttpTest.java index e8951dab..c638d69d 100644 --- a/src/test/java/com/ejlchina/test/HttpTest.java +++ b/src/test/java/com/ejlchina/test/HttpTest.java @@ -396,7 +396,7 @@ public void testCancel() { .setTag("A") .get(); - System.out.println(((HttpClient) http).getTagCallCount()); + System.out.println(((HttpClient) http).getTagTaskCount()); http.async("/user/show/2") .setOnResponse((HttpResult result) -> { @@ -405,7 +405,7 @@ public void testCancel() { .setTag("A.B") .get(); - System.out.println(((HttpClient) http).getTagCallCount()); + System.out.println(((HttpClient) http).getTagTaskCount()); http.async("/user/show/3") .setOnResponse((HttpResult result) -> { @@ -414,7 +414,7 @@ public void testCancel() { .setTag("B.C") .get(); - System.out.println(((HttpClient) http).getTagCallCount()); + System.out.println(((HttpClient) http).getTagTaskCount()); http.async("/user/show/4") .setOnResponse((HttpResult result) -> { @@ -423,24 +423,24 @@ public void testCancel() { .setTag("C") .get(); - System.out.println(((HttpClient) http).getTagCallCount()); + System.out.println(((HttpClient) http).getTagTaskCount()); System.out.println("标签取消:" + http.cancel("B")); - System.out.println(((HttpClient) http).getTagCallCount()); + System.out.println(((HttpClient) http).getTagTaskCount()); sleep(5000); - System.out.println(((HttpClient) http).getTagCallCount()); + System.out.println(((HttpClient) http).getTagTaskCount()); sleep(5000); - System.out.println(((HttpClient) http).getTagCallCount()); + System.out.println(((HttpClient) http).getTagTaskCount()); sleep(5000); - System.out.println(((HttpClient) http).getTagCallCount()); + System.out.println(((HttpClient) http).getTagTaskCount()); // System.out.println("isDone = " + call.isDone()); // System.out.println("isCanceled = " + call.isCanceled()); From 52f628cbe023cbbaef4d2b3cd3631a264fec7548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 16:01:01 +0800 Subject: [PATCH 80/94] . --- src/main/java/com/ejlchina/okhttps/Preprocessor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ejlchina/okhttps/Preprocessor.java b/src/main/java/com/ejlchina/okhttps/Preprocessor.java index d0f6f96c..a1fdd173 100644 --- a/src/main/java/com/ejlchina/okhttps/Preprocessor.java +++ b/src/main/java/com/ejlchina/okhttps/Preprocessor.java @@ -19,8 +19,9 @@ interface PreChain { /** * @return 当前的请求任务 */ + @SuppressWarnings("") HttpTask getTask(); - + /** * @return HTTP */ From 7dfe92f4607b76ff2b4ef33635ea64cb4e7acc86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 16:04:38 +0800 Subject: [PATCH 81/94] . --- src/main/java/com/ejlchina/okhttps/Preprocessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/ejlchina/okhttps/Preprocessor.java b/src/main/java/com/ejlchina/okhttps/Preprocessor.java index a1fdd173..c090c56e 100644 --- a/src/main/java/com/ejlchina/okhttps/Preprocessor.java +++ b/src/main/java/com/ejlchina/okhttps/Preprocessor.java @@ -19,7 +19,7 @@ interface PreChain { /** * @return 当前的请求任务 */ - @SuppressWarnings("") + @SuppressWarnings("all") HttpTask getTask(); /** From 5d2429baab904735ea320c3409b21fa2096940d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 16:10:01 +0800 Subject: [PATCH 82/94] . --- src/main/java/com/ejlchina/okhttps/Preprocessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/ejlchina/okhttps/Preprocessor.java b/src/main/java/com/ejlchina/okhttps/Preprocessor.java index c090c56e..12454e6e 100644 --- a/src/main/java/com/ejlchina/okhttps/Preprocessor.java +++ b/src/main/java/com/ejlchina/okhttps/Preprocessor.java @@ -19,7 +19,7 @@ interface PreChain { /** * @return 当前的请求任务 */ - @SuppressWarnings("all") + @SuppressWarnings("rawtypes") HttpTask getTask(); /** From 38d09fe668ed359dcd4fd2c0aeff1f29ad3e3c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 16:13:42 +0800 Subject: [PATCH 83/94] . --- README.md | 2 +- src/main/java/com/ejlchina/okhttps/Preprocessor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bdc34adf..98ca90d1 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ * 单方法回调,充分利用 Lambda 表达式 * TCP连接池、Http2 -> OkHttps 非常轻量,自身代码在 2000 行左右,且有较高的编码质量,sonarqube 分析结果为 3A +> OkHttps 非常轻量,自身代码在 2000 行左右,且有较高的编码质量,sonarqube 分析结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.1] diff --git a/src/main/java/com/ejlchina/okhttps/Preprocessor.java b/src/main/java/com/ejlchina/okhttps/Preprocessor.java index 12454e6e..c090c56e 100644 --- a/src/main/java/com/ejlchina/okhttps/Preprocessor.java +++ b/src/main/java/com/ejlchina/okhttps/Preprocessor.java @@ -19,7 +19,7 @@ interface PreChain { /** * @return 当前的请求任务 */ - @SuppressWarnings("rawtypes") + @SuppressWarnings("all") HttpTask getTask(); /** From a399222d077f3a5d0d10b3a143716011be2c7638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 16:51:52 +0800 Subject: [PATCH 84/94] V1.0.2 ^_^ --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 1678b893..b8e2b970 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ com.ejlchina okhttps - 1.0.1 + 1.0.2 OkHttps @@ -32,7 +32,7 @@ scm:git://gitee.com/ejlchina-zhxu/okhttps.git scm:http://gitee.com/ejlchina-zhxu/okhttps https://gitee.com/ejlchina-zhxu/okhttps - v1.0.1 + v1.0.2 From 15c74c7b1f14d77ad6c4d47465faf9e470ccc350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 17:21:04 +0800 Subject: [PATCH 85/94] Tests --- src/test/java/com/ejlchina/test/BaseTest.java | 49 ++ .../java/com/ejlchina/test/CacheTests.java | 27 + .../java/com/ejlchina/test/CancelTests.java | 97 +++ .../java/com/ejlchina/test/DownloadTests.java | 140 ++++ .../com/ejlchina/test/ExceptionTests.java | 18 +- .../java/com/ejlchina/test/ExecutorTests.java | 39 ++ src/test/java/com/ejlchina/test/HttpTest.java | 611 ------------------ .../java/com/ejlchina/test/JsonTests.java | 61 ++ .../java/com/ejlchina/test/ListenerTests.java | 94 +++ .../com/ejlchina/test/PreprocessorTests.java | 60 ++ .../java/com/ejlchina/test/SimpleTests.java | 28 +- .../java/com/ejlchina/test/UploadTests.java | 36 ++ 12 files changed, 610 insertions(+), 650 deletions(-) create mode 100644 src/test/java/com/ejlchina/test/BaseTest.java create mode 100644 src/test/java/com/ejlchina/test/CacheTests.java create mode 100644 src/test/java/com/ejlchina/test/CancelTests.java create mode 100644 src/test/java/com/ejlchina/test/DownloadTests.java create mode 100644 src/test/java/com/ejlchina/test/ExecutorTests.java delete mode 100644 src/test/java/com/ejlchina/test/HttpTest.java create mode 100644 src/test/java/com/ejlchina/test/JsonTests.java create mode 100644 src/test/java/com/ejlchina/test/ListenerTests.java create mode 100644 src/test/java/com/ejlchina/test/PreprocessorTests.java create mode 100644 src/test/java/com/ejlchina/test/UploadTests.java diff --git a/src/test/java/com/ejlchina/test/BaseTest.java b/src/test/java/com/ejlchina/test/BaseTest.java new file mode 100644 index 00000000..bd96497b --- /dev/null +++ b/src/test/java/com/ejlchina/test/BaseTest.java @@ -0,0 +1,49 @@ +package com.ejlchina.test; + +public class BaseTest { + + public static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public static void println() { + System.out.println(); + } + + public static void println(Object x) { + System.out.println(x); + } + + public static void println(long t0, String str) { + System.out.println(now() - t0 + "\t" + str); + } + + public static long now() { + return System.currentTimeMillis(); + } + + public static class User { + + private int id; + private String name; + + public void setId(int id) { + this.id = id; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "User [id=" + id + ", name=" + name + "]"; + } + + } + +} diff --git a/src/test/java/com/ejlchina/test/CacheTests.java b/src/test/java/com/ejlchina/test/CacheTests.java new file mode 100644 index 00000000..baa95483 --- /dev/null +++ b/src/test/java/com/ejlchina/test/CacheTests.java @@ -0,0 +1,27 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpResult; +import org.junit.Test; + +public class CacheTests extends BaseTest { + + @Test + public void testCache() { + HTTP http = HTTP.builder().build(); + + HttpResult.Body body = http.sync("http://xxx.cdyun.vip/comm/provinces") + .get() + .getBody() + .cache(); + + println("result = " + body.toString()); + println("result = " + body.toJsonArray()); + + body.close(); + + println("result = " + body.toString()); + println("result = " + body.toJsonArray()); + } + +} diff --git a/src/test/java/com/ejlchina/test/CancelTests.java b/src/test/java/com/ejlchina/test/CancelTests.java new file mode 100644 index 00000000..ebd5aaa0 --- /dev/null +++ b/src/test/java/com/ejlchina/test/CancelTests.java @@ -0,0 +1,97 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.internal.HttpClient; +import org.junit.Test; + +import java.io.IOException; + +/** + * 测试取消 + */ +public class CancelTests extends BaseTest { + + + @Test + public void testCancel() { + + HTTP http = HTTP.builder() + .baseUrl("http://localhost:8080") + .build(); + + http.async("/user/show/1") + .setOnResponse((HttpResult result) -> { + println(result); + }) + .setOnException((IOException e) -> { + println("异常捕获:" + e.getMessage()); + }) + .setOnComplete((HttpResult.State state) -> { + println(state); + }) + .setTag("A") + .get(); + + println(((HttpClient) http).getTagTaskCount()); + + http.async("/user/show/2") + .setOnResponse((HttpResult result) -> { + println(result); + }) + .setTag("A.B") + .get(); + + println(((HttpClient) http).getTagTaskCount()); + + http.async("/user/show/3") + .setOnResponse((HttpResult result) -> { + println(result); + }) + .setTag("B.C") + .get(); + + println(((HttpClient) http).getTagTaskCount()); + + http.async("/user/show/4") + .setOnResponse((HttpResult result) -> { + println(result); + }) + .setTag("C") + .get(); + + println(((HttpClient) http).getTagTaskCount()); + + + println("标签取消:" + http.cancel("B")); + + println(((HttpClient) http).getTagTaskCount()); + + sleep(5000); + + println(((HttpClient) http).getTagTaskCount()); + + sleep(5000); + + println(((HttpClient) http).getTagTaskCount()); + + sleep(5000); + + println(((HttpClient) http).getTagTaskCount()); + +// println("isDone = " + call.isDone()); +// println("isCanceled = " + call.isCanceled()); +// +// println("取消结果 = " + call.cancel()); +// +// println("isDone = " + call.isDone()); +// println("isCanceled = " + call.isCanceled()); +// +// sleep(100); +// println("++++++++"); +// +// println("isDone = " + call.isDone()); +// println("isCanceled = " + call.isCanceled()); + } + +} diff --git a/src/test/java/com/ejlchina/test/DownloadTests.java b/src/test/java/com/ejlchina/test/DownloadTests.java new file mode 100644 index 00000000..12113de8 --- /dev/null +++ b/src/test/java/com/ejlchina/test/DownloadTests.java @@ -0,0 +1,140 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.*; +import com.ejlchina.okhttps.Process; +import okhttp3.OkHttpClient; +import org.junit.Test; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +public class DownloadTests extends BaseTest { + + + /** + * 分块下载测试 + * @param args + */ + public static void main(String[] args) { + long totalSize = HttpUtils.sync("/download/test.zip").get().getBody() + .close() // 因为这次请求只是为了获得文件大小,不消费报文体,所以直接关闭 + .getContentLength(); + download(totalSize, 0); // 从第 0 块开始下载 + sleep(50000); // 等待下载完成 + } + + static void download(long totalSize, int index) { + long size = 3 * 1024 * 1024; // 每块下载 3M + long start = index * size; + long end = Math.min(start + size, totalSize); + HttpUtils.sync("/download/test.zip") + .setRange(start, end) // 设置本次下载的范围 + .get().getBody() + .toFile("D:/download/test.zip") // 下载到同一个文件里 + .setAppended() // 开启文件追加模式 + .setOnSuccess((File file) -> { + if (end < totalSize) { // 若未下载完,则继续下载下一块 + download(totalSize, index + 1); + } else { + println("下载完成"); + } + }) + .start(); + } + + + + @Test + public void testDownload() { + HTTP http = HTTP.builder() + .config((OkHttpClient.Builder builder) -> { + builder.readTimeout(300, TimeUnit.MILLISECONDS); + }) + .build(); + + String url = "https://download.cocos.com/CocosDashboard/v1.0.1/CocosDashboard-v1.0.1-win32-031816.exe"; +// String url = "http://47.100.7.202/ejl-test.zip"; + + long t0 = now(); + +// Ctrl ctrl = + http.sync(url) +// .setRange(24771214) + .bind(this) + .get() + .getBody() + .setOnProcess((Process process) -> { + println(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate()); + }) + .setStepRate(0.1) + .toFolder("D:/WorkSpace/download/") +// .toFile("D:\\WorkSpace\\download\\CocosDashboard-v1.0.1-win32-031816(9).exe") +// .setAppended() // 启用 断点续传 + .nextOnIO() + .setOnSuccess((File file) -> { + println(t0, "下载成功:" + file.getAbsolutePath()); + println(); + }) + .setOnFailure((Download.Failure failure) -> { + println(t0, "下载失败:" + failure.getDoneBytes() + ", path = " + failure.getFile().getAbsolutePath()); + println(); + }) + .start(); + + sleep(10000); + +// ctrl.status(); +// ctrl.pause(); +// println("暂停"); +// sleep(5000); +// +// ctrl.resume(); +// println("继续"); +// sleep(5000); +// +// ctrl.cancel(); +// println("取消"); + + +// + +// +// ctrl.pause(); +// println("暂停"); +// sleep(5000); +// +// ctrl.resume(); +// println("继续"); + sleep(5000); + + } + + + @Test + public void syncHttpExample() { + + HTTP http = HTTP.builder().build(); + // 同步请求 + HttpResult result = http.sync("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584365400942&di=d9e7890f13b7bc4b76080fdd490ed5d5&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D1484500186%2C1503043093%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853") + .get(); + + result.getBody().toFile("E:/3.jpg"); +// // 得到状态码 +// int status = result.getStatus(); +// +// // 得到返回头 +// Headers headers = result.getHeaders(); +// +// User user = result.getBody().toBean(User.class); +// // 得到目标数据 +// +// +// println("status = " + status); +// println("headers = " + headers); +// println("user = " + user); + + println("status = " + result.getStatus()); + + } + +} diff --git a/src/test/java/com/ejlchina/test/ExceptionTests.java b/src/test/java/com/ejlchina/test/ExceptionTests.java index aafeedf1..7cee825b 100644 --- a/src/test/java/com/ejlchina/test/ExceptionTests.java +++ b/src/test/java/com/ejlchina/test/ExceptionTests.java @@ -12,7 +12,10 @@ import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; -public class ExceptionTests { +/** + * 测试异常处理 + */ +public class ExceptionTests extends BaseTest { HTTP http = HTTP.builder() .config((OkHttpClient.Builder builder) -> { @@ -78,11 +81,11 @@ public void testASync() { http.async("/users/1") .setOnResponse((HttpResult result) -> { // 当发生异常时就不会走这里 - System.out.println(result); + println(result); }) .setOnException((IOException e) -> { // 这里处理请求异常 - System.out.println(e); + println(e); }) .get(); @@ -90,13 +93,4 @@ public void testASync() { } - - void sleep(int millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } diff --git a/src/test/java/com/ejlchina/test/ExecutorTests.java b/src/test/java/com/ejlchina/test/ExecutorTests.java new file mode 100644 index 00000000..8f82422b --- /dev/null +++ b/src/test/java/com/ejlchina/test/ExecutorTests.java @@ -0,0 +1,39 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.Process; +import org.junit.Test; + +public class ExecutorTests extends BaseTest { + + + @Test + public void testExecutor() { + HTTP http = HTTP.builder() + .callbackExecutor((Runnable command) -> { + println("主线程执行"); + command.run(); + }).build(); + + http.async("http://47.100.7.202/ejl-test.zip") + .addBodyParam("name", "Jack") +// .nextOnIO() + .setOnProcess((Process process) -> { + println("process: " + process.getRate()); + }) +// .nextOnIO() + .setOnResponse((HttpResult result) -> { + println("status: " + result.close().getStatus()); + }) + .nextOnIO() + .setOnComplete((HttpResult.State state) -> { + println("state: " + state); + }) + .post(); + + sleep(3000); + } + + +} diff --git a/src/test/java/com/ejlchina/test/HttpTest.java b/src/test/java/com/ejlchina/test/HttpTest.java deleted file mode 100644 index c638d69d..00000000 --- a/src/test/java/com/ejlchina/test/HttpTest.java +++ /dev/null @@ -1,611 +0,0 @@ -package com.ejlchina.test; - - -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import org.junit.Test; - -import com.ejlchina.okhttps.Download; -import com.ejlchina.okhttps.HTTP; -import com.ejlchina.okhttps.HttpCall; -import com.ejlchina.okhttps.HttpResult; -import com.ejlchina.okhttps.HttpTask; -import com.ejlchina.okhttps.HttpUtils; -import com.ejlchina.okhttps.Process; -import com.ejlchina.okhttps.Download.Ctrl; -import com.ejlchina.okhttps.Download.Failure; -import com.ejlchina.okhttps.HttpResult.Body; -import com.ejlchina.okhttps.HttpResult.State; -import com.ejlchina.okhttps.Preprocessor.PreChain; -import com.ejlchina.okhttps.internal.HttpClient; - -import okhttp3.ConnectionPool; -import okhttp3.Interceptor.Chain; -import okhttp3.OkHttpClient.Builder; -import okhttp3.Request; - - -public class HttpTest { - - - @Test - public void testCache() { - HTTP http = HTTP.builder().build(); - - Body body = http.sync("http://xxx.cdyun.vip/comm/provinces") - .get() - .getBody() - .cache() - ; - - - System.out.println("result = " + body.toString()); - System.out.println("result = " + body.toJsonArray()); - System.out.println("result = " + body.toBytes()); - - body.close(); - - System.out.println("result = " + body.toString()); - System.out.println("result = " + body.toJsonArray()); - System.out.println("result = " + body.toBytes()); - } - - - @Test - public void testGlobalCallback() { - HTTP http = HTTP.builder() - .responseListener((HttpTask task, HttpResult result) -> { - System.out.println("全局 onResponse: " + result.getStatus()); - return true; - }) - .completeListener((HttpTask task, State state) -> { - System.out.println("全局 onComplete: " + state); - return true; - }) - .exceptionListener((HttpTask task, IOException error) -> { - System.out.println("全局 onException: " + error.getMessage()); - return false; - }) - .build(); - - http.async("http://www.baidu.com") - .setOnResponse((HttpResult result) -> { - System.out.println("单例 onResponse: " + result.getStatus()); - }) - .setOnComplete((State state) -> { - System.out.println("单例 onComplete: " + state); - }) - .setOnException((IOException error) -> { - System.out.println("单例 onException: " + error.getMessage()); - }) - .get(); - - sleep(2000); - } - - @Test - public void testDownloadListener() { - HTTP http = HTTP.builder() - .downloadListener((HttpTask task, Download download) -> { - System.out.println("URL = " + task.getUrl()); - System.out.println("TAG = " + task.getTag()); - Ctrl ctrl = download.getCtrl(); - - new Thread(() -> { - sleep(3000); - ctrl.pause(); - sleep(3000); - ctrl.resume(); - sleep(3000); - ctrl.cancel(); - }).start(); - }) - .build(); - - String url = "https://download.cocos.com/CocosDashboard/v1.0.1/CocosDashboard-v1.0.1-win32-031816.exe"; - - long t0 = System.currentTimeMillis(); - - - http.sync(url) - .setTag("ASD") - .get() - .getBody() - .setOnProcess((Process process) -> { - print(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); - }) - .setStepRate(0.01) - .toFolder("D:/download/") - .nextOnIO() - .setOnSuccess((File file) -> { - print(t0, "下载成功:" + file.getAbsolutePath(), true); - }) - .setOnFailure((Failure failure) -> { - print(t0, "下载失败:" + failure.getDoneBytes() + ", path = " + failure.getFile().getAbsolutePath(), true); - }) - .start(); - - sleep(20000); - } - - @Test - public void testExecutor() { - HTTP http = HTTP.builder() - .callbackExecutor((Runnable command) -> { - System.out.println("主线程执行"); - command.run(); - }).build(); - - http.async("http://47.100.7.202/ejl-test.zip") - .addBodyParam("name", "Jack") -// .nextOnIO() - .setOnProcess((Process process) -> { - System.out.println("process: " + process.getRate()); - }) -// .nextOnIO() - .setOnResponse((HttpResult result) -> { - System.out.println("status: " + result.close().getStatus()); - }) - .nextOnIO() - .setOnComplete((State state) -> { - System.out.println("state: " + state); - }) - .post(); - - sleep(3000); - } - - - public static void main(String[] args) { - long totalSize = HttpUtils.sync("/download/test.zip").get().getBody() - .close() // 因为这次请求只是为了获得文件大小,不消费报文体,所以直接关闭 - .getContentLength(); - download(totalSize, 0); // 从第 0 块开始下载 - sleep(50000); // 等待下载完成 - } - - static void download(long totalSize, int index) { - long size = 3 * 1024 * 1024; // 每块下载 3M - long start = index * size; - long end = Math.min(start + size, totalSize); - HttpUtils.sync("/download/test.zip") - .setRange(start, end) // 设置本次下载的范围 - .get().getBody() - .toFile("D:/download/test.zip") // 下载到同一个文件里 - .setAppended() // 开启文件追加模式 - .setOnSuccess((File file) -> { - if (end < totalSize) { // 若未下载完,则继续下载下一块 - download(totalSize, index + 1); - } else { - System.out.println("下载完成"); - } - }) - .start(); - } - - - @Test - public void testD() { - System.out.println(Long.parseLong("12")); - } - - - @Test - public void testUpload() { - String data = "0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz"; - - HTTP http = HTTP.builder().build(); - - long t0 = System.currentTimeMillis(); - - String res = http.sync("http://localhost:8080/test/index") - .addBodyParam("data", data) - .addFileParam("file", "D:\\WorkSpace\\download\\CocosDashboard-v1.0.1-win32-031816.exe") - .setStepRate(0.01) - .setOnProcess((Process process) -> { - print(t0, "上传:" + process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); - }) - .post() - .getBody() - .setStepBytes(5) - .setOnProcess((Process process) -> { - print(t0, "下载:" + process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); - }) - .toString(); - - System.out.println("响应:" + res); - } - - @Test - public void testDownload() { - HTTP http = HTTP.builder() - .config((Builder builder) -> { - builder.readTimeout(300, TimeUnit.MILLISECONDS); - }) - .build(); - - String url = "https://download.cocos.com/CocosDashboard/v1.0.1/CocosDashboard-v1.0.1-win32-031816.exe"; -// String url = "http://47.100.7.202/ejl-test.zip"; - - long t0 = System.currentTimeMillis(); - -// Ctrl ctrl = - http.sync(url) -// .setRange(24771214) - .bind(this) - .get() - .getBody() - .setOnProcess((Process process) -> { - print(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); - }) - .setStepRate(0.1) - .toFolder("D:/WorkSpace/download/") -// .toFile("D:\\WorkSpace\\download\\CocosDashboard-v1.0.1-win32-031816(9).exe") -// .setAppended() // 启用 断点续传 - .nextOnIO() - .setOnSuccess((File file) -> { - print(t0, "下载成功:" + file.getAbsolutePath(), true); - }) - .setOnFailure((Failure failure) -> { - print(t0, "下载失败:" + failure.getDoneBytes() + ", path = " + failure.getFile().getAbsolutePath(), true); - }) - .start(); - - sleep(10000); - -// ctrl.status(); -// ctrl.pause(); -// System.out.println("暂停"); -// sleep(5000); -// -// ctrl.resume(); -// System.out.println("继续"); -// sleep(5000); -// -// ctrl.cancel(); -// System.out.println("取消"); - - -// - -// -// ctrl.pause(); -// System.out.println("暂停"); -// sleep(5000); -// -// ctrl.resume(); -// System.out.println("继续"); - sleep(5000); - - } - - void print(long t0, String str, boolean ln) { - long now = System.currentTimeMillis() - t0; - System.out.println((now) + "\t" + str); - if (ln) { - System.out.println(); - } - } - - long now(long t0) { - return System.currentTimeMillis() - t0; - } - - - @Test - public void testToList() { - long t0 = System.currentTimeMillis(); - - HTTP http = HTTP.builder() - .baseUrl("http://xxx.cdyun.vip/ejlchina") - .build(); - - HttpResult result = http.sync("/comm/provinces") - .setRange(0) - .get(); - - print(t0, "status: " + result.getStatus(), true); - print(t0, "headers: " + result.getHeaders(), true); - - Body body = result.getBody(); - - print(t0, "total: " + body.getContentLength(), true); - - List list = body.setStepRate(0.1) - .setOnProcess((Process process) -> { - print(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate(), false); - }) - .toList(User.class); - - print(t0, list.toString(), false); - } - - - @Test - public void testPreprocessor() { - - HTTP http = HTTP.builder() - .baseUrl("http://localhost:8080") - .addPreprocessor((PreChain chain) -> { - System.out.println("并行预处理-开始"); -// new Thread(() -> { - sleep(2000); - System.out.println("并行预处理-结束"); - chain.proceed(); -// }).start(); - }) - .addSerialPreprocessor((PreChain chain) -> { - System.out.println("串行预处理-开始"); - new Thread(() -> { - sleep(3000); - System.out.println("串行预处理-结束"); - chain.proceed(); - }).start(); - }) - .build(); - - new Thread(() -> { - System.out.println(http.sync("/user/show/1").get()); - }).start(); - - new Thread(() -> { - System.out.println(http.sync("/user/show/2").get()); - }).start(); - -// new Thread(() -> { -// http.async("/user/show/1") -// .setOnResponse((HttpResult result) -> { -// System.out.println(result); -// }) -// .get(); -// }).start(); -// -// new Thread(() -> { -// http.async("/user/show/2") -// .setOnResponse((HttpResult result) -> { -// System.out.println(result); -// }) -// .get(); -// }).start(); - - sleep(10000); - } - - - - @Test - public void testCancel() { - - HTTP http = HTTP.builder() - .baseUrl("http://localhost:8080") - .build(); - - http.async("/user/show/1") - .setOnResponse((HttpResult result) -> { - System.out.println(result); - }) - .setOnException((IOException e) -> { - System.out.println("异常捕获:" + e.getMessage()); - }) - .setOnComplete((State state) -> { - System.out.println(state); - }) - .setTag("A") - .get(); - - System.out.println(((HttpClient) http).getTagTaskCount()); - - http.async("/user/show/2") - .setOnResponse((HttpResult result) -> { - System.out.println(result); - }) - .setTag("A.B") - .get(); - - System.out.println(((HttpClient) http).getTagTaskCount()); - - http.async("/user/show/3") - .setOnResponse((HttpResult result) -> { - System.out.println(result); - }) - .setTag("B.C") - .get(); - - System.out.println(((HttpClient) http).getTagTaskCount()); - - http.async("/user/show/4") - .setOnResponse((HttpResult result) -> { - System.out.println(result); - }) - .setTag("C") - .get(); - - System.out.println(((HttpClient) http).getTagTaskCount()); - - - System.out.println("标签取消:" + http.cancel("B")); - - System.out.println(((HttpClient) http).getTagTaskCount()); - - sleep(5000); - - System.out.println(((HttpClient) http).getTagTaskCount()); - - sleep(5000); - - System.out.println(((HttpClient) http).getTagTaskCount()); - - sleep(5000); - - System.out.println(((HttpClient) http).getTagTaskCount()); - -// System.out.println("isDone = " + call.isDone()); -// System.out.println("isCanceled = " + call.isCanceled()); -// -// System.out.println("取消结果 = " + call.cancel()); -// -// System.out.println("isDone = " + call.isDone()); -// System.out.println("isCanceled = " + call.isCanceled()); -// -// sleep(100); -// System.out.println("++++++++"); -// -// System.out.println("isDone = " + call.isDone()); -// System.out.println("isCanceled = " + call.isCanceled()); - } - - - - - private HTTP buildHttp() { - - return HTTP.builder() - .config((Builder builder) -> { - - // 配置连接池 最小10个连接(不配置默认为 5) - builder.connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES)); - - // 配置连接超时时间 - builder.connectTimeout(20, TimeUnit.SECONDS); - - builder.addInterceptor((Chain chain) -> { - - Request request = chain.request(); - - return chain.proceed(request); - }); - }) - .baseUrl("http://localhost:8080") - .callbackExecutor((Runnable run) -> { - runOnUiThread(run); - }) - .addPreprocessor((PreChain chain) -> { - new Thread(() -> { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - chain.getTask().addHeader("Token", "yyyyy"); - - chain.proceed(); - - }).start(); - }) - .addPreprocessor((PreChain chain) -> { - new Thread(() -> { - - chain.getTask().addUrlParam("actor", "Alice"); - - chain.proceed(); - - }).start(); - }) - .build(); - - } - - - @Test - public void syncHttpExample() { - - HTTP http = buildHttp(); - // 同步请求 - HttpResult result = http.sync("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584365400942&di=d9e7890f13b7bc4b76080fdd490ed5d5&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D1484500186%2C1503043093%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1280%26h%3D853") - .get(); - - result.getBody().toFile("E:/3.jpg"); -// // 得到状态码 -// int status = result.getStatus(); -// -// // 得到返回头 -// Headers headers = result.getHeaders(); -// -// User user = result.getBody().toBean(User.class); -// // 得到目标数据 -// -// -// System.out.println("status = " + status); -// System.out.println("headers = " + headers); -// System.out.println("user = " + user); - - System.out.println("status = " + result.getStatus()); - - } - - - @Test - public void syncJsonExample() { - HTTP http = buildHttp(); - // 同步请求 - HttpResult result = http.sync("/user/save") - .addJsonParam("name", "Tom") - .addJsonParam("age", 23) - .post(); - - System.out.println("result = " + result); - - result = http.sync("/user/show/1").get(); - - System.out.println("result = " + result); - - System.out.println("isSuccessful = " + result.isSuccessful()); - } - - @Test - public void asyncHttpExample() throws InterruptedException { - HTTP http = buildHttp(); - // 异步请求 - // 最终路径 http://api.demo.com/users/2 - HttpCall call = http.async("/user/show/{id}") - // 设置路径参数 - .addPathParam("id", 2) - // 设置回调函数 - .setOnResponse((HttpResult result) -> { - System.out.println("000"); -// User user = result.getBody().toBean(User.class); -// System.out.println("user = " + user); - }) - .setOnException((IOException e) -> { - e.printStackTrace(); - }) - // 发起 GET 请求 - .get(); - - Thread.sleep(150); - - System.out.println("是否完成: " + call.isDone()); - System.out.println("是否取消: " + call.isCanceled()); - - call.cancel(); // 取消请求 - - System.out.println("是否取消: " + call.isCanceled()); - System.out.println("执行结果: " + call.getResult()); - System.out.println("是否完成: " + call.isDone()); -// - Thread.sleep(100); -// -// System.out.println("是否完成: " + call.isDone()); -// System.out.println("是否取消: " + call.isCanceled()); -// System.out.println("执行状态: " + call.getResult()); - - } - - - private static void sleep(int millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - - static void runOnUiThread(Runnable run) { - run.run(); - } - -} diff --git a/src/test/java/com/ejlchina/test/JsonTests.java b/src/test/java/com/ejlchina/test/JsonTests.java new file mode 100644 index 00000000..4e936e02 --- /dev/null +++ b/src/test/java/com/ejlchina/test/JsonTests.java @@ -0,0 +1,61 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.Process; +import org.junit.Test; + +import java.util.List; + +public class JsonTests extends BaseTest { + + @Test + public void syncJsonExample() { + HTTP http = HTTP.builder().build(); + // 同步请求 + HttpResult result = http.sync("/user/save") + .addJsonParam("name", "Tom") + .addJsonParam("age", 23) + .post(); + + println("result = " + result); + + result = http.sync("/user/show/1").get(); + + println("result = " + result); + + println("isSuccessful = " + result.isSuccessful()); + } + + + @Test + public void testToList() { + long t0 = System.currentTimeMillis(); + + HTTP http = HTTP.builder() + .baseUrl("http://xxx.cdyun.vip/ejlchina") + .build(); + + HttpResult result = http.sync("/comm/provinces") + .setRange(0) + .get(); + + println(t0, "status: " + result.getStatus()); + println(); + println(t0, "headers: " + result.getHeaders()); + println(); + HttpResult.Body body = result.getBody(); + + println(t0, "total: " + body.getContentLength()); + println(); + List list = body.setStepRate(0.1) + .setOnProcess((Process process) -> { + println(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate()); + }) + .toList(User.class); + + println(t0, list.toString()); + } + + +} diff --git a/src/test/java/com/ejlchina/test/ListenerTests.java b/src/test/java/com/ejlchina/test/ListenerTests.java new file mode 100644 index 00000000..5a0ad8ac --- /dev/null +++ b/src/test/java/com/ejlchina/test/ListenerTests.java @@ -0,0 +1,94 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.Download; +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpTask; +import com.ejlchina.okhttps.Process; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +public class ListenerTests extends BaseTest { + + @Test + public void testGlobalCallback() { + HTTP http = HTTP.builder() + .responseListener((HttpTask task, HttpResult result) -> { + println("全局 onResponse: " + result.getStatus()); + return true; + }) + .completeListener((HttpTask task, HttpResult.State state) -> { + println("全局 onComplete: " + state); + return true; + }) + .exceptionListener((HttpTask task, IOException error) -> { + println("全局 onException: " + error.getMessage()); + return false; + }) + .build(); + + http.async("http://www.baidu.com") + .setOnResponse((HttpResult result) -> { + println("单例 onResponse: " + result.getStatus()); + }) + .setOnComplete((HttpResult.State state) -> { + println("单例 onComplete: " + state); + }) + .setOnException((IOException error) -> { + println("单例 onException: " + error.getMessage()); + }) + .get(); + + sleep(2000); + } + + @Test + public void testDownloadListener() { + HTTP http = HTTP.builder() + .downloadListener((HttpTask task, Download download) -> { + println("URL = " + task.getUrl()); + println("TAG = " + task.getTag()); + Download.Ctrl ctrl = download.getCtrl(); + + new Thread(() -> { + sleep(3000); + ctrl.pause(); + sleep(3000); + ctrl.resume(); + sleep(3000); + ctrl.cancel(); + }).start(); + }) + .build(); + + String url = "https://download.cocos.com/CocosDashboard/v1.0.1/CocosDashboard-v1.0.1-win32-031816.exe"; + + long t0 = now(); + + + http.sync(url) + .setTag("ASD") + .get() + .getBody() + .setOnProcess((Process process) -> { + println(t0, process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate()); + }) + .setStepRate(0.01) + .toFolder("D:/download/") + .nextOnIO() + .setOnSuccess((File file) -> { + println(t0, "下载成功:" + file.getAbsolutePath()); + println(); + }) + .setOnFailure((Download.Failure failure) -> { + println(t0, "下载失败:" + failure.getDoneBytes() + ", path = " + failure.getFile().getAbsolutePath()); + println(); + }) + .start(); + + sleep(20000); + } + +} diff --git a/src/test/java/com/ejlchina/test/PreprocessorTests.java b/src/test/java/com/ejlchina/test/PreprocessorTests.java new file mode 100644 index 00000000..f20fe559 --- /dev/null +++ b/src/test/java/com/ejlchina/test/PreprocessorTests.java @@ -0,0 +1,60 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.Preprocessor; +import org.junit.Test; + +public class PreprocessorTests extends BaseTest { + + + @Test + public void testPreprocessor() { + + HTTP http = HTTP.builder() + .baseUrl("http://localhost:8080") + .addPreprocessor((Preprocessor.PreChain chain) -> { + println("并行预处理-开始"); +// new Thread(() -> { + sleep(2000); + println("并行预处理-结束"); + chain.proceed(); +// }).start(); + }) + .addSerialPreprocessor((Preprocessor.PreChain chain) -> { + println("串行预处理-开始"); + new Thread(() -> { + sleep(3000); + println("串行预处理-结束"); + chain.proceed(); + }).start(); + }) + .build(); + + new Thread(() -> { + println(http.sync("/user/show/1").get()); + }).start(); + + new Thread(() -> { + println(http.sync("/user/show/2").get()); + }).start(); + +// new Thread(() -> { +// http.async("/user/show/1") +// .setOnResponse((HttpResult result) -> { +// println(result); +// }) +// .get(); +// }).start(); +// +// new Thread(() -> { +// http.async("/user/show/2") +// .setOnResponse((HttpResult result) -> { +// println(result); +// }) +// .get(); +// }).start(); + + sleep(10000); + } + +} diff --git a/src/test/java/com/ejlchina/test/SimpleTests.java b/src/test/java/com/ejlchina/test/SimpleTests.java index da3b3472..4a6d8a34 100644 --- a/src/test/java/com/ejlchina/test/SimpleTests.java +++ b/src/test/java/com/ejlchina/test/SimpleTests.java @@ -7,7 +7,7 @@ import java.util.List; -public class SimpleTests { +public class SimpleTests extends BaseTest { HTTP http = HTTP.builder() @@ -77,33 +77,7 @@ public void testContentLength() { System.out.println("size = " + (size / 1024) + " KB"); } - void sleep(int millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } } -class User { - - private int id; - private String name; - - public void setId(int id) { - this.id = id; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public String toString() { - return "User [id=" + id + ", name=" + name + "]"; - } - -} \ No newline at end of file diff --git a/src/test/java/com/ejlchina/test/UploadTests.java b/src/test/java/com/ejlchina/test/UploadTests.java new file mode 100644 index 00000000..dc1728f5 --- /dev/null +++ b/src/test/java/com/ejlchina/test/UploadTests.java @@ -0,0 +1,36 @@ +package com.ejlchina.test; + +import com.ejlchina.okhttps.HTTP; +import com.ejlchina.okhttps.Process; +import org.junit.Test; + +public class UploadTests extends BaseTest { + + @Test + public void testUpload() { + String data = "0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz0123456789abcdefghijklmnopqrstuvwsyz"; + + HTTP http = HTTP.builder().build(); + + long t0 = System.currentTimeMillis(); + + String res = http.sync("http://localhost:8080/test/index") + .addBodyParam("data", data) + .addFileParam("file", "D:\\WorkSpace\\download\\CocosDashboard-v1.0.1-win32-031816.exe") + .setStepRate(0.01) + .setOnProcess((Process process) -> { + println(t0, "上传:" + process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate()); + }) + .post() + .getBody() + .setStepBytes(5) + .setOnProcess((Process process) -> { + println(t0, "下载:" + process.getDoneBytes() + "/" + process.getTotalBytes() + "\t" + process.getRate()); + }) + .toString(); + + println("响应:" + res); + } + + +} From 90696dcf6a7b412ecfacaedab1187339e22ff6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 18:26:16 +0800 Subject: [PATCH 86/94] Tests --- src/test/java/com/ejlchina/test/CancelTests.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/com/ejlchina/test/CancelTests.java b/src/test/java/com/ejlchina/test/CancelTests.java index ebd5aaa0..45ec2707 100644 --- a/src/test/java/com/ejlchina/test/CancelTests.java +++ b/src/test/java/com/ejlchina/test/CancelTests.java @@ -13,6 +13,16 @@ public class CancelTests extends BaseTest { + public void testCancelAll() { + HTTP http = HTTP.builder() + .baseUrl("http://localhost:8080") + .build(); + + + + + } + @Test public void testCancel() { From ee6d276c6b3a601e2c2797306b633caefbd8efc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 20:37:19 +0800 Subject: [PATCH 87/94] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=AF=B9=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E8=AF=B7=E6=B1=82=E8=BF=9B=E8=A1=8C=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E6=97=B6=EF=BC=8COkHttp=20=E5=BA=95=E5=B1=82=E6=8A=9B=E5=87=BA?= =?UTF-8?q?=20SocketException=20=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/HttpTask.java | 8 ++++++-- .../java/com/ejlchina/okhttps/internal/AsyncHttpTask.java | 2 +- .../java/com/ejlchina/okhttps/internal/SyncHttpTask.java | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HttpTask.java b/src/main/java/com/ejlchina/okhttps/HttpTask.java index 392484d5..ae8f96fd 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/HttpTask.java @@ -13,6 +13,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; +import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.HashMap; @@ -559,12 +560,15 @@ private void buildHeaders(Request.Builder builder) { } } - protected State toState(IOException e) { + protected State toState(IOException e, boolean sync) { if (e instanceof SocketTimeoutException) { return State.TIMEOUT; } else if (e instanceof UnknownHostException || e instanceof ConnectException) { return State.NETWORK_ERROR; - } else if ("Canceled".equals(e.getMessage())) { + } + String msg = e.getMessage(); + if (msg != null && ("Canceled".equals(msg) || e instanceof SocketException + && msg.startsWith("Socket operation on nonsocket"))) { return State.CANCELED; } return State.EXCEPTION; diff --git a/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java index 7969fc11..49be51ff 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/internal/AsyncHttpTask.java @@ -239,7 +239,7 @@ private HttpCall executeCall(Call call) { call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException error) { - State state = toState(error); + State state = toState(error, false); if (state == State.CANCELED) { httpCall.setResult(new RealHttpResult(AsyncHttpTask.this, state)); } else { diff --git a/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java b/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java index cbb9cdd4..4c3696dc 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/internal/SyncHttpTask.java @@ -98,7 +98,7 @@ private HttpResult request(String method) { SyncHttpTask.this.notify(); } } catch (IOException e) { - State state = toState(e); + State state = toState(e, true); synchronized (SyncHttpTask.this) { result.exception(state, e); SyncHttpTask.this.notify(); @@ -114,7 +114,7 @@ private HttpResult request(String method) { } } } - Exception e = result.getError(); + IOException e = result.getError(); if (e != null && result.getState() != State.CANCELED && !nothrow) { throw new HttpException("请求执行异常", e); From 94043a090bd88fb33e42ab71944f541be1653231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 20:51:35 +0800 Subject: [PATCH 88/94] V1.0.2 ^_^ --- .../java/com/ejlchina/test/CancelTests.java | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/ejlchina/test/CancelTests.java b/src/test/java/com/ejlchina/test/CancelTests.java index 45ec2707..e69dfa2b 100644 --- a/src/test/java/com/ejlchina/test/CancelTests.java +++ b/src/test/java/com/ejlchina/test/CancelTests.java @@ -2,6 +2,7 @@ import com.ejlchina.okhttps.HTTP; import com.ejlchina.okhttps.HttpResult; +import com.ejlchina.okhttps.HttpResult.State; import com.ejlchina.okhttps.internal.HttpClient; import org.junit.Test; @@ -13,14 +14,39 @@ public class CancelTests extends BaseTest { - public void testCancelAll() { + @Test + public void testCancelByTag() { HTTP http = HTTP.builder() - .baseUrl("http://localhost:8080") + .baseUrl("http://tst-api-mini.cdyun.vip") .build(); - - - + new Thread(() -> { + sleep(50); +// http.cancelAll(); +// int count = http.cancel("A"); +// println("count = " + count); + }).start(); + + http.async("/ejlchina/comm/provinces") + .setTag("A") + .setOnResponse((HttpResult result) -> { + println("异步:result = " + result); + }) + .setOnComplete((State state) -> { + println("异步:state = " + state); + }) + .get(); + + try { + HttpResult result = http.sync("/ejlchina/comm/provinces") + .setTag("A") + .get(); + + println("同步:result = " + result); + } catch (Exception e) { + e.printStackTrace(); + } + sleep(5000); } @Test From aa10838a97df402636a0845cc866e007872ec2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Thu, 9 Apr 2020 21:04:48 +0800 Subject: [PATCH 89/94] V1.0.2 ^_^ --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 98ca90d1..31067378 100644 --- a/README.md +++ b/README.md @@ -87,11 +87,12 @@ ```java HTTP http = HTTP.builder().build(); ``` -  以上代码构建了一个最简单的`HTTP`实例,它拥有以下三个方法: +  以上代码构建了一个最简单的`HTTP`实例,它拥有以下方法: * `async(String url)` 开始一个异步HTTP任务 * `sync(String url)` 开始一个同步HTTP任务 * `cancel(String tag)` 根据标签批量取消HTTP任务 +* `cancelAll()` 取消所有任务(since v1.0.2)   为了使用方便,在构建的时候,我们更愿意指定一个`BaseUrl`(请参见[5.1 设置 BaseUrl](#51-设置-baseurl)): @@ -323,7 +324,7 @@ http.async("/users") //(5) int count = http.cancel("B"); //(2)(3)(4)被取消(取消标签包含"B"的任务) System.out.println(count); // 输出 3 ``` -  同样的,只有异步HTTP任务才可以被取消。标签除了可以用来取消任务,在预处理器中它也可以发挥作用,请参见[并行预处理器](#54-并行预处理器)与[串行预处理器](#55-串行预处理器)。 +  标签除了可以用来取消任务,在预处理器中它也可以发挥作用,请参见[并行预处理器](#54-并行预处理器)与[串行预处理器](#55-串行预处理器)。 ### 6 配置 HTTP From 261c2e8c3585e5934b2c192a0dd8bf413545fdee Mon Sep 17 00:00:00 2001 From: ejlchina Date: Fri, 10 Apr 2020 00:21:48 +0800 Subject: [PATCH 90/94] V1.0.2 ^_^ --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 31067378..d4e29534 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ * 单方法回调,充分利用 Lambda 表达式 * TCP连接池、Http2 -> OkHttps 非常轻量,自身代码在 2000 行左右,且有较高的编码质量,sonarqube 分析结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) +> OkHttps 非常轻量,自身代码仅 2000 行左右,且有较高的编码质量,sonarqube 分析结果为 3A,[查看报告](https://gitee.com/ejlchina-zhxu/okhttps/quality_analyses?platform=sonar_qube) ### 前世:[HttpUtils, V2.3.0 之后重命名为 OkHttps](https://gitee.com/ejlchina-zhxu/httputils) ### 当前文档版本[1.0.1] From 0956cec8e7ee74f69c1643f7cedf38d5c522b745 Mon Sep 17 00:00:00 2001 From: richenli <719583022@qq.com> Date: Fri, 10 Apr 2020 10:47:32 +0800 Subject: [PATCH 91/94] =?UTF-8?q?1.=E5=85=81=E8=AE=B8sync()=E5=92=8Casync(?= =?UTF-8?q?)=E7=A9=BA=E5=8F=82=E4=BC=A0=E5=85=A5=20=E5=8E=9F=E5=9B=A0:?= =?UTF-8?q?=E9=83=A8=E5=88=86=E9=A1=B9=E7=9B=AE=E7=9A=84=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=94=BE=E5=9C=A8json=E5=BD=93=E4=B8=AD?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E4=BB=A5url=E4=B8=BA=E4=B8=BB=202.=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E8=B6=85=E6=97=B6=E6=97=B6=E9=97=B4=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=20HTTP=20http=20=3D=20HTTP.builder().builderTimeOut(3,3,=20Tim?= =?UTF-8?q?eUnit.SECONDS).build();=203.=E4=BC=98=E5=8C=96HttpTask=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/HTTP.java | 13 ++++ .../java/com/ejlchina/okhttps/HttpTask.java | 59 ++++++++----------- .../java/com/ejlchina/okhttps/HttpUtils.java | 16 +++++ .../ejlchina/okhttps/internal/HttpClient.java | 38 +++++++++++- 4 files changed, 91 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HTTP.java b/src/main/java/com/ejlchina/okhttps/HTTP.java index 5252ff53..22232f1e 100644 --- a/src/main/java/com/ejlchina/okhttps/HTTP.java +++ b/src/main/java/com/ejlchina/okhttps/HTTP.java @@ -24,12 +24,25 @@ public interface HTTP { */ AsyncHttpTask async(String url); + /** + * 异步请求 + * @param url 请求地址 + * @return 异步HTTP任务 + */ + AsyncHttpTask async(); + /** * 同步请求 * @param url 请求地址 * @return 同步HTTP任务 */ SyncHttpTask sync(String url); + + /** + * 同步请求 + * @return 同步HTTP任务 + */ + SyncHttpTask sync(); /** * 根据标签取消HTTP任务,只要任务的标签包含指定的Tag就会被取消 diff --git a/src/main/java/com/ejlchina/okhttps/HttpTask.java b/src/main/java/com/ejlchina/okhttps/HttpTask.java index ae8f96fd..4d6038d7 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/HttpTask.java @@ -18,6 +18,7 @@ import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; /** @@ -33,7 +34,6 @@ public abstract class HttpTask> { protected boolean nothrow; protected String tag; protected boolean nextOnIO = false; - private String urlPath; private Map headers; private Map pathParams; @@ -246,19 +246,10 @@ public C addPathParam(String name, Object value) { * @return HttpTask 实例 **/ public C addPathParam(Map params) { - if (params != null) { - if (pathParams == null) { - pathParams = new HashMap<>(); - } - params.forEach((String name, Object value) -> { - if (name != null && value != null) { - pathParams.put(name, value.toString()); - } - }); - } + addParams(params,this.pathParams); return (C) this; } - + /** * URL参数:拼接在URL后的参数 * @param name 参数名 @@ -281,16 +272,7 @@ public C addUrlParam(String name, Object value) { * @return HttpTask 实例 **/ public C addUrlParam(Map params) { - if (params != null) { - if (urlParams == null) { - urlParams = new HashMap<>(); - } - params.forEach((String name, Object value) -> { - if (name != null && value != null) { - urlParams.put(name, value.toString()); - } - }); - } + addParams(params,this.urlParams); return (C) this; } @@ -316,19 +298,26 @@ public C addBodyParam(String name, Object value) { * @return HttpTask 实例 **/ public C addBodyParam(Map params) { - if (params != null) { - if (bodyParams == null) { - bodyParams = new HashMap<>(); + addParams(params,this.bodyParams); + return (C) this; + } + private void addParams(Map params,Map addParam){ + if (params != null) { + if (addParam == null) { + addParam = new HashMap<>(); + } +// params.forEach((String name, Object value) -> { +// if (name != null && value != null) { +// addParam.put(name, value.toString()); +// } +// }); + for (Map.Entry stringEntry : params.entrySet()) { + if (stringEntry.getKey() != null && stringEntry.getValue() != null) { + addParam.put(stringEntry.getKey(), stringEntry.getValue().toString()); + } } - params.forEach((String name, Object value) -> { - if (name != null && value != null) { - bodyParams.put(name, value.toString()); - } - }); } - return (C) this; } - /** * Json参数:请求体为Json,支持多层结构 * @param name JSON键名 @@ -504,6 +493,10 @@ class FilePara { } protected Call prepareCall(String method) { +// OkHttpClient client = new OkHttpClient.Builder() +// .connectTimeout(connectTimeout,timeUnit) // 设置连接超时时间 +// .readTimeout(readTimeout,timeUnit) // 设置读取超时时间 +// .build(); assertNotConflict("GET".equals(method)); Request.Builder builder = new Request.Builder() .url(buildUrlPath()); @@ -526,7 +519,7 @@ protected Call prepareCall(String method) { } switch (method) { case "GET": - builder.get(); + builder.get().build(); break; case "POST": builder.post(reqBody); diff --git a/src/main/java/com/ejlchina/okhttps/HttpUtils.java b/src/main/java/com/ejlchina/okhttps/HttpUtils.java index a03e365f..e2a8e0af 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpUtils.java +++ b/src/main/java/com/ejlchina/okhttps/HttpUtils.java @@ -59,6 +59,14 @@ public static AsyncHttpTask async(String url) { return getHttp().async(url); } + /** + * 异步请求 + * @return 异步 HttpTask + */ + public static AsyncHttpTask async() { + return getHttp().async(); + } + /** * 同步请求 * @param url 请求地址 @@ -68,6 +76,14 @@ public static SyncHttpTask sync(String url) { return getHttp().sync(url); } + /** + * 同步请求 + * @return 同步 HttpTask + */ + public static SyncHttpTask sync() { + return getHttp().sync(); + } + /** * 根据标签取消HTTP任务,只要任务的标签包含指定的Tag就会被取消 * @param tag 标签 diff --git a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java index 15e69497..5a1ad119 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java +++ b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java @@ -10,6 +10,7 @@ import java.util.Map; import java.util.Queue; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import com.ejlchina.okhttps.*; import com.ejlchina.okhttps.HttpResult.State; @@ -58,7 +59,17 @@ public AsyncHttpTask async(String url) { public SyncHttpTask sync(String url) { return new SyncHttpTask(this, urlPath(url)); } - + + @Override + public AsyncHttpTask async() { + return new AsyncHttpTask(this, urlPath()); + } + + @Override + public SyncHttpTask sync() { + return new SyncHttpTask(this, urlPath()); + } + @Override public int cancel(String tag) { if (tag == null) { @@ -86,6 +97,7 @@ public void cancelAll() { @Override public Call request(Request request) { + return client.newCall(request); } @@ -269,6 +281,13 @@ private String urlPath(String urlPath) { } throw new HttpException("在设置 BaseUrl 之前,您必须使用全路径URL发起请求,当前URL为:" + urlPath); } + + private String urlPath() { + if (baseUrl != null) { + return baseUrl; + } + throw new HttpException("在设置 BaseUrl 之前,您必须使用全路径URL发起请求,当前URL为:" + baseUrl); + } public static class Builder { @@ -430,7 +449,22 @@ public Builder downloadListener(DownListener listener) { this.downloadListener = listener; return this; } - + + /** + * 设置超时时间 + * @param connectTimeout 连接时间 + * @param readTimeout 读取时间 + * @param timeUnit + * @return + */ + public Builder builderTimeOut(long connectTimeout, long readTimeout, TimeUnit timeUnit) { + this.client= new OkHttpClient.Builder() + .connectTimeout(connectTimeout,timeUnit) // 设置连接超时时间 + .readTimeout(readTimeout,timeUnit) // 设置读取超时时间 + .build(); + return this; + } + public HTTP build() { if (configurator != null || client == null) { OkHttpClient.Builder builder = new OkHttpClient.Builder(); From 82eb2ea1c97f1033916a7b5b10bb57c9d4207018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Fri, 10 Apr 2020 11:42:47 +0800 Subject: [PATCH 92/94] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=A4=E6=96=AD=20Ca?= =?UTF-8?q?ncel=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/HttpTask.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ejlchina/okhttps/HttpTask.java b/src/main/java/com/ejlchina/okhttps/HttpTask.java index ae8f96fd..51d69a98 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/HttpTask.java @@ -567,7 +567,8 @@ protected State toState(IOException e, boolean sync) { return State.NETWORK_ERROR; } String msg = e.getMessage(); - if (msg != null && ("Canceled".equals(msg) || e instanceof SocketException + if (msg != null && ("Canceled".equals(msg) + || sync && e instanceof SocketException && msg.startsWith("Socket operation on nonsocket"))) { return State.CANCELED; } From f63c0ac50db00a665703a9852594701dd3e813a5 Mon Sep 17 00:00:00 2001 From: richenli <719583022@qq.com> Date: Fri, 10 Apr 2020 12:20:25 +0800 Subject: [PATCH 93/94] =?UTF-8?q?1.=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E9=97=AE=E9=A2=98=E4=BB=A5=E5=8F=8A=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/ejlchina/okhttps/HTTP.java | 82 +- .../java/com/ejlchina/okhttps/HttpTask.java | 511 ++++++----- .../ejlchina/okhttps/internal/HttpClient.java | 852 +++++++++--------- 3 files changed, 743 insertions(+), 702 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HTTP.java b/src/main/java/com/ejlchina/okhttps/HTTP.java index 22232f1e..acb1fbd2 100644 --- a/src/main/java/com/ejlchina/okhttps/HTTP.java +++ b/src/main/java/com/ejlchina/okhttps/HTTP.java @@ -12,79 +12,87 @@ /** * HTTP 客户端接口 - * @author 15735 * + * @author 15735 */ public interface HTTP { - /** - * 异步请求 - * @param url 请求地址 - * @return 异步HTTP任务 - */ + /** + * 异步请求 + * + * @param url 请求地址 + * @return 异步HTTP任务 + */ AsyncHttpTask async(String url); - /** - * 异步请求 - * @param url 请求地址 - * @return 异步HTTP任务 - */ - AsyncHttpTask async(); + /** + * 异步请求 + * + * @return 异步HTTP任务 + */ + AsyncHttpTask async(); - /** - * 同步请求 - * @param url 请求地址 - * @return 同步HTTP任务 - */ + /** + * 同步请求 + * + * @param url 请求地址 + * @return 同步HTTP任务 + */ SyncHttpTask sync(String url); - /** - * 同步请求 - * @return 同步HTTP任务 - */ - SyncHttpTask sync(); - + /** + * 同步请求 + * + * @return 同步HTTP任务 + */ + SyncHttpTask sync(); + /** * 根据标签取消HTTP任务,只要任务的标签包含指定的Tag就会被取消 - * 从 v1.0.2 开始支持取消同步请求 + * 从 v1.0.2 开始支持取消同步请求 + * * @param tag 标签 * @return 被取消的任务数量 */ int cancel(String tag); - /** - * @since 1.0.2 - * 取消所有HTTP任务,包括同步和异步 - */ - void cancelAll(); + /** + * @since 1.0.2 + * 取消所有HTTP任务,包括同步和异步 + */ + void cancelAll(); /** * OkHttp 原生请求 (该请求不经过 预处理器) + * * @param request 请求 * @return Call */ Call request(Request request); - + /** * Websocket(该请求不经过 预处理器) - * @param request 请求 + * + * @param request 请求 * @param listener 监听器 * @return */ WebSocket webSocket(Request request, WebSocketListener listener); - + /** * 获取任务执行器 + * * @return TaskExecutor */ TaskExecutor getExecutor(); - + /** * HTTP 构建器 + * * @return HTTP 构建器 */ - static Builder builder() { - return new Builder(); - } - + static Builder builder() { + return new Builder(); + } + } diff --git a/src/main/java/com/ejlchina/okhttps/HttpTask.java b/src/main/java/com/ejlchina/okhttps/HttpTask.java index 4d6038d7..002fe663 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/HttpTask.java @@ -44,273 +44,297 @@ public abstract class HttpTask> { private String requestJson; private OnCallback onProcess; private boolean pOnIO; - private long stepBytes = 0; - private double stepRate = -1; - - private Object object; - - + private long stepBytes = 0; + private double stepRate = -1; + + private Object object; + + public HttpTask(HttpClient httpClient, String url) { - this.httpClient = httpClient; - this.urlPath = url; + this.httpClient = httpClient; + this.urlPath = url; } /** * 获取请求任务的URL地址 + * * @return URL地址 */ public String getUrl() { - return urlPath; + return urlPath; } - + /** * 获取请求任务的标签 + * * @return 标签 */ public String getTag() { - return tag; - } - + return tag; + } + /** * 标签匹配 * 判断任务标签与指定的标签是否匹配(包含指定的标签) + * * @param tag 标签 * @return 是否匹配 */ public boolean isTagged(String tag) { - if (this.tag != null && tag != null) { - return this.tag.contains(tag); - } - return false; + if (this.tag != null && tag != null) { + return this.tag.contains(tag); + } + return false; } - + /** * 获取请求任务的头信息 + * * @return 头信息 */ public Map getHeaders() { - return headers; - } - + return headers; + } + /** * 获得被绑定的对象 + * * @return Object */ public Object getBound() { - return object; + return object; } - /** + /** * 设置在发生异常时不向上抛出,设置后: * 异步请求可以在异常回调内捕获异常,同步请求在返回结果中找到该异常 + * * @return HttpTask 实例 */ public C nothrow() { - this.nothrow = true; - return (C) this; + this.nothrow = true; + return (C) this; } - + /** * 为请求任务添加标签 + * * @param tag 标签 * @return HttpTask 实例 */ public C setTag(String tag) { - this.tag = tag; - return (C) this; + this.tag = tag; + return (C) this; } /** * 下一个回调在IO线程执行 + * * @return HttpTask 实例 */ public C nextOnIO() { - nextOnIO = true; - return (C) this; + nextOnIO = true; + return (C) this; } - + /** * 绑定一个对象 + * * @return HttpTask 实例 */ public C bind(Object object) { - this.object = object; - return (C) this; + this.object = object; + return (C) this; } - - /** + + /** * 添加请求头 - * @param name 请求头名 + * + * @param name 请求头名 * @param value 请求头值 * @return HttpTask 实例 */ - public C addHeader(String name, String value) { - if (name != null && value != null) { - if (headers == null) { + public C addHeader(String name, String value) { + if (name != null && value != null) { + if (headers == null) { headers = new HashMap<>(); } headers.put(name, value); - } + } return (C) this; } /** * 添加请求头 + * * @param headers 请求头集合 * @return HttpTask 实例 */ public C addHeader(Map headers) { - if (headers != null) { - if (this.headers == null) { - this.headers = new HashMap<>(); + if (headers != null) { + if (this.headers == null) { + this.headers = new HashMap<>(); } this.headers.putAll(headers); - } + } return (C) this; } - + /** * 设置Range头信息 * 表示接收报文体时跳过的字节数,用于断点续传 + * * @param rangeStart 表示从 rangeStart 个字节处开始接收,通常是已经下载的字节数,即上次的断点) * @return HttpTask 实例 */ public C setRange(long rangeStart) { - return addHeader("Range", "bytes=" + rangeStart + "-"); + return addHeader("Range", "bytes=" + rangeStart + "-"); } - + /** * 设置Range头信息 * 设置接收报文体时接收的范围,用于分块下载 + * * @param rangeStart 表示从 rangeStart 个字节处开始接收 - * @param rangeEnd 表示接收到 rangeEnd 个字节处 + * @param rangeEnd 表示接收到 rangeEnd 个字节处 * @return HttpTask 实例 */ public C setRange(long rangeStart, long rangeEnd) { - return addHeader("Range", "bytes=" + rangeStart + "-" + rangeEnd); - } - - /** - * 设置报文体发送进度回调 - * @param onProcess 进度回调函数 - * @return HttpTask 实例 - */ - public C setOnProcess(OnCallback onProcess) { - this.onProcess = onProcess; - pOnIO = nextOnIO; - nextOnIO = false; - return (C) this; - } - - /** - * 设置进度回调的步进字节,默认 8K(8192) - * 表示每接收 stepBytes 个字节,执行一次进度回调 - * @param stepBytes 步进字节 - * @return HttpTask 实例 - */ - public C setStepBytes(long stepBytes) { - this.stepBytes = stepBytes; - return (C) this; - } - - /** - * 设置进度回调的步进比例 - * 表示每接收 stepRate 比例,执行一次进度回调 - * @param stepRate 步进比例 - * @return HttpTask 实例 - */ - public C setStepRate(double stepRate) { - this.stepRate = stepRate; - return (C) this; - } - + return addHeader("Range", "bytes=" + rangeStart + "-" + rangeEnd); + } + + /** + * 设置报文体发送进度回调 + * + * @param onProcess 进度回调函数 + * @return HttpTask 实例 + */ + public C setOnProcess(OnCallback onProcess) { + this.onProcess = onProcess; + pOnIO = nextOnIO; + nextOnIO = false; + return (C) this; + } + + /** + * 设置进度回调的步进字节,默认 8K(8192) + * 表示每接收 stepBytes 个字节,执行一次进度回调 + * + * @param stepBytes 步进字节 + * @return HttpTask 实例 + */ + public C setStepBytes(long stepBytes) { + this.stepBytes = stepBytes; + return (C) this; + } + + /** + * 设置进度回调的步进比例 + * 表示每接收 stepRate 比例,执行一次进度回调 + * + * @param stepRate 步进比例 + * @return HttpTask 实例 + */ + public C setStepRate(double stepRate) { + this.stepRate = stepRate; + return (C) this; + } + /** * 路径参数:替换URL里的{name} - * @param name 参数名 + * + * @param name 参数名 * @param value 参数值 * @return HttpTask 实例 **/ public C addPathParam(String name, Object value) { - if (name != null && value != null) { - if (pathParams == null) { - pathParams = new HashMap<>(); - } - pathParams.put(name, value.toString()); - } + if (name != null && value != null) { + if (pathParams == null) { + pathParams = new HashMap<>(); + } + pathParams.put(name, value.toString()); + } return (C) this; } /** * 路径参数:替换URL里的{name} + * * @param params 参数集合 * @return HttpTask 实例 **/ public C addPathParam(Map params) { - addParams(params,this.pathParams); + addParams(params, this.pathParams); return (C) this; } /** * URL参数:拼接在URL后的参数 - * @param name 参数名 + * + * @param name 参数名 * @param value 参数值 * @return HttpTask 实例 **/ public C addUrlParam(String name, Object value) { - if (name != null && value != null) { - if (urlParams == null) { - urlParams = new HashMap<>(); - } - urlParams.put(name, value.toString()); - } + if (name != null && value != null) { + if (urlParams == null) { + urlParams = new HashMap<>(); + } + urlParams.put(name, value.toString()); + } return (C) this; } /** * URL参数:拼接在URL后的参数 + * * @param params 参数集合 * @return HttpTask 实例 **/ public C addUrlParam(Map params) { - addParams(params,this.urlParams); + addParams(params, this.urlParams); return (C) this; } /** * Body参数:放在Body里的参数 - * @param name 参数名 + * + * @param name 参数名 * @param value 参数值 * @return HttpTask 实例 **/ public C addBodyParam(String name, Object value) { - if (name != null && value != null) { - if (bodyParams == null) { - bodyParams = new HashMap<>(); - } - bodyParams.put(name, value.toString()); - } + if (name != null && value != null) { + if (bodyParams == null) { + bodyParams = new HashMap<>(); + } + bodyParams.put(name, value.toString()); + } return (C) this; } /** * Body参数:放在Body里的参数 + * * @param params 参数集合 * @return HttpTask 实例 **/ public C addBodyParam(Map params) { - addParams(params,this.bodyParams); + addParams(params, this.bodyParams); return (C) this; } - private void addParams(Map params,Map addParam){ + + /** + * 批量添加参数 + * + * @param params + * @param addParam + */ + private void addParams(Map params, Map addParam) { if (params != null) { if (addParam == null) { addParam = new HashMap<>(); } -// params.forEach((String name, Object value) -> { -// if (name != null && value != null) { -// addParam.put(name, value.toString()); -// } -// }); for (Map.Entry stringEntry : params.entrySet()) { if (stringEntry.getKey() != null && stringEntry.getValue() != null) { addParam.put(stringEntry.getKey(), stringEntry.getValue().toString()); @@ -318,32 +342,35 @@ private void addParams(Map params,Map addParam){ } } } + /** * Json参数:请求体为Json,支持多层结构 - * @param name JSON键名 + * + * @param name JSON键名 * @param value JSON键值 * @return HttpTask 实例 */ public C addJsonParam(String name, Object value) { - if (name != null && value != null) { - if (jsonParams == null) { - jsonParams = new HashMap<>(); - } - jsonParams.put(name, value); - } + if (name != null && value != null) { + if (jsonParams == null) { + jsonParams = new HashMap<>(); + } + jsonParams.put(name, value); + } return (C) this; } /** * Json参数:请求体为Json,只支持单层Json * 若请求json为多层结构,请使用setRequestJson方法 + * * @param params JSON键值集合 * @return HttpTask 实例 */ public C addJsonParam(Map params) { - if (params != null) { + if (params != null) { if (jsonParams == null) { - jsonParams = new HashMap<>(); + jsonParams = new HashMap<>(); } jsonParams.putAll(params); } @@ -352,6 +379,7 @@ public C addJsonParam(Map params) { /** * 设置 json 请求体 + * * @param json JSON字符串 或 Java对象(将依据 对象的get方法序列化为 json 字符串) * @return HttpTask 实例 **/ @@ -361,26 +389,28 @@ public C setRequestJson(Object json) { /** * 请求体为json - * @param json JSON字符串 或 Java对象,将跟换 bean的get方法序列化程 json 字符串 + * + * @param json JSON字符串 或 Java对象,将跟换 bean的get方法序列化程 json 字符串 * @param dateFormat 序列化json时对日期类型字段的处理格式 * @return HttpTask 实例 **/ public C setRequestJson(Object json, String dateFormat) { if (json != null) { - if (json instanceof String) { - requestJson = json.toString(); - } else if (dateFormat != null) { - requestJson = JSON.toJSONStringWithDateFormat(json, dateFormat); - } else { - requestJson = JSON.toJSONString(json); - } + if (json instanceof String) { + requestJson = json.toString(); + } else if (dateFormat != null) { + requestJson = JSON.toJSONStringWithDateFormat(json, dateFormat); + } else { + requestJson = JSON.toJSONString(json); + } } return (C) this; } - + /** * 添加文件参数 - * @param name 参数名 + * + * @param name 参数名 * @param filePath 文件路径 * @return HttpTask 实例 */ @@ -390,6 +420,7 @@ public C addFileParam(String name, String filePath) { /** * 添加文件参数 + * * @param name 参数名 * @param file 文件 * @return HttpTask 实例 @@ -408,57 +439,61 @@ public C addFileParam(String name, File file) { /** * 添加文件参数 - * @param name 参数名 - * @param type 文件类型: 如 png、jpg、jpeg 等 + * + * @param name 参数名 + * @param type 文件类型: 如 png、jpg、jpeg 等 * @param inputStream 文件输入流 * @return HttpTask 实例 */ public C addFileParam(String name, String type, InputStream inputStream) { - return addFileParam(name, type, null, inputStream); + return addFileParam(name, type, null, inputStream); } - + /** * 添加文件参数 - * @param name 参数名 - * @param type 文件类型: 如 png、jpg、jpeg 等 + * + * @param name 参数名 + * @param type 文件类型: 如 png、jpg、jpeg 等 * @param fileName 文件名 - * @param input 文件输入流 + * @param input 文件输入流 * @return HttpTask 实例 */ public C addFileParam(String name, String type, String fileName, InputStream input) { if (name != null && input != null) { byte[] content = null; - try { - Buffer buffer = new Buffer(); - content = buffer.readFrom(input).readByteArray(); - buffer.close(); - } catch (IOException e) { - throw new HttpException("读取文件输入流出错:", e); - } finally { - Util.closeQuietly(input); - } + try { + Buffer buffer = new Buffer(); + content = buffer.readFrom(input).readByteArray(); + buffer.close(); + } catch (IOException e) { + throw new HttpException("读取文件输入流出错:", e); + } finally { + Util.closeQuietly(input); + } addFileParam(name, type, fileName, content); } return (C) this; } - + /** * 添加文件参数 - * @param name 参数名 - * @param type 文件类型: 如 png、jpg、jpeg 等 + * + * @param name 参数名 + * @param type 文件类型: 如 png、jpg、jpeg 等 * @param content 文件内容 * @return HttpTask 实例 */ public C addFileParam(String name, String type, byte[] content) { - return addFileParam(name, type, null, content); + return addFileParam(name, type, null, content); } - + /** * 添加文件参数 - * @param name 参数名 - * @param type 文件类型: 如 png、jpg、jpeg 等 + * + * @param name 参数名 + * @param type 文件类型: 如 png、jpg、jpeg 等 * @param fileName 文件名 - * @param content 文件内容 + * @param content 文件内容 * @return HttpTask 实例 */ public C addFileParam(String name, String type, String fileName, byte[] content) { @@ -472,75 +507,75 @@ public C addFileParam(String name, String type, String fileName, byte[] content) } class FilePara { - - String type; - String fileName; - byte[] content; - File file; - - FilePara(String type, String fileName, byte[] content) { - this.type = type; - this.fileName = fileName; - this.content = content; - } - - FilePara(String type, String fileName, File file) { - this.type = type; - this.fileName = fileName; - this.file = file; - } - - } - + + String type; + String fileName; + byte[] content; + File file; + + FilePara(String type, String fileName, byte[] content) { + this.type = type; + this.fileName = fileName; + this.content = content; + } + + FilePara(String type, String fileName, File file) { + this.type = type; + this.fileName = fileName; + this.file = file; + } + + } + protected Call prepareCall(String method) { // OkHttpClient client = new OkHttpClient.Builder() // .connectTimeout(connectTimeout,timeUnit) // 设置连接超时时间 // .readTimeout(readTimeout,timeUnit) // 设置读取超时时间 // .build(); - assertNotConflict("GET".equals(method)); + assertNotConflict("GET".equals(method)); Request.Builder builder = new Request.Builder() - .url(buildUrlPath()); + .url(buildUrlPath()); buildHeaders(builder); - RequestBody reqBody = null; - if (!"GET".equals(method)) { - reqBody = buildRequestBody(); - if (onProcess != null) { - long contentLength = contentLength(reqBody); - if (stepRate > 0 && stepRate <= 1) { - stepBytes = (long) (contentLength * stepRate); - } - if (stepBytes <= 0) { - stepBytes = Process.DEFAULT_STEP_BYTES; - } - reqBody = new ProcessRequestBody(reqBody, onProcess, - httpClient.getExecutor().getExecutor(pOnIO), - contentLength, stepBytes); - } - } - switch (method) { - case "GET": - builder.get().build(); - break; - case "POST": - builder.post(reqBody); - break; - case "PUT": - builder.put(reqBody); - break; - case "DELETE": - builder.delete(reqBody); - break; + RequestBody reqBody = null; + if (!"GET".equals(method)) { + reqBody = buildRequestBody(); + if (onProcess != null) { + long contentLength = contentLength(reqBody); + if (stepRate > 0 && stepRate <= 1) { + stepBytes = (long) (contentLength * stepRate); + } + if (stepBytes <= 0) { + stepBytes = Process.DEFAULT_STEP_BYTES; + } + reqBody = new ProcessRequestBody(reqBody, onProcess, + httpClient.getExecutor().getExecutor(pOnIO), + contentLength, stepBytes); + } + } + switch (method) { + case "GET": + builder.get().build(); + break; + case "POST": + builder.post(reqBody); + break; + case "PUT": + builder.put(reqBody); + break; + case "DELETE": + builder.delete(reqBody); + break; } - return httpClient.request(builder.build()); - } + return httpClient.request(builder.build()); + } - private long contentLength(RequestBody reqBody) { - try { - return reqBody.contentLength(); - } catch (IOException e) { - throw new HttpException("无法获取请求体长度", e); - } - } + private long contentLength(RequestBody reqBody) { + try { + return reqBody.contentLength(); + } catch (IOException e) { + throw new HttpException("无法获取请求体长度", e); + } + } private void buildHeaders(Request.Builder builder) { if (headers != null) { @@ -553,19 +588,19 @@ private void buildHeaders(Request.Builder builder) { } } - protected State toState(IOException e, boolean sync) { - if (e instanceof SocketTimeoutException) { - return State.TIMEOUT; - } else if (e instanceof UnknownHostException || e instanceof ConnectException) { - return State.NETWORK_ERROR; - } - String msg = e.getMessage(); - if (msg != null && ("Canceled".equals(msg) || e instanceof SocketException + protected State toState(IOException e, boolean sync) { + if (e instanceof SocketTimeoutException) { + return State.TIMEOUT; + } else if (e instanceof UnknownHostException || e instanceof ConnectException) { + return State.NETWORK_ERROR; + } + String msg = e.getMessage(); + if (msg != null && ("Canceled".equals(msg) || e instanceof SocketException && msg.startsWith("Socket operation on nonsocket"))) { - return State.CANCELED; - } - return State.EXCEPTION; - } + return State.CANCELED; + } + return State.EXCEPTION; + } private RequestBody buildRequestBody() { if (jsonParams != null) { @@ -584,11 +619,11 @@ private RequestBody buildRequestBody() { MediaType type = httpClient.getMediaType(file.type); RequestBody bodyPart; if (file.file != null) { - bodyPart = RequestBody.create(type, file.file); + bodyPart = RequestBody.create(type, file.file); } else { - bodyPart = RequestBody.create(type, file.content); + bodyPart = RequestBody.create(type, file.content); } - builder.addFormDataPart(name, file.fileName, bodyPart); + builder.addFormDataPart(name, file.fileName, bodyPart); } return builder.build(); } else if (requestJson != null) { @@ -596,17 +631,17 @@ private RequestBody buildRequestBody() { } else { FormBody.Builder builder = new FormBody.Builder(); if (bodyParams != null) { - for (String name : bodyParams.keySet()) { - String value = bodyParams.get(name); - builder.add(name, value); - } + for (String name : bodyParams.keySet()) { + String value = bodyParams.get(name); + builder.add(name, value); + } } return builder.build(); } } private String buildUrlPath() { - String url = urlPath; + String url = urlPath; if (url == null || url.trim().isEmpty()) { throw new HttpException("url 不能为空!"); } diff --git a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java index 5a1ad119..62d48d69 100644 --- a/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java +++ b/src/main/java/com/ejlchina/okhttps/internal/HttpClient.java @@ -24,130 +24,130 @@ public class HttpClient implements HTTP { - // OkHttpClient - final OkHttpClient client; - // 根URL - final String baseUrl; - // 媒体类型 - final Map mediaTypes; - // 执行器 - final TaskExecutor executor; - // 预处理器 - final Preprocessor[] preprocessors; - // 持有标签的任务 - final List tagTasks; - - - private HttpClient(Builder builder) { - this.client = builder.client; - this.baseUrl = builder.baseUrl; - this.mediaTypes = builder.mediaTypes; - this.executor = new TaskExecutor(client.dispatcher().executorService(), - builder.mainExecutor, builder.downloadListener, - builder.responseListener, builder.exceptionListener, - builder.completeListener); - this.preprocessors = builder.preprocessors.toArray(new Preprocessor[0]); - this.tagTasks = Collections.synchronizedList(new LinkedList<>()); - } - - @Override + // OkHttpClient + final OkHttpClient client; + // 根URL + final String baseUrl; + // 媒体类型 + final Map mediaTypes; + // 执行器 + final TaskExecutor executor; + // 预处理器 + final Preprocessor[] preprocessors; + // 持有标签的任务 + final List tagTasks; + + + private HttpClient(Builder builder) { + this.client = builder.client; + this.baseUrl = builder.baseUrl; + this.mediaTypes = builder.mediaTypes; + this.executor = new TaskExecutor(client.dispatcher().executorService(), + builder.mainExecutor, builder.downloadListener, + builder.responseListener, builder.exceptionListener, + builder.completeListener); + this.preprocessors = builder.preprocessors.toArray(new Preprocessor[0]); + this.tagTasks = Collections.synchronizedList(new LinkedList<>()); + } + + @Override public AsyncHttpTask async(String url) { return new AsyncHttpTask(this, urlPath(url)); } - @Override + @Override public SyncHttpTask sync(String url) { return new SyncHttpTask(this, urlPath(url)); } - @Override - public AsyncHttpTask async() { - return new AsyncHttpTask(this, urlPath()); - } - - @Override - public SyncHttpTask sync() { - return new SyncHttpTask(this, urlPath()); - } - - @Override - public int cancel(String tag) { - if (tag == null) { - return 0; - } - int count = 0; - Iterator it = tagTasks.iterator(); - while (it.hasNext()) { - TagTask tagCall = it.next(); - // 只要任务的标签包含指定的Tag就会被取消 - if (tagCall.tag.contains(tag)) { - if (tagCall.canceler.cancel()) { - count++; - } - it.remove(); - } - } - return count; - } - - @Override - public void cancelAll() { - client.dispatcher().cancelAll(); - } - - @Override - public Call request(Request request) { - - return client.newCall(request); + @Override + public AsyncHttpTask async() { + return new AsyncHttpTask(this, urlPath()); + } + + @Override + public SyncHttpTask sync() { + return new SyncHttpTask(this, urlPath()); + } + + @Override + public int cancel(String tag) { + if (tag == null) { + return 0; + } + int count = 0; + Iterator it = tagTasks.iterator(); + while (it.hasNext()) { + TagTask tagCall = it.next(); + // 只要任务的标签包含指定的Tag就会被取消 + if (tagCall.tag.contains(tag)) { + if (tagCall.canceler.cancel()) { + count++; + } + it.remove(); + } + } + return count; + } + + @Override + public void cancelAll() { + client.dispatcher().cancelAll(); + } + + @Override + public Call request(Request request) { + + return client.newCall(request); } - - @Override - public WebSocket webSocket(Request request, WebSocketListener listener) { - return client.newWebSocket(request, listener); - } - - public OkHttpClient getOkClient() { - return client; - } - - public int totalTimeoutMillis() { - return client.connectTimeoutMillis() + client.writeTimeoutMillis() + client.readTimeoutMillis(); - } - - public int getTagTaskCount() { - return tagTasks.size(); - } - - public void addTagTask(String tag, Cancelable canceler, HttpTask task) { - tagTasks.add(new TagTask(tag, canceler, task)); - } - - public void removeTagTask(HttpTask task) { - Iterator it = tagTasks.iterator(); - while (it.hasNext()) { - TagTask tagCall = it.next(); - if (tagCall.task == task) { - it.remove(); - break; - } - } - } - - static class TagTask { - - String tag; - Cancelable canceler; - HttpTask task; - - public TagTask(String tag, Cancelable canceler, HttpTask task) { - this.tag = tag; - this.canceler = canceler; - this.task = task; - } - - } - - public MediaType getMediaType(String type) { + + @Override + public WebSocket webSocket(Request request, WebSocketListener listener) { + return client.newWebSocket(request, listener); + } + + public OkHttpClient getOkClient() { + return client; + } + + public int totalTimeoutMillis() { + return client.connectTimeoutMillis() + client.writeTimeoutMillis() + client.readTimeoutMillis(); + } + + public int getTagTaskCount() { + return tagTasks.size(); + } + + public void addTagTask(String tag, Cancelable canceler, HttpTask task) { + tagTasks.add(new TagTask(tag, canceler, task)); + } + + public void removeTagTask(HttpTask task) { + Iterator it = tagTasks.iterator(); + while (it.hasNext()) { + TagTask tagCall = it.next(); + if (tagCall.task == task) { + it.remove(); + break; + } + } + } + + static class TagTask { + + String tag; + Cancelable canceler; + HttpTask task; + + public TagTask(String tag, Cancelable canceler, HttpTask task) { + this.tag = tag; + this.canceler = canceler; + this.task = task; + } + + } + + public MediaType getMediaType(String type) { String mediaType = mediaTypes.get(type); if (mediaType != null) { return MediaType.parse(mediaType); @@ -155,327 +155,325 @@ public MediaType getMediaType(String type) { return MediaType.parse("application/octet-stream"); } - public TaskExecutor getExecutor() { - return executor; - } - - public void preprocess(HttpTask> httpTask, Runnable request) { - if (preprocessors.length > 0) { - RealPreChain process = new RealPreChain(preprocessors, - httpTask, request); - preprocessors[0].doProcess(process); - } else { - request.run(); - } + public TaskExecutor getExecutor() { + return executor; + } + + public void preprocess(HttpTask> httpTask, Runnable request) { + if (preprocessors.length > 0) { + RealPreChain process = new RealPreChain(preprocessors, + httpTask, request); + preprocessors[0].doProcess(process); + } else { + request.run(); + } } - + /** * 串行预处理器 + * * @author Troy.Zhou */ static class SerialPreprocessor implements Preprocessor { - - // 预处理器 - private Preprocessor preprocessor; - // 待处理的任务队列 - private Queue pendings; - // 是否有任务正在执行 - private boolean running = false; - - SerialPreprocessor(Preprocessor preprocessor) { - this.preprocessor = preprocessor; - this.pendings = new LinkedList<>(); - } - - @Override - public void doProcess(PreChain process) { - boolean should = true; - synchronized (this) { - if (running) { - pendings.add(process); - should = false; - } else { - running = true; - } - } - if (should) { - preprocessor.doProcess(process); - } - } - - public void afterProcess() { - PreChain process = null; - synchronized (this) { - if (pendings.size() > 0) { - process = pendings.poll(); - } else { - running = false; - } - } - if (process != null) { - preprocessor.doProcess(process); - } - } - + + // 预处理器 + private Preprocessor preprocessor; + // 待处理的任务队列 + private Queue pendings; + // 是否有任务正在执行 + private boolean running = false; + + SerialPreprocessor(Preprocessor preprocessor) { + this.preprocessor = preprocessor; + this.pendings = new LinkedList<>(); + } + + @Override + public void doProcess(PreChain process) { + boolean should = true; + synchronized (this) { + if (running) { + pendings.add(process); + should = false; + } else { + running = true; + } + } + if (should) { + preprocessor.doProcess(process); + } + } + + public void afterProcess() { + PreChain process = null; + synchronized (this) { + if (pendings.size() > 0) { + process = pendings.poll(); + } else { + running = false; + } + } + if (process != null) { + preprocessor.doProcess(process); + } + } + } - - + + class RealPreChain implements Preprocessor.PreChain { - private int index; - - private Preprocessor[] preprocessors; - - private HttpTask httpTask; - - private Runnable request; - - public RealPreChain(Preprocessor[] preprocessors, - HttpTask httpTask, - Runnable request) { - this.index = 1; - this.preprocessors = preprocessors; - this.httpTask = httpTask; - this.request = request; - } - - @Override - public HttpTask getTask() { - return httpTask; - } - - @Override - public HTTP getHttp() { - return HttpClient.this; - } - - @Override - public void proceed() { - if (index > 0) { - Preprocessor last = preprocessors[index - 1]; - if (last instanceof SerialPreprocessor) { - ((SerialPreprocessor) last).afterProcess(); - } - } - if (index < preprocessors.length) { - preprocessors[index++].doProcess(this); - } else { - request.run(); - } - } + private int index; + + private Preprocessor[] preprocessors; + + private HttpTask httpTask; + + private Runnable request; + + public RealPreChain(Preprocessor[] preprocessors, + HttpTask httpTask, + Runnable request) { + this.index = 1; + this.preprocessors = preprocessors; + this.httpTask = httpTask; + this.request = request; + } + + @Override + public HttpTask getTask() { + return httpTask; + } + + @Override + public HTTP getHttp() { + return HttpClient.this; + } + + @Override + public void proceed() { + if (index > 0) { + Preprocessor last = preprocessors[index - 1]; + if (last instanceof SerialPreprocessor) { + ((SerialPreprocessor) last).afterProcess(); + } + } + if (index < preprocessors.length) { + preprocessors[index++].doProcess(this); + } else { + request.run(); + } + } + + } + + public Builder newBuilder() { + return new Builder(this); + } + + private String urlPath(String urlPath) { + boolean isFullPath = urlPath.startsWith("https://") + || urlPath.startsWith("http://"); + if (isFullPath) { + return urlPath; + } + if (baseUrl != null) { + return baseUrl + urlPath; + } + throw new HttpException("在设置 BaseUrl 之前,您必须使用全路径URL发起请求,当前URL为:" + urlPath); + } + + private String urlPath() { + if (baseUrl != null) { + return baseUrl; + } + throw new HttpException("在设置 BaseUrl 之前,您必须使用全路径URL发起请求,当前URL为:" + baseUrl); + } + + + public static class Builder { + + private OkHttpClient client; + + private String baseUrl; + + private Map mediaTypes; + + private Configurator configurator; + + private Executor mainExecutor; + + private List preprocessors; + + private DownListener downloadListener; + + private TaskListener responseListener; + + private TaskListener exceptionListener; + + private TaskListener completeListener; + + public Builder() { + mediaTypes = new HashMap<>(); + mediaTypes.put("*", "application/octet-stream"); + mediaTypes.put("png", "image/png"); + mediaTypes.put("jpg", "image/jpeg"); + mediaTypes.put("jpeg", "image/jpeg"); + mediaTypes.put("wav", "audio/wav"); + mediaTypes.put("mp3", "audio/mp3"); + mediaTypes.put("mp4", "video/mpeg4"); + mediaTypes.put("txt", "text/plain"); + mediaTypes.put("xls", "application/x-xls"); + mediaTypes.put("xml", "text/xml"); + mediaTypes.put("apk", "application/vnd.android.package-archive"); + mediaTypes.put("doc", "application/msword"); + mediaTypes.put("pdf", "application/pdf"); + mediaTypes.put("html", "text/html"); + preprocessors = new ArrayList<>(); + } + + private Builder(HttpClient hc) { + this.client = hc.client; + this.baseUrl = hc.baseUrl; + this.mediaTypes = hc.mediaTypes; + this.preprocessors = new ArrayList<>(); + Collections.addAll(this.preprocessors, hc.preprocessors); + } + + /** + * 配置 OkHttpClient + * + * @param configurator 配置器 + * @return Builder + */ + public Builder config(Configurator configurator) { + this.configurator = configurator; + return this; + } + + /** + * 设置 baseUrl + * + * @param baseUrl 全局URL前缀 + * @return Builder + */ + public Builder baseUrl(String baseUrl) { + this.baseUrl = baseUrl; + return this; + } + + /** + * 配置媒体类型 + * + * @param mediaTypes 媒体类型 + * @return Builder + */ + public Builder mediaTypes(Map mediaTypes) { + this.mediaTypes.putAll(mediaTypes); + return this; + } + + /** + * 配置媒体类型 + * + * @param key 媒体类型KEY + * @param value 媒体类型VALUE + * @return Builder + */ + public Builder mediaTypes(String key, String value) { + this.mediaTypes.put(key, value); + return this; + } + + /** + * 设置回调执行器,例如实现切换线程功能,只对异步请求有效 + * + * @param executor 回调执行器 + * @return Builder + */ + public Builder callbackExecutor(Executor executor) { + this.mainExecutor = executor; + return this; + } + + /** + * 添加可并行处理请求任务的预处理器 + * + * @param preprocessor 预处理器 + * @return Builder + */ + public Builder addPreprocessor(Preprocessor preprocessor) { + preprocessors.add(preprocessor); + return this; + } + + /** + * 添加预处理器 + * + * @param preprocessor 预处理器 + * @return Builder + */ + public Builder addSerialPreprocessor(Preprocessor preprocessor) { + preprocessors.add(new SerialPreprocessor(preprocessor)); + return this; + } + + /** + * 设置全局响应监听 + * + * @param listener 监听器 + * @return Builder + */ + public Builder responseListener(TaskListener listener) { + this.responseListener = listener; + return this; + } + + /** + * 设置全局异常监听 + * + * @param listener 监听器 + * @return Builder + */ + public Builder exceptionListener(TaskListener listener) { + this.exceptionListener = listener; + return this; + } + + /** + * 设置全局完成监听 + * + * @param listener 监听器 + * @return Builder + */ + public Builder completeListener(TaskListener listener) { + this.completeListener = listener; + return this; + } + + /** + * 设置下载监听器 + * + * @param listener 监听器 + * @return Builder + */ + public Builder downloadListener(DownListener listener) { + this.downloadListener = listener; + return this; + } + + + public HTTP build() { + if (configurator != null || client == null) { + OkHttpClient.Builder builder = new OkHttpClient.Builder(); + if (configurator != null) { + configurator.config(builder); + } + client = builder.build(); + } + return new HttpClient(this); + } } - - public Builder newBuilder() { - return new Builder(this); - } - - private String urlPath(String urlPath) { - boolean isFullPath = urlPath.startsWith("https://") - || urlPath.startsWith("http://"); - if (isFullPath) { - return urlPath; - } - if (baseUrl != null) { - return baseUrl + urlPath; - } - throw new HttpException("在设置 BaseUrl 之前,您必须使用全路径URL发起请求,当前URL为:" + urlPath); - } - - private String urlPath() { - if (baseUrl != null) { - return baseUrl; - } - throw new HttpException("在设置 BaseUrl 之前,您必须使用全路径URL发起请求,当前URL为:" + baseUrl); - } - - - public static class Builder { - - private OkHttpClient client; - - private String baseUrl; - - private Map mediaTypes; - - private Configurator configurator; - - private Executor mainExecutor; - - private List preprocessors; - - private DownListener downloadListener; - - private TaskListener responseListener; - - private TaskListener exceptionListener; - - private TaskListener completeListener; - - public Builder() { - mediaTypes = new HashMap<>(); - mediaTypes.put("*", "application/octet-stream"); - mediaTypes.put("png", "image/png"); - mediaTypes.put("jpg", "image/jpeg"); - mediaTypes.put("jpeg", "image/jpeg"); - mediaTypes.put("wav", "audio/wav"); - mediaTypes.put("mp3", "audio/mp3"); - mediaTypes.put("mp4", "video/mpeg4"); - mediaTypes.put("txt", "text/plain"); - mediaTypes.put("xls", "application/x-xls"); - mediaTypes.put("xml", "text/xml"); - mediaTypes.put("apk", "application/vnd.android.package-archive"); - mediaTypes.put("doc", "application/msword"); - mediaTypes.put("pdf", "application/pdf"); - mediaTypes.put("html", "text/html"); - preprocessors = new ArrayList<>(); - } - - private Builder(HttpClient hc) { - this.client = hc.client; - this.baseUrl = hc.baseUrl; - this.mediaTypes = hc.mediaTypes; - this.preprocessors = new ArrayList<>(); - Collections.addAll(this.preprocessors, hc.preprocessors); - } - - /** - * 配置 OkHttpClient - * @param configurator 配置器 - * @return Builder - */ - public Builder config(Configurator configurator) { - this.configurator = configurator; - return this; - } - - /** - * 设置 baseUrl - * @param baseUrl 全局URL前缀 - * @return Builder - */ - public Builder baseUrl(String baseUrl) { - this.baseUrl = baseUrl; - return this; - } - - /** - * 配置媒体类型 - * @param mediaTypes 媒体类型 - * @return Builder - */ - public Builder mediaTypes(Map mediaTypes) { - this.mediaTypes.putAll(mediaTypes); - return this; - } - - /** - * 配置媒体类型 - * @param key 媒体类型KEY - * @param value 媒体类型VALUE - * @return Builder - */ - public Builder mediaTypes(String key, String value) { - this.mediaTypes.put(key, value); - return this; - } - - /** - * 设置回调执行器,例如实现切换线程功能,只对异步请求有效 - * @param executor 回调执行器 - * @return Builder - */ - public Builder callbackExecutor(Executor executor) { - this.mainExecutor = executor; - return this; - } - - /** - * 添加可并行处理请求任务的预处理器 - * @param preprocessor 预处理器 - * @return Builder - */ - public Builder addPreprocessor(Preprocessor preprocessor) { - preprocessors.add(preprocessor); - return this; - } - - /** - * 添加预处理器 - * @param preprocessor 预处理器 - * @return Builder - */ - public Builder addSerialPreprocessor(Preprocessor preprocessor) { - preprocessors.add(new SerialPreprocessor(preprocessor)); - return this; - } - - /** - * 设置全局响应监听 - * @param listener 监听器 - * @return Builder - */ - public Builder responseListener(TaskListener listener) { - this.responseListener = listener; - return this; - } - - /** - * 设置全局异常监听 - * @param listener 监听器 - * @return Builder - */ - public Builder exceptionListener(TaskListener listener) { - this.exceptionListener = listener; - return this; - } - - /** - * 设置全局完成监听 - * @param listener 监听器 - * @return Builder - */ - public Builder completeListener(TaskListener listener) { - this.completeListener = listener; - return this; - } - - /** - * 设置下载监听器 - * @param listener 监听器 - * @return Builder - */ - public Builder downloadListener(DownListener listener) { - this.downloadListener = listener; - return this; - } - - /** - * 设置超时时间 - * @param connectTimeout 连接时间 - * @param readTimeout 读取时间 - * @param timeUnit - * @return - */ - public Builder builderTimeOut(long connectTimeout, long readTimeout, TimeUnit timeUnit) { - this.client= new OkHttpClient.Builder() - .connectTimeout(connectTimeout,timeUnit) // 设置连接超时时间 - .readTimeout(readTimeout,timeUnit) // 设置读取超时时间 - .build(); - return this; - } - - public HTTP build() { - if (configurator != null || client == null) { - OkHttpClient.Builder builder = new OkHttpClient.Builder(); - if (configurator != null) { - configurator.config(builder); - } - client = builder.build(); - } - return new HttpClient(this); - } - - } - + } From dbef39a0ad6d3dec0a98e1c74e64ea897b34d7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E6=97=AD?= Date: Fri, 10 Apr 2020 14:14:51 +0800 Subject: [PATCH 94/94] =?UTF-8?q?=E4=BC=98=E5=8C=96=20build=20url=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/ejlchina/okhttps/HttpTask.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/ejlchina/okhttps/HttpTask.java b/src/main/java/com/ejlchina/okhttps/HttpTask.java index 7e251013..59b57d93 100644 --- a/src/main/java/com/ejlchina/okhttps/HttpTask.java +++ b/src/main/java/com/ejlchina/okhttps/HttpTask.java @@ -474,7 +474,7 @@ public C addFileParam(String name, String type, String fileName, byte[] content) return (C) this; } - class FilePara { + static class FilePara { String type; String fileName; @@ -624,27 +624,31 @@ private String buildUrlPath() { throw new HttpException("url 里有 pathParameter 没有设置,你必须先调用 addPathParam 为其设置!"); } if (urlParams != null) { - if (url.contains("?")) { - if (!url.endsWith("?")) { - url = url.trim(); - if (url.lastIndexOf("=") < url.lastIndexOf("?") + 2) { - throw new HttpException("url 格式错误,'?' 后没有发现 '='"); - } - if (!url.endsWith("&")) { - url += "&"; - } - } - } else { - url += "?"; - } - for (String name : urlParams.keySet()) { - url += name + "=" + urlParams.get(name) + "&"; - } - url = url.substring(0, url.length() - 1); + url = buildUrl(url.trim()); } return url; } + private String buildUrl(String url) { + StringBuilder sb = new StringBuilder(url); + if (url.contains("?")) { + if (!url.endsWith("?")) { + if (url.lastIndexOf("=") < url.lastIndexOf("?") + 2) { + throw new HttpException("url 格式错误,'?' 后没有发现 '='"); + } + if (!url.endsWith("&")) { + sb.append('&'); + } + } + } else { + sb.append('?'); + } + for (String name : urlParams.keySet()) { + sb.append(name).append('=').append(urlParams.get(name)).append('&'); + } + sb.delete(sb.length() - 1, sb.length()); + return sb.toString(); + } protected void assertNotConflict(boolean isGetRequest) { if (isGetRequest) {