From b2b10dab51173f895d2aaee9f587f882df575ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Android=20=E8=BD=AE=E5=AD=90=E5=93=A5?= Date: Sun, 26 Jun 2022 15:39:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BD=91=E7=BB=9C=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E6=97=A5=E5=BF=97=E7=9A=84=20TAG=20=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=20=E4=BC=98=E5=8C=96=E5=8F=8D=E5=B0=84=E6=80=A7=E8=83=BD?= =?UTF-8?q?=EF=BC=88=E5=88=A9=E7=94=A8=20LruCache=20=E7=BC=93=E5=AD=98?= =?UTF-8?q?=EF=BC=89=20=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=9B=9E=E8=B0=83=E7=9B=91=E5=90=AC=E6=89=80=E5=9C=A8?= =?UTF-8?q?=E7=9A=84=E7=BA=BF=E7=A8=8B=E7=B1=BB=E5=9E=8B=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20IRequestHandler=20=E4=B8=8B=E8=BD=BD=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20Delete=20=E4=BD=BF=E7=94=A8=20Body=20=E4=BC=A0?= =?UTF-8?q?=E5=8F=82=E7=9A=84=E8=AF=B7=E6=B1=82=E6=96=B9=E6=B3=95=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=B7=E6=B1=82=20Bean=20=E7=B1=BB?= =?UTF-8?q?=E4=B8=BA=E6=B5=81=E7=B1=BB=E5=9E=8B=E6=97=B6=E6=B5=81=E8=A2=AB?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E7=9A=84=E9=97=AE=E9=A2=98=20=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E6=97=B6=E5=B8=A6?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E7=9A=84=E6=96=87=E4=BB=B6=E5=90=8D=E8=A2=AB?= =?UTF-8?q?=E7=BC=96=E7=A0=81=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/issue_template_bug.md | 24 ++-- .../ISSUE_TEMPLATE/issue_template_suggest.md | 6 +- HelpDoc.md | 38 +++++- README.md | 8 +- app/build.gradle | 16 +-- .../java/com/hjq/easy/demo/MainActivity.java | 9 +- .../easy/demo/http/model/RequestHandler.java | 27 ++++- app/src/main/res/values/strings.xml | 3 +- library/build.gradle | 4 +- .../main/java/com/hjq/http/EasyConfig.java | 23 +++- .../src/main/java/com/hjq/http/EasyHttp.java | 13 ++ .../src/main/java/com/hjq/http/EasyLog.java | 3 +- .../src/main/java/com/hjq/http/EasyUtils.java | 88 ++++++++++++-- .../java/com/hjq/http/body/ProgressBody.java | 38 +++--- .../com/hjq/http/callback/BaseCallback.java | 17 ++- .../hjq/http/callback/DownloadCallback.java | 112 ++++++++++-------- .../com/hjq/http/callback/NormalCallback.java | 81 +++++++------ .../com/hjq/http/config/IRequestHandler.java | 12 ++ ...D5Exception.java => FileMD5Exception.java} | 40 +++---- .../com/hjq/http/exception/HttpException.java | 6 +- .../http/lifecycle/ApplicationLifecycle.java | 8 ++ .../com/hjq/http/model/ThreadSchedulers.java | 16 +++ .../com/hjq/http/request/BodyRequest.java | 10 +- .../hjq/http/request/DeleteBodyRequest.java | 25 ++++ .../com/hjq/http/request/HttpRequest.java | 44 ++++--- 25 files changed, 468 insertions(+), 203 deletions(-) rename library/src/main/java/com/hjq/http/exception/{MD5Exception.java => FileMD5Exception.java} (71%) create mode 100644 library/src/main/java/com/hjq/http/model/ThreadSchedulers.java create mode 100644 library/src/main/java/com/hjq/http/request/DeleteBodyRequest.java diff --git a/.github/ISSUE_TEMPLATE/issue_template_bug.md b/.github/ISSUE_TEMPLATE/issue_template_bug.md index 9e2b8c8..2b36929 100644 --- a/.github/ISSUE_TEMPLATE/issue_template_bug.md +++ b/.github/ISSUE_TEMPLATE/issue_template_bug.md @@ -8,31 +8,31 @@ assignees: getActivity ## 问题描述 -* 框架版本:XXX +* 框架版本【必填】:XXX -* 问题描述:XXX +* 问题描述【必填】:XXX -* 复现步骤:XXX +* 复现步骤【必填】:XXX -* 是否必现:填是/否 +* 是否必现【必填】:填是/否 -* 出现问题的手机信息:请填写出现问题的品牌和机型 +* 出现问题的手机信息【必填】:请填写出现问题的品牌和机型 -* 出现问题的安卓版本:请填写出现问题的 Android 版本 +* 出现问题的安卓版本【必填】:请填写出现问题的 Android 版本 ## 请回答 -* 是部分机型还是所有机型都会出现:部分/全部(例如:某为,某 Android 版本会出现) +* 是部分机型还是所有机型都会出现【必答】:部分/全部(例如:某为,某 Android 版本会出现) -* 框架最新的版本是否存在这个问题:是/否(如果用的是旧版本的话,建议升级看问题是否还存在) +* 框架最新的版本是否存在这个问题【必答】:是/否(如果用的是旧版本的话,建议升级看问题是否还存在) -* 是否已经查阅框架文档还未能解决的:是/否(文档会提供最常见的问题解答,可以看看是否有自己想要的) +* 是否已经查阅框架文档还未能解决的【必答】:是/否(文档会提供最常见的问题解答,可以看看是否有自己想要的) -* issue 是否有人曾提过类似的问题:是/否(看看曾经有人提过类似的问题,先参考一下别人是怎么解决的) +* issue 是否有人曾提过类似的问题【必答】:是/否(看看曾经有人提过类似的问题,先参考一下别人是怎么解决的) -* 是否可以通过 Demo 来复现该问题:是/否(排查一下是不是自己的项目代码写得有问题导致的) +* 是否可以通过 Demo 来复现该问题【必答】:是/否(排查一下是不是自己的项目代码写得有问题导致的) -* 这个问题是不是后台自己的问题导致的:是/否(如果无法确定问题的原因,请先和后台开发人员协商联调,确认了问题是框架的再反馈给作者,如果是后台的问题作者也没用,最后还是要自己找后台处理才有用的) +* 这个问题是不是后台自己的问题导致的【必答】:是/否(如果无法确定问题的原因,请先和后台开发人员协商联调,确认了问题是框架的再反馈给作者,如果是后台的问题作者也没用,最后还是要自己找后台处理才有用的) ## 其他 diff --git a/.github/ISSUE_TEMPLATE/issue_template_suggest.md b/.github/ISSUE_TEMPLATE/issue_template_suggest.md index 9c343fc..4901b6a 100644 --- a/.github/ISSUE_TEMPLATE/issue_template_suggest.md +++ b/.github/ISSUE_TEMPLATE/issue_template_suggest.md @@ -8,8 +8,8 @@ assignees: getActivity ## 建议收集 -* issue 是否有人曾提过类似的问题?(必答项,一旦出现重复提问我将不会再次解答) +* issue 是否有人曾提过类似的问题?【必答】(一旦出现重复提问我将不会再次解答) -* 你觉得框架有什么不足之处?(必答项,你可以描述框架有什么令你不满意的地方) +* 你觉得框架有什么不足之处?【必答】(你可以描述框架有什么令你不满意的地方) -* 你觉得该怎么去完善会比较好?(非必答项,你可以提供一下自己的想法或者做法供作者参考) \ No newline at end of file +* 你觉得该怎么去完善会比较好?【非必答】(你可以提供一下自己的想法或者做法供作者参考) \ No newline at end of file diff --git a/HelpDoc.md b/HelpDoc.md index f857bc6..b76fb79 100644 --- a/HelpDoc.md +++ b/HelpDoc.md @@ -72,6 +72,8 @@ * [如何对接口路径进行动态化拼接](#如何对接口路径进行动态化拼接) + * [如何动态化整个请求的 url](#如何动态化整个请求的-url) + * [Https 如何配置信任所有证书](#https-如何配置信任所有证书) * [我不想一个接口写一个类怎么办](#我不想一个接口写一个类怎么办) @@ -88,6 +90,8 @@ * [如何设置自定义的 UA 标识](#如何设置自定义的-ua-标识) + * [我想修改请求回调所在的线程该怎么办](#我想修改请求回调所在的线程该怎么办) + * [我想自定义一个 RequestBody 进行请求该怎么办](#我想自定义一个-requestbody-进行请求该怎么办) * [搭配 RxJava](#搭配-rxjava) @@ -281,7 +285,7 @@ public final class XxxApi implements IRequestServer, IRequestApi { #### 发起请求 -* 需要配置请求状态及生命周期处理,具体封装可以参考 [BaseActivity](app/src/main/java/com/hjq/http/demo/BaseActivity.java) +* 需要配置请求状态及生命周期处理,具体封装可以参考 [BaseActivity](app/src/main/java/com/hjq/easy/demo/BaseActivity.java) ```java EasyHttp.post(this) @@ -1170,6 +1174,20 @@ public final class XxxApi implements IRequestApi { } ``` +#### 如何动态化整个请求的 url + +```java +EasyHttp.post(this) + .api(new RequestUrl("https://xxxx.com/aaaa")) + .request(new HttpCallback(this) { + + @Override + public void onSucceed(Xxx result) { + + } + }); +``` + #### Https 如何配置信任所有证书 * 在初始化 OkHttp 的时候这样设置 @@ -1240,7 +1258,7 @@ EasyHttp.post(new ActivityLifecycle(this)) * 如果以上条件都不满足,但是你就是想在某个地方请求网络,那么你可以这样写 ```java -EasyHttp.post(new ApplicationLifecycle()) +EasyHttp.post(ApplicationLifecycle.getInstance()) .api(new XxxApi()) .tag("abc") .request(new OnHttpListener>() { @@ -1422,6 +1440,22 @@ EasyHttp.post(this) * 首先 UA 是 User Agent 的简称,当我们没有设置自定义 UA 标识的时候,那么 OkHttp 会在 BridgeInterceptor 拦截器添加一个默认的 UA 标识,那么如何在 EasyHttp 设置自定义 UA 标识呢?其实很简单,UA 标识本质上其实就是一个请求头,在 EasyHttp 中添加一个请求头为 `"User-Agent` 的参数即可,至于怎么添加请求头,前面的文档已经有介绍了,这里不再赘述。 +#### 我想修改请求回调所在的线程该怎么办 + +``` +EasyHttp.post(this) + .api(new XxxApi()) + // 表示回调是在子线程中进行 + .schedulers(ThreadSchedulers.IOThread) + .request(new HttpCallback>(this) { + + @Override + public void onSucceed(HttpData result) { + + } + }); +``` + #### 我想自定义一个 RequestBody 进行请求该怎么办 * 在一些极端的情况下,框架无法满足使用的前提下,这个时候需要自定义 `RequestBody` 来实现,那么怎么使用自定义 `RequestBody` 呢?框架其实有开放方法,具体使用示例如下: diff --git a/README.md b/README.md index 3611881..8331e86 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ * 博客地址:[网络请求,如斯优雅](https://www.jianshu.com/p/93cd59dec002) -* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处下载Demo](https://github.com/getActivity/EasyHttp/releases/download/11.0/EasyHttp.apk) +* 可以扫码下载 Demo 进行演示或者测试,如果扫码下载不了的,[点击此处下载Demo](https://github.com/getActivity/EasyHttp/releases/download/11.2/EasyHttp.apk) ![](picture/demo_code.png) @@ -61,7 +61,7 @@ android { dependencies { // 网络请求框架:https://github.com/getActivity/EasyHttp - implementation 'com.github.getActivity:EasyHttp:11.0' + implementation 'com.github.getActivity:EasyHttp:11.2' // OkHttp 框架:https://github.com/square/okhttp // noinspection GradleDependency implementation 'com.squareup.okhttp3:okhttp:3.12.13' @@ -76,9 +76,9 @@ dependencies { | 功能或细节 | [EasyHttp](https://github.com/getActivity/EasyHttp) | [Retrofit](https://github.com/square/retrofit) | [OkGo](https://github.com/jeasonlzy/okhttp-OkGo) | | :----: | :------: | :-----: | :-----: | -| 对应版本 | 11.0 | 2.9.0 | 3.0.4 | +| 对应版本 | 11.2 | 2.9.0 | 3.0.4 | | issues 数 | [![](https://img.shields.io/github/issues/getActivity/EasyHttp.svg)](https://github.com/getActivity/EasyHttp/issues) | [![](https://img.shields.io/github/issues/square/retrofit.svg)](https://github.com/square/retrofit/issues) | [![](https://img.shields.io/github/issues/jeasonlzy/okhttp-OkGo.svg)](https://github.com/jeasonlzy/okhttp-OkGo/issues) | -| **aar 包大小** | 80 KB | 123 KB | 131 KB | +| **aar 包大小** | 86 KB | 123 KB | 131 KB | | minSdk 要求 | API 14+ | API 21+ | API 14+ | | 配置多域名 | ✅ | ❌ | ✅ | | **动态 Host** | ✅ | ❌ | ❌ | diff --git a/app/build.gradle b/app/build.gradle index 40594fa..72f9bf4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId 'com.hjq.easy.demo' minSdkVersion 16 targetSdkVersion 31 - versionCode 1100 - versionName '11.0' + versionCode 1120 + versionName '11.2' } // 支持 JDK 1.8 @@ -63,6 +63,8 @@ dependencies { // AndroidX 库:https://github.com/androidx/androidx implementation 'androidx.appcompat:appcompat:1.4.0' + // Material 库:https://github.com/material-components/material-components-android + implementation 'com.google.android.material:material:1.4.0' // OkHttp 框架:https://github.com/square/okhttp // 升级注意事项:https://www.jianshu.com/p/d12d0f536f55 @@ -70,24 +72,24 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:3.12.13' // 吐司框架:https://github.com/getActivity/ToastUtils - implementation 'com.github.getActivity:ToastUtils:10.3' + implementation 'com.github.getActivity:ToastUtils:10.5' // 权限请求框架:https://github.com/getActivity/XXPermissions - implementation 'com.github.getActivity:XXPermissions:13.5' + implementation 'com.github.getActivity:XXPermissions:15.0' // 标题栏框架:https://github.com/getActivity/TitleBar - implementation 'com.github.getActivity:TitleBar:9.3' + implementation 'com.github.getActivity:TitleBar:9.5' // Json 解析框架:https://github.com/google/gson implementation 'com.google.code.gson:gson:2.9.0' // Gson 解析容错:https://github.com/getActivity/GsonFactory - implementation 'com.github.getActivity:GsonFactory:6.0' + implementation 'com.github.getActivity:GsonFactory:6.2' // 腾讯 MMKV:https://github.com/Tencent/MMKV implementation 'com.tencent:mmkv-static:1.2.12' // 日志调试框架:https://github.com/getActivity/Logcat - debugImplementation 'com.github.getActivity:Logcat:10.3' + debugImplementation 'com.github.getActivity:Logcat:10.6' // 内存泄漏监测框架:https://github.com/square/leakcanary debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' diff --git a/app/src/main/java/com/hjq/easy/demo/MainActivity.java b/app/src/main/java/com/hjq/easy/demo/MainActivity.java index 661dcf6..82aa71c 100644 --- a/app/src/main/java/com/hjq/easy/demo/MainActivity.java +++ b/app/src/main/java/com/hjq/easy/demo/MainActivity.java @@ -24,6 +24,7 @@ import com.hjq.easy.demo.http.model.HttpData; import com.hjq.http.EasyHttp; import com.hjq.http.EasyUtils; +import com.hjq.http.exception.FileMD5Exception; import com.hjq.http.listener.HttpCallback; import com.hjq.http.listener.OnDownloadListener; import com.hjq.http.listener.OnUpdateListener; @@ -184,7 +185,7 @@ public void onSucceed(HttpData result) { // } // 如果是放到外部存储的应用专属目录则不需要适配分区存储特性 - File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "EasyHttp.png"); + File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "我是测试专用的图片.png"); if (!file.exists()) { // 生成图片到本地 @@ -286,7 +287,11 @@ public void onComplete(File file) { @Override public void onError(File file, Exception e) { - ToastUtils.show("下载出错:" + e.getMessage()); + ToastUtils.show(e.getMessage()); + if (e instanceof FileMD5Exception) { + // 如果是文件 md5 校验失败,则删除文件 + file.delete(); + } } @Override diff --git a/app/src/main/java/com/hjq/easy/demo/http/model/RequestHandler.java b/app/src/main/java/com/hjq/easy/demo/http/model/RequestHandler.java index ec56ac0..17128b6 100644 --- a/app/src/main/java/com/hjq/easy/demo/http/model/RequestHandler.java +++ b/app/src/main/java/com/hjq/easy/demo/http/model/RequestHandler.java @@ -19,6 +19,7 @@ import com.hjq.http.config.IRequestHandler; import com.hjq.http.exception.CancelException; import com.hjq.http.exception.DataException; +import com.hjq.http.exception.FileMD5Exception; import com.hjq.http.exception.HttpException; import com.hjq.http.exception.NetworkException; import com.hjq.http.exception.NullBodyException; @@ -61,9 +62,8 @@ public Object requestSucceed(@NonNull HttpRequest httpRequest, @NonNull Respo } if (!response.isSuccessful()) { - // 返回响应异常 - throw new ResponseException(mApplication.getString(R.string.http_response_error) + ", responseCode: " + - response.code() + ", message: " + response.message(), response); + throw new ResponseException(String.format(mApplication.getString(R.string.http_response_error), + response.code(), response.message()), response); } if (Headers.class.equals(type)) { @@ -171,6 +171,27 @@ public Exception requestFail(@NonNull HttpRequest httpRequest, @NonNull Excep return new HttpException(e.getMessage(), e); } + @NonNull + @Override + public Exception downloadFail(@NonNull HttpRequest httpRequest, @NonNull Exception e) { + if (e instanceof ResponseException) { + ResponseException responseException = ((ResponseException) e); + Response response = responseException.getResponse(); + responseException.setMessage(String.format(mApplication.getString(R.string.http_response_error), + response.code(), response.message())); + return responseException; + } else if (e instanceof NullBodyException) { + NullBodyException nullBodyException = ((NullBodyException) e); + nullBodyException.setMessage(mApplication.getString(R.string.http_response_null_body)); + return nullBodyException; + } else if (e instanceof FileMD5Exception) { + FileMD5Exception fileMd5Exception = ((FileMD5Exception) e); + fileMd5Exception.setMessage(mApplication.getString(R.string.http_response_md5_error)); + return fileMd5Exception; + } + return requestFail(httpRequest, e); + } + @Nullable @Override public Object readCache(@NonNull HttpRequest httpRequest, @NonNull Type type, long cacheTime) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c33f222..60b77ac 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,9 +8,10 @@ 数据解析异常,请稍后 服务器请求超时,请稍后再试 请求失败,请检查网络设置 - 服务器响应异常,请稍后再试 + 服务器响应异常,请稍后再试,响应码:%d,响应信息:%s 服务器连接异常,请稍后再试 请求被中断,请重试 服务器数据返回异常,请稍后再试 + 下载失败,文件 md5 校验失败 \ No newline at end of file diff --git a/library/build.gradle b/library/build.gradle index 2d9af27..56cc9a6 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -5,8 +5,8 @@ android { defaultConfig { minSdkVersion 14 - versionCode 1100 - versionName "11.0" + versionCode 1120 + versionName "11.2" } // 使用 JDK 1.8 diff --git a/library/src/main/java/com/hjq/http/EasyConfig.java b/library/src/main/java/com/hjq/http/EasyConfig.java index 8547ccf..3ee30c1 100644 --- a/library/src/main/java/com/hjq/http/EasyConfig.java +++ b/library/src/main/java/com/hjq/http/EasyConfig.java @@ -6,6 +6,7 @@ import com.hjq.http.config.IRequestServer; import com.hjq.http.config.LogStrategy; import com.hjq.http.config.RequestServer; +import com.hjq.http.model.ThreadSchedulers; import java.net.MalformedURLException; import java.net.URL; @@ -57,6 +58,9 @@ public static EasyConfig with(OkHttpClient client) { /** 通用请求头 */ private HashMap mHeaders; + /** 线程调度器 */ + private ThreadSchedulers mThreadSchedulers = ThreadSchedulers.MainThread; + /** 日志开关 */ private boolean mLogEnabled = true; /** 日志 TAG */ @@ -144,6 +148,15 @@ public EasyConfig removeParam(String key) { return this; } + public EasyConfig setThreadSchedulers(ThreadSchedulers schedulers) { + if (mThreadSchedulers == null) { + // 线程调度器不能为空 + throw new NullPointerException("Thread schedulers cannot be empty"); + } + mThreadSchedulers = schedulers; + return this; + } + public EasyConfig setLogStrategy(ILogStrategy strategy) { mLogStrategy = strategy; return this; @@ -201,6 +214,10 @@ public HashMap getHeaders() { return mHeaders; } + public ThreadSchedulers getThreadSchedulers() { + return mThreadSchedulers; + } + public ILogStrategy getLogStrategy() { return mLogStrategy; } @@ -223,15 +240,15 @@ public long getRetryTime() { public void into() { if (mClient == null) { - throw new IllegalArgumentException("The OkHttp client object cannot be empty"); + throw new IllegalArgumentException("Please set up the OkHttpClient object"); } if (mServer == null) { - throw new IllegalArgumentException("The host configuration cannot be empty"); + throw new IllegalArgumentException("Please set up the RequestServer object"); } if (mHandler == null) { - throw new IllegalArgumentException("The object being processed by the request cannot be empty"); + throw new IllegalArgumentException("Please set the RequestHandler object"); } try { diff --git a/library/src/main/java/com/hjq/http/EasyHttp.java b/library/src/main/java/com/hjq/http/EasyHttp.java index 09a86c4..a483b62 100644 --- a/library/src/main/java/com/hjq/http/EasyHttp.java +++ b/library/src/main/java/com/hjq/http/EasyHttp.java @@ -2,6 +2,7 @@ import androidx.lifecycle.LifecycleOwner; +import com.hjq.http.request.DeleteBodyRequest; import com.hjq.http.request.DeleteRequest; import com.hjq.http.request.DownloadRequest; import com.hjq.http.request.GetRequest; @@ -72,6 +73,18 @@ public static DeleteRequest delete(LifecycleOwner lifecycleOwner) { return new DeleteRequest(lifecycleOwner); } + /** + * Delete 请求(参数使用 Body 传递) + * + * @param lifecycleOwner 请传入 AppCompatActivity 或者 AndroidX.Fragment 子类 + * 如需传入其他对象请参考以下两个类 + * {@link com.hjq.http.lifecycle.ActivityLifecycle} + * {@link com.hjq.http.lifecycle.ApplicationLifecycle} + */ + public static DeleteBodyRequest deleteBody(LifecycleOwner lifecycleOwner) { + return new DeleteBodyRequest(lifecycleOwner); + } + /** * Put 请求 * diff --git a/library/src/main/java/com/hjq/http/EasyLog.java b/library/src/main/java/com/hjq/http/EasyLog.java index dbf986f..3b00848 100644 --- a/library/src/main/java/com/hjq/http/EasyLog.java +++ b/library/src/main/java/com/hjq/http/EasyLog.java @@ -86,6 +86,7 @@ private static String getLogTag(HttpRequest httpRequest) { if (httpRequest == null) { return logTag; } - return logTag + " " + httpRequest.getRequestApi().getClass().getSimpleName(); + return logTag + " " + httpRequest.getRequestApi().getClass().getSimpleName() + + "@" + Integer.toHexString(httpRequest.hashCode()); } } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/EasyUtils.java b/library/src/main/java/com/hjq/http/EasyUtils.java index a71fc8e..11db3f9 100644 --- a/library/src/main/java/com/hjq/http/EasyUtils.java +++ b/library/src/main/java/com/hjq/http/EasyUtils.java @@ -3,6 +3,7 @@ import android.os.Handler; import android.os.Looper; import android.text.TextUtils; +import android.util.LruCache; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -10,6 +11,7 @@ import com.hjq.http.annotation.HttpIgnore; import com.hjq.http.annotation.HttpRename; import com.hjq.http.model.FileContentResolver; +import com.hjq.http.model.ThreadSchedulers; import org.json.JSONArray; import org.json.JSONException; @@ -33,6 +35,8 @@ import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,18 +56,51 @@ public final class EasyUtils { /** Handler 对象 */ private static final Handler HANDLER = new Handler(Looper.getMainLooper()); + /** 反射字段缓存 */ + private static final LruCache, List> CLASS_LIST_LRU_CACHE = new LruCache<>(30); + /** * 在主线程中执行 */ - public static void post(Runnable r) { - HANDLER.post(r); + public static void runOnMainThread(Runnable runnable) { + HANDLER.post(runnable); + } + + /** + * 在子线程中执行 + */ + public static void runOnIOThread(Runnable runnable) { + EasyConfig.getInstance().getClient().dispatcher().executorService().execute(runnable); + } + + /** + * 运行在指定线程 + */ + public static void runOnAssignThread(ThreadSchedulers schedulers, Runnable runnable) { + switch (schedulers) { + case IOThread: + if (isMainThread()) { + runOnIOThread(runnable); + } else { + runnable.run(); + } + break; + case MainThread: + default: + if (isMainThread()) { + runnable.run(); + } else { + runOnMainThread(runnable); + } + break; + } } /** * 延迟一段时间执行 */ - public static void postDelayed(Runnable r, long delayMillis) { - HANDLER.postDelayed(r, delayMillis); + public static void postDelayed(Runnable runnable, long delayMillis) { + HANDLER.postDelayed(runnable, delayMillis); } /** @@ -112,10 +149,7 @@ public static boolean isMultipartParameter(List fields) { // 允许访问私有字段 field.setAccessible(true); - int modifiers = field.getModifiers(); - // 如果这是一个常量字段,则直接忽略掉,例如 Parcelable 接口中的 CREATOR 字段 - // https://github.com/getActivity/EasyHttp/issues/112 - if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + if (EasyUtils.isConstantField(field)) { continue; } @@ -259,10 +293,7 @@ public static HashMap beanToHashMap(Object object) { // 允许访问私有字段 field.setAccessible(true); - int modifiers = field.getModifiers(); - // 如果这是一个常量字段,则直接忽略掉,例如 Parcelable 接口中的 CREATOR 字段 - // https://github.com/getActivity/EasyHttp/issues/112 - if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + if (EasyUtils.isConstantField(field)) { continue; } @@ -490,4 +521,37 @@ public static OutputStream openFileOutputStream(File file) throws FileNotFoundEx } return new FileOutputStream(file); } + + /** + * 判断一个字段是否是常量字段 + */ + public static boolean isConstantField(Field field) { + int modifiers = field.getModifiers(); + // 如果这是一个常量字段,则直接忽略掉,例如 Parcelable 接口中的 CREATOR 字段 + // https://github.com/getActivity/EasyHttp/issues/112 + return Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers); + } + + /** + * 获取类的所有字段 + */ + public static List getAllFields(Class originalClass) { + List fields = CLASS_LIST_LRU_CACHE.get(originalClass); + if (fields != null) { + return fields; + } + + fields = new ArrayList<>(); + Class clazz = originalClass; + do { + Field[] declaredFields = clazz.getDeclaredFields(); + fields.addAll(0, Arrays.asList(declaredFields)); + // 遍历获取父类的字段 + clazz = clazz.getSuperclass(); + } while (clazz != null && !Object.class.equals(clazz)); + + CLASS_LIST_LRU_CACHE.put(originalClass, fields); + + return fields; + } } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/body/ProgressBody.java b/library/src/main/java/com/hjq/http/body/ProgressBody.java index 1954088..552b058 100644 --- a/library/src/main/java/com/hjq/http/body/ProgressBody.java +++ b/library/src/main/java/com/hjq/http/body/ProgressBody.java @@ -1,5 +1,6 @@ package com.hjq.http.body; +import androidx.annotation.NonNull; import androidx.lifecycle.LifecycleOwner; import com.hjq.http.EasyLog; @@ -56,7 +57,7 @@ public long contentLength() throws IOException { } @Override - public void writeTo(BufferedSink sink) throws IOException { + public void writeTo(@NonNull BufferedSink sink) throws IOException { mTotalByte = contentLength(); sink = Okio.buffer(new WrapperSink(sink)); mRequestBody.writeTo(sink); @@ -73,22 +74,25 @@ public WrapperSink(Sink delegate) { public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); mUpdateByte += byteCount; - EasyUtils.post(() -> { - if (mListener != null && HttpLifecycleManager.isLifecycleActive(mLifecycleOwner)) { - mListener.onByte(mTotalByte, mUpdateByte); - } - int progress = EasyUtils.getProgressProgress(mTotalByte, mUpdateByte); - // 只有上传进度发生改变的时候才回调此方法,避免引起不必要的 View 重绘 - if (progress != mUpdateProgress) { - mUpdateProgress = progress; - if (mListener != null && HttpLifecycleManager.isLifecycleActive(mLifecycleOwner)) { - mListener.onProgress(progress); - } - EasyLog.printLog(mHttpRequest, "Uploading in progress, uploaded: " + - mUpdateByte + " / " + mTotalByte + - ", progress: " + progress + "%"); - } - }); + + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), ProgressBody.this::callOnProgress); + } + } + + private void callOnProgress() { + if (mListener != null && HttpLifecycleManager.isLifecycleActive(mLifecycleOwner)) { + mListener.onByte(mTotalByte, mUpdateByte); + } + int progress = EasyUtils.getProgressProgress(mTotalByte, mUpdateByte); + // 只有上传进度发生改变的时候才回调此方法,避免引起不必要的 View 重绘 + if (progress != mUpdateProgress) { + mUpdateProgress = progress; + if (mListener != null && HttpLifecycleManager.isLifecycleActive(mLifecycleOwner)) { + mListener.onProgress(progress); + } + EasyLog.printLog(mHttpRequest, "Uploading in progress, uploaded: " + + mUpdateByte + " / " + mTotalByte + + ", progress: " + progress + "%"); } } } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/callback/BaseCallback.java b/library/src/main/java/com/hjq/http/callback/BaseCallback.java index 6204a2b..582f3ec 100644 --- a/library/src/main/java/com/hjq/http/callback/BaseCallback.java +++ b/library/src/main/java/com/hjq/http/callback/BaseCallback.java @@ -1,5 +1,7 @@ package com.hjq.http.callback; +import androidx.annotation.NonNull; + import com.hjq.http.EasyConfig; import com.hjq.http.EasyLog; import com.hjq.http.EasyUtils; @@ -31,7 +33,7 @@ public abstract class BaseCallback implements Callback { /** 当前重试次数 */ private int mRetryCount; - public BaseCallback(HttpRequest request) { + public BaseCallback(@NonNull HttpRequest request) { mHttpRequest = request; HttpLifecycleManager.bind(mHttpRequest.getLifecycleOwner()); } @@ -51,7 +53,7 @@ protected CallProxy getCall() { } @Override - public void onResponse(Call call, Response response) { + public void onResponse(@NonNull Call call, @NonNull Response response) { try { // 收到响应 onResponse(response); @@ -60,12 +62,12 @@ public void onResponse(Call call, Response response) { onFailure(e); } finally { // 关闭响应 - EasyUtils.closeStream(response); + closeResponse(response); } } @Override - public void onFailure(Call call, IOException e) { + public void onFailure(@NonNull Call call, @NonNull IOException e) { // 服务器请求超时重试 if (e instanceof SocketTimeoutException && mRetryCount < EasyConfig.getInstance().getRetryCount()) { // 设置延迟 N 秒后重试该请求 @@ -107,4 +109,11 @@ public void onFailure(Call call, IOException e) { * 请求失败 */ protected abstract void onFailure(Exception e); + + /** + * 关闭响应 + */ + protected void closeResponse(Response response) { + EasyUtils.closeStream(response); + } } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/callback/DownloadCallback.java b/library/src/main/java/com/hjq/http/callback/DownloadCallback.java index 904bbe2..1a3896e 100644 --- a/library/src/main/java/com/hjq/http/callback/DownloadCallback.java +++ b/library/src/main/java/com/hjq/http/callback/DownloadCallback.java @@ -2,11 +2,14 @@ import android.text.TextUtils; +import androidx.annotation.NonNull; + import com.hjq.http.EasyLog; import com.hjq.http.EasyUtils; import com.hjq.http.config.IRequestInterceptor; -import com.hjq.http.exception.MD5Exception; +import com.hjq.http.exception.FileMD5Exception; import com.hjq.http.exception.NullBodyException; +import com.hjq.http.exception.ResponseException; import com.hjq.http.lifecycle.HttpLifecycleManager; import com.hjq.http.listener.OnDownloadListener; import com.hjq.http.request.HttpRequest; @@ -28,6 +31,7 @@ public final class DownloadCallback extends BaseCallback { /** 请求配置 */ + @NonNull private final HttpRequest mHttpRequest; /** 文件 MD5 正则表达式 */ @@ -51,7 +55,7 @@ public final class DownloadCallback extends BaseCallback { /** 下载进度 */ private int mDownloadProgress; - public DownloadCallback(HttpRequest request) { + public DownloadCallback(@NonNull HttpRequest request) { super(request); mHttpRequest = request; } @@ -73,12 +77,7 @@ public DownloadCallback setListener(OnDownloadListener listener) { @Override protected void onStart(Call call) { - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onStart(mFile); - }); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), this::callOnStart); } @Override @@ -92,12 +91,17 @@ protected void onResponse(Response response) throws Exception { response = interceptor.interceptResponse(mHttpRequest, response); } + if (!response.isSuccessful()) { + throw new ResponseException("The request failed, responseCode: " + + response.code() + ", message: " + response.message(), response); + } + // 如果没有指定文件的 md5 值 if (mMd5 == null) { // 获取响应头中的文件 MD5 值 String md5 = response.header("Content-MD5"); // 这个 md5 值必须是文件的 md5 值 - if (!TextUtils.isEmpty(md5) && md5.matches(FILE_MD5_REGEX)) { + if (md5 != null && md5.matches(FILE_MD5_REGEX)) { mMd5 = md5; } } @@ -119,15 +123,9 @@ protected void onResponse(Response response) throws Exception { // 如果这个文件已经下载过,并且经过校验 MD5 是同一个文件的话,就直接回调下载成功监听 if (!TextUtils.isEmpty(mMd5) && mFile.isFile() && mMd5.equalsIgnoreCase(EasyUtils.getFileMd5(EasyUtils.openFileInputStream(mFile)))) { - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onComplete(mFile, true); - mListener.onEnd(mFile); - // 文件已存在,跳过下载 - EasyLog.printLog(mHttpRequest, mFile.getPath() + " file already exists, skip download"); - }); + // 文件已存在,跳过下载 + EasyLog.printLog(mHttpRequest, mFile.getPath() + " file already exists, skip download"); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> callOnComplete(true)); return; } @@ -139,21 +137,7 @@ protected void onResponse(Response response) throws Exception { while ((readLength = inputStream.read(bytes)) != -1) { mDownloadByte += readLength; outputStream.write(bytes, 0, readLength); - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onByte(mFile, mTotalByte, mDownloadByte); - int progress = EasyUtils.getProgressProgress(mTotalByte, mDownloadByte); - // 只有下载进度发生改变的时候才回调此方法,避免引起不必要的 View 重绘 - if (progress != mDownloadProgress) { - mDownloadProgress = progress; - mListener.onProgress(mFile, mDownloadProgress); - EasyLog.printLog(mHttpRequest, mFile.getPath() + - ", downloaded: " + mDownloadByte + " / " + mTotalByte + - ", progress: " + progress + " %"); - } - }); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), this::callOnProgress); } EasyUtils.closeStream(inputStream); EasyUtils.closeStream(outputStream); @@ -161,34 +145,62 @@ protected void onResponse(Response response) throws Exception { String md5 = EasyUtils.getFileMd5(EasyUtils.openFileInputStream(mFile)); if (!TextUtils.isEmpty(mMd5) && !mMd5.equalsIgnoreCase(md5)) { // 文件 MD5 值校验失败 - throw new MD5Exception("MD5 verify failure", md5); + throw new FileMD5Exception("MD5 verify failure", md5); } - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onComplete(mFile, false); - mListener.onEnd(mFile); - }); + EasyLog.printLog(mHttpRequest, mFile.getPath() + " download completed"); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> callOnComplete(false)); } @Override protected void onFailure(final Exception e) { EasyLog.printThrowable(mHttpRequest, e); // 打印错误堆栈 - final Exception finalException = mHttpRequest.getRequestHandler().requestFail(mHttpRequest, e); + final Exception finalException = mHttpRequest.getRequestHandler().downloadFail(mHttpRequest, e); if (finalException != e) { EasyLog.printThrowable(mHttpRequest, finalException); } - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onError(mFile, finalException); - mListener.onEnd(mFile); - EasyLog.printLog(mHttpRequest, mFile.getPath() + " download completed"); - }); + EasyLog.printLog(mHttpRequest, mFile.getPath() + " download error"); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> callOnError(finalException)); + } + + private void callOnStart() { + if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { + return; + } + mListener.onStart(mFile); + } + + private void callOnProgress() { + if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { + return; + } + mListener.onByte(mFile, mTotalByte, mDownloadByte); + int progress = EasyUtils.getProgressProgress(mTotalByte, mDownloadByte); + // 只有下载进度发生改变的时候才回调此方法,避免引起不必要的 View 重绘 + if (progress != mDownloadProgress) { + mDownloadProgress = progress; + mListener.onProgress(mFile, mDownloadProgress); + EasyLog.printLog(mHttpRequest, mFile.getPath() + + ", downloaded: " + mDownloadByte + " / " + mTotalByte + + ", progress: " + progress + " %"); + } + } + + private void callOnComplete(boolean cache) { + if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { + return; + } + mListener.onComplete(mFile, cache); + mListener.onEnd(mFile); + } + + private void callOnError(Exception e) { + if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { + return; + } + mListener.onError(mFile, e); + mListener.onEnd(mFile); } } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/callback/NormalCallback.java b/library/src/main/java/com/hjq/http/callback/NormalCallback.java index 619ee7e..60904a2 100644 --- a/library/src/main/java/com/hjq/http/callback/NormalCallback.java +++ b/library/src/main/java/com/hjq/http/callback/NormalCallback.java @@ -1,5 +1,7 @@ package com.hjq.http.callback; +import androidx.annotation.NonNull; + import com.hjq.http.EasyLog; import com.hjq.http.EasyUtils; import com.hjq.http.config.IRequestInterceptor; @@ -9,10 +11,12 @@ import com.hjq.http.request.HttpRequest; import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.Type; import okhttp3.Call; import okhttp3.Response; +import okhttp3.ResponseBody; /** * author : Android 轮子哥 @@ -30,7 +34,7 @@ public final class NormalCallback extends BaseCallback { /** 解析类型 */ private Type mReflectType; - public NormalCallback(HttpRequest request) { + public NormalCallback(@NonNull HttpRequest request) { super(request); mHttpRequest = request; } @@ -62,13 +66,9 @@ public void start() { } // 读取缓存成功 - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onStart(getCall()); - mListener.onSucceed(result, true); - mListener.onEnd(getCall()); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> { + onStart(getCall()); + callOnSucceed(result, true); }); // 如果当前模式是先读缓存再写请求 @@ -92,12 +92,7 @@ public void start() { @Override protected void onStart(Call call) { - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onStart(call); - }); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), this::callOnStart); } @Override @@ -128,13 +123,7 @@ protected void onResponse(Response response) throws Exception { } } - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onSucceed(result, false); - mListener.onEnd(getCall()); - }); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> callOnSucceed(result, false)); } @Override @@ -148,13 +137,7 @@ protected void onFailure(Exception exception) { mReflectType, mHttpRequest.getRequestCache().getCacheTime()); EasyLog.printLog(mHttpRequest, "ReadCache result:" + result); if (result != null) { - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onSucceed(result, true); - mListener.onEnd(getCall()); - }); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> callOnSucceed(result, true)); return; } } catch (Exception cacheException) { @@ -168,12 +151,40 @@ protected void onFailure(Exception exception) { EasyLog.printThrowable(mHttpRequest, finalException); } - EasyUtils.post(() -> { - if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { - return; - } - mListener.onFail(finalException); - mListener.onEnd(getCall()); - }); + EasyUtils.runOnAssignThread(mHttpRequest.getThreadSchedulers(), () -> callOnFail(finalException)); + } + + private void callOnStart() { + if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { + return; + } + mListener.onStart(getCall()); + } + + private void callOnSucceed(Object result, boolean cache) { + if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { + return; + } + mListener.onSucceed(result, cache); + mListener.onEnd(getCall()); + } + + private void callOnFail(Exception e) { + if (mListener == null || !HttpLifecycleManager.isLifecycleActive(mHttpRequest.getLifecycleOwner())) { + return; + } + mListener.onFail(e); + mListener.onEnd(getCall()); + } + + @Override + protected void closeResponse(Response response) { + if (Response.class.equals(mReflectType) || + ResponseBody.class.equals(mReflectType) || + InputStream.class.equals(mReflectType)) { + // 如果反射是这几个类型,则不关闭 Response,否则会导致拉取不到里面的流 + return; + } + super.closeResponse(response); } } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/config/IRequestHandler.java b/library/src/main/java/com/hjq/http/config/IRequestHandler.java index 931742f..3573a07 100644 --- a/library/src/main/java/com/hjq/http/config/IRequestHandler.java +++ b/library/src/main/java/com/hjq/http/config/IRequestHandler.java @@ -41,6 +41,18 @@ public interface IRequestHandler { @NonNull Exception requestFail(@NonNull HttpRequest httpRequest, @NonNull Exception e); + /** + * 下载失败 + * + * @param httpRequest 请求接口对象 + * @param e 错误对象 + * @return 错误对象 + */ + @NonNull + default Exception downloadFail(@NonNull HttpRequest httpRequest, @NonNull Exception e) { + return requestFail(httpRequest, e); + } + /** * 读取缓存 * diff --git a/library/src/main/java/com/hjq/http/exception/MD5Exception.java b/library/src/main/java/com/hjq/http/exception/FileMD5Exception.java similarity index 71% rename from library/src/main/java/com/hjq/http/exception/MD5Exception.java rename to library/src/main/java/com/hjq/http/exception/FileMD5Exception.java index f83a415..379f977 100644 --- a/library/src/main/java/com/hjq/http/exception/MD5Exception.java +++ b/library/src/main/java/com/hjq/http/exception/FileMD5Exception.java @@ -1,21 +1,21 @@ -package com.hjq.http.exception; - -/** - * author : Android 轮子哥 - * github : https://github.com/getActivity/EasyHttp - * time : 2019/11/16 - * desc : MD5 校验异常 - */ -public final class MD5Exception extends HttpException { - - private final String mMD5; - - public MD5Exception(String message, String md5) { - super(message); - mMD5 = md5; - } - - public String getMD5() { - return mMD5; - } +package com.hjq.http.exception; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2019/11/16 + * desc : MD5 校验异常 + */ +public final class FileMD5Exception extends HttpException { + + private final String mMD5; + + public FileMD5Exception(String message, String md5) { + super(message); + mMD5 = md5; + } + + public String getMD5() { + return mMD5; + } } \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/exception/HttpException.java b/library/src/main/java/com/hjq/http/exception/HttpException.java index b7da055..c3eab48 100644 --- a/library/src/main/java/com/hjq/http/exception/HttpException.java +++ b/library/src/main/java/com/hjq/http/exception/HttpException.java @@ -11,7 +11,7 @@ */ public class HttpException extends Exception { - private final String mMessage; + private String mMessage; private Throwable mThrowable; public HttpException(String message) { @@ -25,6 +25,10 @@ public HttpException(String message, Throwable cause) { mThrowable = cause; } + public void setMessage(String message) { + mMessage = message; + } + /** * 获取错误信息 */ diff --git a/library/src/main/java/com/hjq/http/lifecycle/ApplicationLifecycle.java b/library/src/main/java/com/hjq/http/lifecycle/ApplicationLifecycle.java index ecb8b72..d412389 100644 --- a/library/src/main/java/com/hjq/http/lifecycle/ApplicationLifecycle.java +++ b/library/src/main/java/com/hjq/http/lifecycle/ApplicationLifecycle.java @@ -13,8 +13,16 @@ */ public final class ApplicationLifecycle implements LifecycleOwner { + private static final ApplicationLifecycle INSTANCE = new ApplicationLifecycle(); + + public static ApplicationLifecycle getInstance() { + return INSTANCE; + } + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); + private ApplicationLifecycle() {} + @NonNull @Override public Lifecycle getLifecycle() { diff --git a/library/src/main/java/com/hjq/http/model/ThreadSchedulers.java b/library/src/main/java/com/hjq/http/model/ThreadSchedulers.java new file mode 100644 index 0000000..3792bcc --- /dev/null +++ b/library/src/main/java/com/hjq/http/model/ThreadSchedulers.java @@ -0,0 +1,16 @@ +package com.hjq.http.model; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2022/06/18 + * desc : 线程调度器 + */ +public enum ThreadSchedulers { + + /** 主线程 */ + MainThread, + + /** IO 线程 */ + IOThread +} \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/request/BodyRequest.java b/library/src/main/java/com/hjq/http/request/BodyRequest.java index 3f3019f..7cec798 100644 --- a/library/src/main/java/com/hjq/http/request/BodyRequest.java +++ b/library/src/main/java/com/hjq/http/request/BodyRequest.java @@ -275,19 +275,17 @@ private void addFormData(MultipartBody.Builder bodyBuilder, String key, Object o if (TextUtils.isEmpty(fileName)) { fileName = file.getName(); } - // 文件名必须不能带中文,所以这里要编码 - String encodeFileName = EasyUtils.encodeString(fileName); try { MultipartBody.Part part; if (file instanceof FileContentResolver) { FileContentResolver fileContentResolver = (FileContentResolver) file; InputStream inputStream = fileContentResolver.openInputStream(); - part = MultipartBody.Part.createFormData(key, encodeFileName, new UpdateBody( + part = MultipartBody.Part.createFormData(key, fileName, new UpdateBody( Okio.source(inputStream), fileContentResolver.getContentType(), fileName, inputStream.available())); } else { - part = MultipartBody.Part.createFormData(key, encodeFileName, new UpdateBody(file)); + part = MultipartBody.Part.createFormData(key, fileName, new UpdateBody(file)); } bodyBuilder.addPart(part); } catch (FileNotFoundException e) { @@ -312,8 +310,8 @@ private void addFormData(MultipartBody.Builder bodyBuilder, String key, Object o // 如果这是一个自定义的 RequestBody 对象 RequestBody requestBody = (RequestBody) object; if (requestBody instanceof UpdateBody) { - bodyBuilder.addPart(MultipartBody.Part.createFormData(key, EasyUtils.encodeString( - ((UpdateBody) requestBody).getKeyName()), requestBody)); + bodyBuilder.addPart(MultipartBody.Part.createFormData(key, + ((UpdateBody) requestBody).getKeyName(), requestBody)); } else { bodyBuilder.addPart(MultipartBody.Part.createFormData(key, null, requestBody)); } diff --git a/library/src/main/java/com/hjq/http/request/DeleteBodyRequest.java b/library/src/main/java/com/hjq/http/request/DeleteBodyRequest.java new file mode 100644 index 0000000..cdda8b5 --- /dev/null +++ b/library/src/main/java/com/hjq/http/request/DeleteBodyRequest.java @@ -0,0 +1,25 @@ +package com.hjq.http.request; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; + +import com.hjq.http.model.HttpMethod; + +/** + * author : Android 轮子哥 + * github : https://github.com/getActivity/EasyHttp + * time : 2020/10/07 + * desc : Delete 请求(参数使用 Body 传递) + */ +public final class DeleteBodyRequest extends BodyRequest { + + public DeleteBodyRequest(LifecycleOwner lifecycleOwner) { + super(lifecycleOwner); + } + + @NonNull + @Override + public String getRequestMethod() { + return HttpMethod.DELETE.toString(); + } +} \ No newline at end of file diff --git a/library/src/main/java/com/hjq/http/request/HttpRequest.java b/library/src/main/java/com/hjq/http/request/HttpRequest.java index 522ea43..bc37243 100644 --- a/library/src/main/java/com/hjq/http/request/HttpRequest.java +++ b/library/src/main/java/com/hjq/http/request/HttpRequest.java @@ -30,13 +30,11 @@ import com.hjq.http.model.HttpHeaders; import com.hjq.http.model.HttpParams; import com.hjq.http.model.ResponseClass; +import com.hjq.http.model.ThreadSchedulers; import java.io.IOException; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -72,6 +70,8 @@ public abstract class HttpRequest> { private IRequestHandler mRequestHandler = EasyConfig.getInstance().getHandler(); /** 请求拦截策略 */ private IRequestInterceptor mRequestInterceptor = EasyConfig.getInstance().getInterceptor(); + /** 线程调度器 */ + private ThreadSchedulers mThreadSchedulers = EasyConfig.getInstance().getThreadSchedulers(); /** 请求执行代理类 */ private CallProxy mCallProxy; @@ -194,6 +194,14 @@ public T tag(String tag) { return (T) this; } + /** + * 设置线程调度器 + */ + public T schedulers(@NonNull ThreadSchedulers schedulers) { + mThreadSchedulers = schedulers; + return (T) this; + } + /** * 创建连接对象 */ @@ -205,15 +213,8 @@ protected Call createCall() { HttpParams params = new HttpParams(); HttpHeaders headers = new HttpHeaders(); - List fields = new ArrayList<>(); - - Class clazz = mRequestApi.getClass(); - do { - Field[] declaredFields = clazz.getDeclaredFields(); - fields.addAll(0, Arrays.asList(declaredFields)); - // 遍历获取父类的字段 - clazz = clazz.getSuperclass(); - } while (clazz != null && !Object.class.equals(clazz)); + // 反射获取类的所有字段 + List fields = EasyUtils.getAllFields(mRequestApi.getClass()); // 当前请求是否存在流参数 params.setMultipart(EasyUtils.isMultipartParameter(fields)); @@ -228,10 +229,7 @@ protected Call createCall() { // 允许访问私有字段 field.setAccessible(true); - int modifiers = field.getModifiers(); - // 如果这是一个常量字段,则直接忽略掉,例如 Parcelable 接口中的 CREATOR 字段 - // https://github.com/getActivity/EasyHttp/issues/112 - if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + if (EasyUtils.isConstantField(field)) { continue; } @@ -246,8 +244,10 @@ protected Call createCall() { key = annotation.value(); } else { key = field.getName(); - // 如果是内部类则会出现一个字段名为 this$0 的外部类对象,会导致无限递归,这里要忽略掉,如果使用静态内部类则不会出现这个问题 - // 和规避 Kotlin 自动生成的伴生对象:https://github.com/getActivity/EasyHttp/issues/15 + // 如果是内部类则会出现一个字段名为 this$0 的外部类对象,会导致无限递归 + // 这里要忽略掉,如果使用静态内部类则不会出现这个问题 + // 另外还要规避 Kotlin 自动生成的伴生对象: + // https://github.com/getActivity/EasyHttp/issues/15 if (key.matches("this\\$\\d+") || "Companion".equals(key)) { continue; } @@ -508,6 +508,14 @@ public IRequestInterceptor getRequestInterceptor() { return mRequestInterceptor; } + /** + * 获取当前线程的调度器 + */ + @NonNull + public ThreadSchedulers getThreadSchedulers() { + return mThreadSchedulers; + } + /** * 获取延迟请求时间 */