diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index b80f96a5..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,13 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -custom: https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa6d3941c2c944e59831640fa0ece60d~tplv-k3u1fbpfcp-watermark.image diff --git a/.gitignore b/.gitignore index 4bb8246c..dc36cf4a 100644 --- a/.gitignore +++ b/.gitignore @@ -76,6 +76,8 @@ fastlane/readme.md .idea +app/src/androidTest/java/com/example/httpsender/ExampleInstrumentedTest.java + gradlew.bat gradlew diff --git a/LICENSE b/LICENSE index 1f653504..261eeb9e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +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. + 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.md b/README.md index 846319dd..dc50b725 100644 --- a/README.md +++ b/README.md @@ -1,405 +1,112 @@ -# RxHttp +[ ![Download](https://api.bintray.com/packages/32774707/maven/rxhttp/images/download.svg) ](https://bintray.com/32774707/maven/rxhttp/_latestVersion) -English | [中文文档](https://github.com/liujingxing/rxhttp/blob/master/README_zh.md) +# RxHttp主要优势 -[![](https://jitpack.io/v/liujingxing/rxhttp.svg)](https://jitpack.io/#liujingxing/rxhttp) - -# [(RxHttp 3.0 更新指南,升级必看)](https://github.com/liujingxing/rxhttp/wiki/RxHttp-3.0-%E6%9B%B4%E6%96%B0%E6%8C%87%E5%8D%97%EF%BC%8C%E5%8D%87%E7%BA%A7%E5%BF%85%E7%9C%8B) - -# 使用RxHttp的知名App - - - -抖音旗下***汽水音乐***(app v18.1.0 设置/关于汽水音乐/开源软件声明 可查) - - - - -# A type-safe HTTP client for Android. Written based on OkHttp - - -![sequence_chart_en.jpg](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e25f9c6aa694b57bd43871eff95cecd~tplv-k3u1fbpfcp-watermark.image) - - - - - - - - - - - - + ***3. 史上最优雅的处理多个BaseUrl及动态BaseUrl*** - - - - - + ***7. 支持Get、Post、Put、Delete等任意请求方式,可自定义请求方式*** - - -
AwaitFlowRxJava
(Kotlin)
RxJava
(Java)
+ ***1. 30秒即可上手,学习成本极低*** - ```java - //await return User - //tryAwait return User? - val user = RxHttp.get("/server/..") - .add("key", "value") - .toAwait() - .await() + ***2. 史上最优雅的处理网络缓存*** - //or awaitResult return kotlin.Result - RxHttp.get("/server/..") - .add("key", "value") - .toAwait() - .awaitResult { - //Success - }.onFailure { - //Failure - } - ``` - + ***4. 史上最优雅的对错误统一处理,且不打破Lambda表达式*** - ```java - RxHttp.get("/server/..") - .add("key", "value") - .toFlow() - .catch { - //Failure - }.collect { - //Success - } - ``` - + ***5. 史上最优雅的实现文件上传/下载及进度的监听,且支持断点下载*** - ```java - RxHttp.get("/server/..") - .add("key", "value") - .toObservable() - .subscribe({ - //Success - }, { - //Failure - }) - ``` - + ***6. 支持Gson、Xml、ProtoBuf、FastJson等第三方数据解析工具*** - ```java - RxHttp.get("/server/..") - .add("key", "value") - .toObservable(User.class) - .subscribe(user - > { - //Success - }, throwable -> { - //Failure - }) - ``` -
- - - -## 1、Feature - -- Support kotlin coroutines, RxJava2, RxJava3 - -- Support Gson, Xml, ProtoBuf, FastJson and other third-party data parsing tools - -- Supports automatic closure of requests in FragmentActivity, Fragment, View, ViewModel, and any class - -- Support global encryption and decryption, add common parameters and headers, network cache, all support a request set up separately - -## 2、usage - -1、Adding dependencies and configurations + ***8. 支持在Activity/Fragment/View/ViewModel/任意类中,自动关闭请求*** + + ***9. 支持统一加解密,且可对单个请求设置是否加解密*** + + ***10. 支持添加公共参数/头部,且可对单个请求设置是否添加公共参数/头部*** -### Required +**Gradle依赖** -
-1、Add jitpack to your build.gradle - ```java -allprojects { - repositories { - maven { url "https://jitpack.io" } - } -} -``` -
-
-2、Java 8 or higher - -```java -android { - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} -``` -
-
-3、Add RxHttp dependency - -```kotlin -plugins { - // kapt/ksp choose one - // id 'kotlin-kapt' - id 'com.google.devtools.ksp' version '2.3.4' -} - dependencies { - def rxhttp_version = '3.5.1' - implementation 'com.squareup.okhttp3:okhttp:4.12.0' - implementation "com.github.liujingxing.rxhttp:rxhttp:$rxhttp_version" - // ksp/kapt/annotationProcessor choose one - ksp "com.github.liujingxing.rxhttp:rxhttp-compiler:$rxhttp_version" - } -``` -
- -### Optional - -### 1、Converter -```kotlin -implementation "com.github.liujingxing.rxhttp:converter-serialization:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-fastjson:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-jackson:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-moshi:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-protobuf:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-simplexml:$rxhttp_version" -``` + implementation 'com.rxjava.rxhttp:rxhttp:1.4.0' + annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.4.0' //注解处理器,生成RxHttp类 -### 2、RxJava -
-RxHttp + RxJava3 - - ```java -implementation 'io.reactivex.rxjava3:rxjava:3.1.6' -implementation 'io.reactivex.rxjava3:rxandroid:3.0.2' -implementation 'com.github.liujingxing.rxlife:rxlife-rxjava3:2.2.2' //RxJava3, Automatic close request -``` - -
-
-RxHttp + RxJava2 - -```java -implementation 'io.reactivex.rxjava2:rxjava:2.2.8' -implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' -implementation 'com.github.liujingxing.rxlife:rxlife-rxjava2:2.2.2' //RxJava2, Automatic close request -``` -
+ implementation 'com.rxjava.rxlife:rxlife:1.1.0' //页面销毁,关闭请求,非必须 + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' //切换主线程,非必须 -
-ksp passes the RxJava version - -```java -ksp { - arg("rxhttp_rxjava", "3.1.6") + //Converter 根据自己需求选择 非必须 RxHttp默认内置了GsonConverter + implementation 'com.rxjava.rxhttp:converter-jackson:1.4.0' + implementation 'com.rxjava.rxhttp:converter-fastjson:1.4.0' + implementation 'com.rxjava.rxhttp:converter-protobuf:1.4.0' + implementation 'com.rxjava.rxhttp:converter-simplexml:1.4.0' } ``` - -
+`注:kotlin用户,请使用kapt替代annotationProcessor` -
-Kapt passes the RxJava version - -```java -kapt { - arguments { - arg("rxhttp_rxjava", "3.1.6") - } -} -``` - -
- -
-javaCompileOptions passes the RxJava version - -```java -android { - defaultConfig { - javaCompileOptions { - annotationProcessorOptions { - arguments = [ - rxhttp_rxjava: '3.1.6', - ] - } - } - } -} -``` - -
+***RxHttp&RxLife 交流群:378530627*** +## 准备工作 -### 3、set RxHttp class package name +**RxHttp 要求项目使用Java 8,请在 app 的 build.gradle 添加以下代码** -
-ksp pass package name - ```java -ksp { - arg("rxhttp_package", "rxhttp.xxx") +compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } ``` - -
-kapt pass package name - -```java -kapt { - arguments { - arg("rxhttp_package", "rxhttp.xxx") - } -} -``` - -
- -
-javaCompileOptions pass package name - -```java -android { - defaultConfig { - javaCompileOptions { - annotationProcessorOptions { - arguments = [ - rxhttp_package: 'rxhttp.xxx' - ] - } - } - } -} -``` -
+此时rebuild一下项目,就能看到RxHttp类了,到这,准备工作完毕,即可直接调用RxHttp发送请求了。 +## 上手教程 -**Finally, rebuild the project, which is necessary** +30秒上手教程:https://juejin.im/post/5cfcbbcbe51d455a694f94df -2、Initialize the SDK +掘金详细文档:https://juejin.im/post/5ded221a518825125d14a1d4 -This step is optional +wiki详细文档:https://github.com/liujingxing/okhttp-RxHttp/wiki (此文档会持续更新) -```java -RxHttpPlugins.init(OkHttpClient) - .setDebug(boolean) - .setOnParamAssembly(Consumer) - .... -``` +自动关闭请求用到的RxLife类,详情请查看[RxLife库](https://github.com/liujingxing/RxLife) -3、Configuration BaseUrl +## API兼容 -This step is optional +RxHttp最低要求为API 15,但是由于内部依赖OkHttp 3.14.1版本, 最低要求为API 21。 +如果你要的项目要兼容到API 15,请将RxHttp内部的OkHttp剔除,并引入低版本的OkHttp,如下: -```java -public class Url { - - //Add the @defaultDomain annotation to BASE_URL - @DefaultDomain - public static BASE_URL = "https://..." +``` +implementation('com.rxjava.rxhttp:rxhttp:x.x.x') { //xxx为RxHttp最新版本 + exclude group: "com.squareup.okhttp3" } +implementation 'com.squareup.okhttp3:okhttp:3.12.6' //此版本最低要求 API 9 ``` -4、Perform the requested +## 混淆 + +RxHttp作为开源库,可混淆,也可不混淆,如果不希望被混淆,请在proguard-rules.pro文件添加以下代码 ```java -// java -RxHttp.get("/service/...") //1、You can choose get,postFrom,postJson etc - .addQuery("key", "value") //add query param - .addHeader("headerKey", "headerValue") //add request header - .toObservable(Student.class) //2、Use the toXxx method to determine the return value type, customizable - .subscribe(student -> { //3、Subscribing observer - //Success callback,Default IO thread - }, throwable -> { - //Abnormal callback - }); - -// kotlin -RxHttp.postForm("/service/...") //post FormBody - .add("key", "value") //add param to body - .addQuery("key1", "value1") //add query param - .addFile("file", File(".../1.png")) //add file to body - .toObservable() - .subscribe({ student -> - //Default IO thread - }, { throwable -> - - }) - -// kotlin coroutine -val students = RxHttp.postJson("/service/...") //1、post {application/json; charset=utf-8} - .toAwaitList() //2、Use the toXxx method to determine the return value type, customizable - .await() //3、Get the return value, await is the suspend method +-keep class rxhttp.**{*;} ``` -## 3、Advanced usage +## 小技巧 - 1、Close the request +在这教大家一个小技巧,由于使用RxHttp发送请求都遵循请求三部曲,故我们可以在android studio 设置代码模版,如下 -```java -//In Rxjava2 , Automatic close request -RxHttp.get("/service/...") - .toObservableString() - .as(RxLife.as(this)) //The Activity destroys and automatically closes the request - .subscribe(s -> { - //Default IO thread - }, throwable -> { - - }); - -//In Rxjava3 , Automatic close request -RxHttp.get("/service/...") - .toObservableString() - .to(RxLife.to(this)) //The Activity destroys and automatically closes the request - .subscribe(s -> { - //Default IO thread - }, throwable -> { - - }); - - -//In RxJava2/RxJava3, close the request manually -Disposable disposable = RxHttp.get("/service/...") - .toObservableString() - .subscribe(s -> { - //Default IO thread - }, throwable -> { - - }); - -disposable.dispose(); //Close the request at the appropriate time -``` +![image](https://github.com/liujingxing/RxHttp/blob/master/screen/templates.png) -## 4、ProGuard +如图设置好后,写代码时,输入rp,就会自动生成模版,如下: -If you are using RxHttp v2.2.8 or above the shrinking and obfuscation rules are included automatically. -Otherwise you must manually add the options in [rxhttp.pro](https://github.com/liujingxing/rxhttp/blob/master/rxhttp/src/main/resources/META-INF/proguard/rxhttp.pro). +![image](https://github.com/liujingxing/RxHttp/blob/master/screen/templates_demo.gif) -## 5、Donations -If this project helps you a lot and you want to support the project's development and maintenance of this project, feel free to scan the following QR code for donation. Your donation is highly appreciated. Thank you! +## Demo演示 + -![donations.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa6d3941c2c944e59831640fa0ece60d~tplv-k3u1fbpfcp-watermark.image?) +> 更多功能,请下载Demo体验 + +## Donations +如果它对你帮助很大,并且你很想支持库的后续开发和维护,那么你可以扫下方二维码随意打赏我,就当是请我喝杯咖啡或是啤酒,开源不易,感激不尽 + +![image](https://github.com/liujingxing/RxHttp/blob/master/screen/donations.jpeg) -# Licenses -``` -Copyright 2019 liujingxing -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_zh.md b/README_zh.md deleted file mode 100644 index 4a71418a..00000000 --- a/README_zh.md +++ /dev/null @@ -1,444 +0,0 @@ -# RxHttp - -[English](https://github.com/liujingxing/rxhttp/blob/master/README.md) | 中文文档 - -[![](https://jitpack.io/v/liujingxing/rxhttp.svg)](https://jitpack.io/#liujingxing/rxhttp) -![](https://img.shields.io/badge/API-9+-blue.svg) -[![](https://img.shields.io/badge/change-更新日志-success.svg)](https://github.com/liujingxing/rxhttp/wiki/%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97) -[![](https://img.shields.io/badge/FAQ-常见问题-success.svg)](https://github.com/liujingxing/rxhttp/wiki/FAQ) -[![](https://img.shields.io/badge/掘金-@刘一刀-blue.svg)](https://juejin.cn/user/272334612601559/posts) - - -# [(RxHttp 3.0 更新指南,升级必看)](https://github.com/liujingxing/rxhttp/wiki/RxHttp-3.0-%E6%9B%B4%E6%96%B0%E6%8C%87%E5%8D%97%EF%BC%8C%E5%8D%87%E7%BA%A7%E5%BF%85%E7%9C%8B) - -# 使用RxHttp的知名App - - - -抖音旗下***汽水音乐***(app v18.1.0 设置/关于汽水音乐/开源软件声明 可查) - - -***加我微信 ljx-studio 拉你进微信群(备注RxHttp)*** - -# 1、主要优势 - - ***1. 30秒即可上手,学习成本极低*** - - ***2. 史上最优雅的支持 Kotlin 协程*** - - ***3. 史上最优雅的处理多个BaseUrl及动态BaseUrl*** - - ***4. 史上最优雅的对错误统一处理,且不打破Lambda表达式*** - - ***5. 史上最优雅的文件上传/下载/断点下载/进度监听,已适配Android 10*** - - ***6. 支持Gson、Xml、ProtoBuf、FastJson等第三方数据解析工具*** - - ***7. 支持Get、Post、Put、Delete等任意请求方式,可自定义请求方式*** - - ***8. 支持在Activity/Fragment/View/ViewModel/任意类中,自动关闭请求*** - - ***9. 支持全局加解密、添加公共参数及头部、网络缓存,均支持对某个请求单独设置*** - -# 2、请求三部曲 - -![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/29bdab825c4f42c0983c58777f3af675~tplv-k3u1fbpfcp-watermark.image) - -***代码表示, -[toObservableXxx、toAwaitXxx、toFlowXxx方法介绍点这里](https://github.com/liujingxing/rxhttp/wiki/RxJava%E3%80%81Await%E3%80%81Flow-%E5%AF%B9%E5%BA%94%E7%9A%84-asXxx%E3%80%81toXxx%E3%80%81toFlowXxx%E6%96%B9%E6%B3%95%E4%BB%8B%E7%BB%8D)*** - - - - - - - - - - - - - - - - - - - - -
AwaitFlowRxJava
(Kotlin)
RxJava
(Java)
- - ```java - //同步式写法 - val user = RxHttp.get("/server/..") - .add("key", "value") - .toAwait() - .await() //tryAwait return User? - - //回调式写法 - RxHttp.get("/server/..") - .add("key", "value") - .toAwait() - .awaitResult { - //成功回调 - }.onFailure { - //异常回调 - } - ``` - - - ```java - RxHttp.get("/server/..") - .add("key", "value") - .toFlow() - .catch { - //异常回调 - }.collect { - //成功回调 - } - ``` - - - ```java - RxHttp.get("/server/..") - .add("key", "value") - .toObservable() - .subscribe({ - //成功回调 - }, { - //异常回调 - }) - ``` - - - ```java - RxHttp.get("/server/..") - .add("key", "value") - .toObservable(User.class) - .subscribe(user - > { - //成功回调 - }, throwable -> { - //异常回调 - }) - ``` -
- -***RxHttp与Retrofit对比*** - -| 功能说明 | RxHttp | [Retrofit](https://github.com/square/retrofit) | -| --- | :---: | :---: | -| 版本| v3.2.6| v2.9.0 | -| 状态| 维护中| 维护中 | -| 标准RESTful风格| ✅ | ✅ | -| 学习成本| 低 | 高| -| 扩展性| 高| 高| -| 源码大小| 73k | 75k | -| jar包大小| 210k | 123k | -| RxJava| RxJava ❌
RxJava2✅
RxJava3✅| RxJava ✅
RxJava2✅
RxJava3✅| -| Kotlin协程| ✅ | ✅ | -| Flow流| ✅ | ✅ | -| Converter| Gson✅
Jackson✅
fastJson✅
Moshi✅
Protobuf✅
simplexml✅
kotlinx.serialization✅
自定义✅
| Gson✅
Jackson✅
fastJson✅
Moshi✅
Protobuf✅
simplexml✅
kotlinx.serialization✅
自定义✅
| -| 关闭请求 | 手动✅
自动✅
批量✅| 手动✅
自动✅
批量✅ | -| 文件上传/下载/进度监听| ✅ | ❌需再次封装| -| Android 10分区存储| ✅ | ❌需再次封装| -| 公共参数| ✅| ❌需再次封装 | -| 多域名/动态域名| ✅好用 | ✅一般 | -| 日志打印| ✅| ✅ | -| Json数据格式化输出| ✅| ❌需再次封装 | -| 业务code统一判断| ✅ | ❌需再次封装| -| 请求缓存| ✅ | ❌需再次封装| -| 全局加解密| ✅ | ❌需再次封装 | -| 部分字段解密 | ✅ | ❌需再次封装 | - -**说明** - -也许你有会有疑问,RxHttp源码大小仅比retrofit大6k的情况下,jar包大小为何会大一倍多?功能太多导致的代码臃肿?并不是,而是由kotlin导致的,在RxHttp内部,为了支持`Await/Flow`,运用了大量的kotlin内联方法及扩展方法,这些方法在编译为字节码后,都会相对较大,其中[AwaitTransform.kt](https://github.com/liujingxing/rxhttp/blob/master/rxhttp/src/main/java/rxhttp/AwaitTransform.kt)、[CallFactoryToAwait.kt](https://github.com/liujingxing/rxhttp/blob/master/rxhttp/src/main/java/rxhttp/CallFactoryToAwait.kt)、[CallFactoryToFlow.kt](https://github.com/liujingxing/rxhttp/blob/master/rxhttp/src/main/java/rxhttp/CallFactoryToFlow.kt)这3个kotlin文件,编译字节码后,就接近70k - - -# 3、相关文档 - -30秒上手教程:[30秒上手新一代Http请求神器RxHttp](https://juejin.im/post/5cfcbbcbe51d455a694f94df) - -Flow文档:[RxHttp + Flow 三步搞定任意请求](https://juejin.cn/post/7017604875764629540) - -Await文档:[RxHttp ,比Retrofit 更优雅的协程体验](https://juejin.im/post/5e77604fe51d4527066eb81a#heading-2) - -RxJava及核心Api介绍:[RxHttp 让你眼前一亮的Http请求框架](https://juejin.im/post/5ded221a518825125d14a1d4) - -wiki详细文档:https://github.com/liujingxing/rxhttp/wiki (此文档会持续更新) - -## [(RxHttp 3.0 更新指南,升级必看)](https://github.com/liujingxing/rxhttp/wiki/RxHttp-3.0-%E6%9B%B4%E6%96%B0%E6%8C%87%E5%8D%97%EF%BC%8C%E5%8D%87%E7%BA%A7%E5%BF%85%E7%9C%8B) - -自动关闭请求用到的RxLife类,详情请查看[RxLife库](https://github.com/liujingxing/rxlife) - - -# 4、上手准备 - -***1、RxHttp依赖有3种方式,选择其中一种就好, -[ksp、kapt、annotationProcessor 如何选择点击这里](https://github.com/liujingxing/rxhttp/wiki/ksp%E3%80%81kapt%E3%80%81annotationProcessor-%E7%94%A8%E6%B3%95%E5%8F%8A%E5%8C%BA%E5%88%AB)*** - -***2、asXxx方法内部通过RxJava实现,如需使用,需额外依赖RxJava并告知RxHttp你依赖的Rxjava版本*** - -***3、RxHttp已适配`OkHttp 3.12.0 - v4.12.0`版本(4.3.0除外), 如需兼容21以下,请依赖`OkHttp 3.12.x`,该版本最低要求 API 9*** - -***4、[Maven依赖点击这里](https://github.com/liujingxing/rxhttp/blob/master/maven_dependency.md)*** - -## 4.1、必须 - -
-annotationProcessor依赖 - -```gradle -//1、项目的build.gradle文件 -allprojects { - repositories { - maven { url "https://jitpack.io" } - } -} -//2、java 8或更高 -android { - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} -//3、添加依赖 -dependencies { - def rxhttp_version = '3.5.1' - implementation 'com.squareup.okhttp3:okhttp:4.12.0' - implementation "com.github.liujingxing.rxhttp:rxhttp:$rxhttp_version" - annotationProcessor "com.github.liujingxing.rxhttp:rxhttp-compiler:$rxhttp_version" -} -``` - -
- -
-kapt依赖 - -```gradle -//1、项目的build.gradle文件 -allprojects { - repositories { - maven { url "https://jitpack.io" } - } -} -//2、java 8或更高 -android { - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} -//3、添加插件及依赖 -plugins { - id 'kotlin-kapt' -} - -dependencies { - def rxhttp_version = '3.5.1' - implementation 'com.squareup.okhttp3:okhttp:4.12.0' - implementation "com.github.liujingxing.rxhttp:rxhttp:$rxhttp_version" - kapt "com.github.liujingxing.rxhttp:rxhttp-compiler:$rxhttp_version" -} -``` - -
- -
-ksp依赖 - -```gradle -//1、项目的build.gradle文件 -allprojects { - repositories { - maven { url "https://jitpack.io" } - } -} -//2、java 8或更高,及配置sourceSets -android { - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} -//3、添加插件及依赖 -plugins { - id 'com.google.devtools.ksp' version '2.3.4' -} - -dependencies { - def rxhttp_version = '3.5.1' - implementation 'com.squareup.okhttp3:okhttp:4.12.0' - implementation "com.github.liujingxing.rxhttp:rxhttp:$rxhttp_version" - ksp "com.github.liujingxing.rxhttp:rxhttp-compiler:$rxhttp_version" -} -``` - -
- - - - -## 4.2、可选 - -### 4.2.1、配置RxJava - -如果你需要结合`toObservableXxx`方法发请求,就需要额外依赖`RxJava`,并且告知`rxhttp`你依赖的`RxJava`版本号 - -- ***依赖RxJava,RxJava2/RxJava3选其一*** - -```java -//RxJava3 -implementation 'io.reactivex.rxjava3:rxjava:3.1.6' -implementation 'io.reactivex.rxjava3:rxandroid:3.0.2' -implementation 'com.github.liujingxing.rxlife:rxlife-rxjava3:2.2.2' //管理RxJava3生命周期,页面销毁,关闭请求 - -//RxJava2 -implementation 'io.reactivex.rxjava2:rxjava:2.2.8' -implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' -implementation 'com.github.liujingxing.rxlife:rxlife-rxjava2:2.2.2' //管理RxJava2生命周期,页面销毁,关闭请求 -``` - -- ***通过ksp/kapt/annotationProcessor,其中一种方式传递RxJava版本号*** -
-通过ksp传递RxJava版本 - -```java -ksp { - arg("rxhttp_rxjava", "3.1.6") -} -``` - -
- -
-通过kapt传递RxJava版本 - -```java -kapt { - arguments { - arg("rxhttp_rxjava", "3.1.6") - } -} -``` - -
- -
-通过annotationProcessor传递RxJava版本 - -```java -android { - defaultConfig { - javaCompileOptions { - annotationProcessorOptions { - arguments = [ - //使用asXxx方法时必须,传入你依赖的RxJava版本 - rxhttp_rxjava: '3.1.6', - ] - } - } - } -} -``` - -
- - -### 4.2.2、配置Converter - -```kotlin -//非必须,根据自己需求选择 RxHttp默认内置了GsonConverter -implementation "com.github.liujingxing.rxhttp:converter-serialization:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-fastjson:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-jackson:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-moshi:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-protobuf:$rxhttp_version" -implementation "com.github.liujingxing.rxhttp:converter-simplexml:$rxhttp_version" -``` - -### 4.2.3、指定RxHttp相关类的存放目录 - -如果你有多个module依赖`rxhttp-compiler`(不建议这么做,一般base module依赖就好),则每个module下都会生成`RxHttp`类,且目录相同,在运行或打包时,就会出现RxHttp类冲突的问题,此时就需要你自定义RxHttp的存放目录,也就是RxHttp类的包名,`ksp/kapt/annotationProcessor`选择其中一种方式就好 - -
-通过ksp指定RxHttp相关类包名 - -```java -ksp { - arg("rxhttp_package", "rxhttp") //指定RxHttp类包名,可随意指定 -} -``` - -
- -
-通过kapt指定RxHttp相关类包名 - -```java - -kapt { - arguments { - arg("rxhttp_package", "rxhttp") //指定RxHttp类包名,可随意指定 - } -} -``` - -
- -
-通过javaCompileOptions指定RxHttp相关类包名 - -```java -android { - defaultConfig { - javaCompileOptions { - annotationProcessorOptions { - arguments = [ - rxhttp_package: 'rxhttp', //指定RxHttp类包名,可随意指定 - ] - } - } - } -} -``` -
- - -最后,***rebuild一下(此步骤是必须的)*** ,就会自动生成RxHttp类 - - -# 5、混淆 - -- `RxHttp v2.2.8`及以上版本,无需添加任何混淆规则,将你自己的Bean类Keep下就好 -- `RxHttp v2.2.8`以下版本,将[RxHttp 混淆规则](https://github.com/liujingxing/rxhttp/wiki/关于混淆),添加到自己项目中,并将你自己的Bean类Keep下 - -# 6、Demo演示 - - - -> 更多功能,请[下载apk](https://github.com/liujingxing/rxhttp/blob/master/screen/app-debug.apk)体验 - -# 7、Donations -如果它对你帮助很大,并且你很想支持库的后续开发和维护,那么你可以扫下方二维码随意打赏我,就当是请我喝杯咖啡或是啤酒,开源不易,感激不尽 - -![donations.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa6d3941c2c944e59831640fa0ece60d~tplv-k3u1fbpfcp-watermark.image?) - - -# Licenses -``` -Copyright 2019 liujingxing - -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/app/.gitignore b/app/.gitignore index 93c66cbb..67e07b8f 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,2 +1,2 @@ -#/build +/build /release diff --git a/app/build.gradle b/app/build.gradle index 8b2151d6..0230f981 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,27 +1,19 @@ -plugins { - alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) - id 'kotlin-kapt' - id 'com.google.devtools.ksp' version libs.versions.ksp -} +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-android-extensions' android { - - namespace 'com.example.httpsender' - - compileSdk 35 + compileSdkVersion 28 defaultConfig { applicationId "com.example.rxhttp" - minSdk 23 - targetSdk 35 + minSdkVersion 19 + targetSdkVersion 28 versionCode 1 versionName "1.0" multiDexEnabled true - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - buildTypes { release { minifyEnabled true @@ -34,82 +26,43 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } - buildFeatures { - buildConfig = true - dataBinding = true - } - lint { - abortOnError false - checkReleaseBuilds false + kotlinOptions { + jvmTarget = '1.8' } -} -ksp { - arg("rxhttp_rxjava", libs.versions.rxjava.get()) -// arg("rxhttp_package", "rxhttp") -} - -kapt { - arguments { - arg("rxhttp_rxjava", libs.versions.rxjava.get()) //可传入rxjava2、rxjava3或具体版本号,如 3.1.1 -// arg("rxhttp_package", "rxhttp") //设置RxHttp相关类的包名,多module依赖时,需要配置不同的包名 + dataBinding { + enabled = true } } - dependencies { - implementation libs.androidx.multidex - implementation libs.androidx.appcompat - implementation libs.androidx.recyclerview - implementation libs.androidx.constraintlayout - implementation libs.github.magicindicator - implementation libs.androidx.fragment.ktx - implementation libs.androidx.lifecycle.runtime.ktx - implementation libs.androidx.lifecycle.livedata.ktx - implementation libs.androidx.lifecycle.viewmodel.ktx - implementation libs.androidx.lifecycle.service - - implementation projects.rxhttp - ksp projects.rxhttpCompiler -// kapt projects.rxhttpCompiler - - implementation libs.okhttp - testImplementation libs.mockwebserver - -// implementation libs.rxhttp -// ksp libs.rxhttp.compiler -// kapt libs.rxhttp.compiler + implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') + implementation 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' //管理RxJava及生命周期,Activity/Fragment 销毁,自动关闭未完成的请求 - implementation libs.github.rxlife.rxjava3 - implementation libs.rxandroid - implementation libs.rxjava + implementation 'com.rxjava.rxlife:rxlife-x:1.1.0' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' -// implementation libs.rxhttp.converter.serialization -// implementation libs.rxhttp.converter.fastjson -// implementation libs.rxhttp.converter.jackson -// implementation libs.rxhttp.converter.moshi -// implementation libs.rxhttp.converter.protobuf -// implementation libs.rxhttp.converter.simplexml +// implementation project(":rxhttp") +// kapt project(':rxhttp-compiler') - implementation projects.rxhttpConverter.converterSerialization - implementation projects.rxhttpConverter.converterFastjson - implementation projects.rxhttpConverter.converterSimplexml - implementation projects.rxhttpConverter.converterProtobuf - implementation projects.rxhttpConverter.converterMoshi - implementation projects.rxhttpConverter.converterJackson + implementation "com.rxjava.rxhttp:rxhttp:${rxhttp_version}" + kapt "com.rxjava.rxhttp:rxhttp-compiler:${rxhttp_version}" - testImplementation libs.junit - androidTestImplementation libs.androidx.junit - androidTestImplementation libs.androidx.espresso.core - implementation libs.utilcodex + implementation "com.rxjava.rxhttp:converter-jackson:${rxhttp_converter_version}" + implementation "com.rxjava.rxhttp:converter-fastjson:${rxhttp_converter_version}" + implementation "com.rxjava.rxhttp:converter-protobuf:${rxhttp_converter_version}" + implementation "com.rxjava.rxhttp:converter-simplexml:${rxhttp_converter_version}" - testImplementation libs.kotlin.compile.testing.ksp - testImplementation libs.kotlinpoet - testImplementation libs.kotlinpoet.ksp - testImplementation projects.rxhttpCompiler +// implementation project(':rxhttp-converter:converter-fastjson') +// implementation project(':rxhttp-converter:converter-simplexml') +// implementation project(':rxhttp-converter:converter-protobuf') +// implementation project(':rxhttp-converter:converter-jackson') } diff --git a/app/build/generated/ksp/debug/java/rxhttp/wrapper/param/ObservableCall.java b/app/build/generated/ksp/debug/java/rxhttp/wrapper/param/ObservableCall.java deleted file mode 100644 index b59437bd..00000000 --- a/app/build/generated/ksp/debug/java/rxhttp/wrapper/param/ObservableCall.java +++ /dev/null @@ -1,242 +0,0 @@ -package rxhttp.wrapper.param; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; - -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.core.Scheduler; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import io.reactivex.rxjava3.schedulers.Schedulers; -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.Response; -import rxhttp.wrapper.BodyParamFactory; -import rxhttp.wrapper.CallFactory; -import rxhttp.wrapper.callback.ProgressCallback; -import rxhttp.wrapper.entity.OkResponse; -import rxhttp.wrapper.entity.Progress; -import rxhttp.wrapper.exception.ProxyException; -import rxhttp.wrapper.parse.OkResponseParser; -import rxhttp.wrapper.parse.Parser; -import rxhttp.wrapper.parse.StreamParser; -import rxhttp.wrapper.utils.LogUtil; - -/** - * User: ljx - * Date: 2020/9/5 - * Time: 21:59 - */ -public final class ObservableCall extends Observable { - - private final Parser parser; - private final CallFactory callFactory; - private boolean syncRequest = false; - //上传/下载进度回调最小周期, 值越小,回调事件越多,设置一个合理值,可避免密集回调 - private int minPeriod = Integer.MIN_VALUE; - - ObservableCall(CallFactory callFactory, Parser parser) { - this.callFactory = callFactory; - this.parser = parser; - } - - @Override - protected void subscribeActual(Observer observer) { - CallExecuteDisposable d = syncRequest ? new CallExecuteDisposable<>(observer, callFactory, parser) : - new CallEnqueueDisposable<>(observer, callFactory, parser); - observer.onSubscribe(d); - if (d.isDisposed()) { - return; - } - if (minPeriod != Integer.MIN_VALUE && observer instanceof ProgressCallback) { - ProgressCallback pc = (ProgressCallback) observer; - Parser parser = this.parser; - while (parser instanceof OkResponseParser) { - parser = ((OkResponseParser) parser).parser; - } - if (parser instanceof StreamParser) { - ((StreamParser) parser).setProgressCallback(minPeriod, pc); - } else if (callFactory instanceof BodyParamFactory) { - ((BodyParamFactory) callFactory).getParam().setProgressCallback(minPeriod, pc); - } - } - d.run(); - } - - @NotNull - public ObservableCall<@NotNull OkResponse<@Nullable T>> toObservableOkResponse() { - return new ObservableCall<>(callFactory, new OkResponseParser<>(parser)); - } - - @NotNull - public ObservableCall<@NotNull T> syncRequest() { - syncRequest = true; - return this; - } - - @NotNull - public Observable<@NotNull T> onProgress(@NotNull Consumer> progressConsumer) { - return onProgress(Schedulers.io(), progressConsumer); - } - - @NotNull - public Observable<@NotNull T> onProgress(@NotNull Scheduler scheduler, @NotNull Consumer> progressConsumer) { - return onProgress(2, scheduler, progressConsumer); - } - - @NotNull - public Observable<@NotNull T> onMainProgress(@NotNull Consumer> progressConsumer) { - return onMainProgress(2, progressConsumer); - } - - @NotNull - public Observable<@NotNull T> onMainProgress(int capacity, @NotNull Consumer> progressConsumer) { - return onProgress(capacity, AndroidSchedulers.mainThread(), progressConsumer); - } - - @NotNull - public Observable<@NotNull T> onMainProgress(int capacity, int minPeriod, @NotNull Consumer> progressConsumer) { - return onProgress(capacity, minPeriod, AndroidSchedulers.mainThread(), progressConsumer); - } - - @NotNull - public Observable<@NotNull T> onProgress(int capacity, @NotNull Scheduler scheduler, @NotNull Consumer> progressConsumer) { - return onProgress(capacity, 500, scheduler, progressConsumer); - } - - /** - * Upload or Download progress callback - * - * @param capacity queue size, must be in [2..100], is invalid when the scheduler is TrampolineScheduler - * @param minPeriod minimum period of progress callback, must be between 1 and {@link Integer.MAX_VALUE}, The default value is 500 milliseconds, - * @param scheduler the Scheduler to notify Observers on - * @param progressConsumer progress callback - * @return the new Observable instance - */ - @NotNull - public Observable<@NotNull T> onProgress(int capacity, int minPeriod, @NotNull Scheduler scheduler, @NotNull Consumer> progressConsumer) { - if (capacity < 2 || capacity > 100) { - throw new IllegalArgumentException("capacity must be in [2..100], but it was " + capacity); - } - if (minPeriod < 0) { - throw new IllegalArgumentException("minPeriod must be between 0 and Integer.MAX_VALUE, but it was " + minPeriod); - } - Objects.requireNonNull(scheduler, "scheduler is null"); - Parser streamParser = parser; - while (streamParser instanceof OkResponseParser) { - streamParser = ((OkResponseParser) streamParser).parser; - } - if (!(streamParser instanceof StreamParser) && !(callFactory instanceof BodyParamFactory)) { - throw new UnsupportedOperationException("parser is " + streamParser.getClass().getName() + ", callFactory is " + callFactory.getClass().getName()); - } - this.minPeriod = minPeriod; - return new ObservableProgress<>(this, capacity, scheduler, progressConsumer); - } - - private static class CallEnqueueDisposable extends CallExecuteDisposable implements Callback { - - CallEnqueueDisposable(Observer downstream, CallFactory callFactory, Parser parser) { - super(downstream, callFactory, parser); - } - - @Override - public void onResponse(@NotNull Call call, @NotNull Response response) { - try { - T t = Objects.requireNonNull(parser.onParse(response), "The onParse function returned a null value."); - if (!disposed) { - downstream.onNext(t); - } - if (!disposed) { - downstream.onComplete(); - } - } catch (Throwable t) { - onError(call, t); - } - } - - @Override - public void onFailure(@NotNull Call call, @NotNull IOException e) { - onError(call, e); - } - - @Override - public void run() { - try { - call = callFactory.newCall(); - call.enqueue(this); - } catch (Throwable e) { - onError(call, e); - } - } - } - - - private static class CallExecuteDisposable implements Disposable { - - protected final Observer downstream; - protected final Parser parser; - protected final CallFactory callFactory; - protected volatile boolean disposed; - protected Call call; - private final AtomicReference upstream; - - CallExecuteDisposable(Observer downstream, CallFactory callFactory, Parser parser) { - this.downstream = downstream; - this.callFactory = callFactory; - this.parser = parser; - upstream = new AtomicReference<>(); - } - - public void run() { - try { - call = callFactory.newCall(); - Response response = call.execute(); - T t = Objects.requireNonNull(parser.onParse(response), "The onParse function returned a null value."); - if (!disposed) { - downstream.onNext(t); - } - if (!disposed) { - downstream.onComplete(); - } - } catch (Throwable e) { - onError(call, e); - } - } - - void onError(@Nullable Call call, Throwable e) { - LogUtil.logCall(call, e); - Exceptions.throwIfFatal(e); - if (!disposed) { - downstream.onError(e); - } else { - RxJavaPlugins.onError(e); - } - } - - @Override - public void dispose() { - DisposableHelper.dispose(upstream); - disposed = true; - if (call != null) - call.cancel(); - } - - @Override - public boolean isDisposed() { - return disposed; - } - - public void setDisposable(Disposable d) { - DisposableHelper.setOnce(upstream, d); - } - } -} diff --git a/app/build/generated/ksp/debug/java/rxhttp/wrapper/param/ObservableProgress.java b/app/build/generated/ksp/debug/java/rxhttp/wrapper/param/ObservableProgress.java deleted file mode 100644 index cc0ed03b..00000000 --- a/app/build/generated/ksp/debug/java/rxhttp/wrapper/param/ObservableProgress.java +++ /dev/null @@ -1,287 +0,0 @@ -package rxhttp.wrapper.param; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Queue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; - -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.core.Scheduler; -import io.reactivex.rxjava3.core.Scheduler.Worker; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.exceptions.Exceptions; -import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.internal.disposables.DisposableHelper; -import io.reactivex.rxjava3.internal.schedulers.TrampolineScheduler; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; -import rxhttp.wrapper.callback.ProgressCallback; -import rxhttp.wrapper.entity.Progress; - -public final class ObservableProgress extends Observable { - - private final Observable source; - private final int capacity; - private final Scheduler scheduler; - private final Consumer> progressConsumer; - - ObservableProgress(Observable source, int capacity, Scheduler scheduler, Consumer> progressConsumer) { - this.source = source; - this.capacity = capacity; - this.scheduler = scheduler; - this.progressConsumer = progressConsumer; - } - - @Override - protected void subscribeActual(@NotNull Observer observer) { - if (scheduler instanceof TrampolineScheduler) { - source.subscribe(new SyncObserver<>(observer, progressConsumer)); - } else { - Worker worker = scheduler.createWorker(); - source.subscribe(new AsyncObserver<>(worker, observer, capacity, progressConsumer)); - } - } - - private static final class SyncObserver implements Observer, Disposable, ProgressCallback { - - private final Observer downstream; - private final Consumer> progressConsumer; - private Disposable upstream; - private boolean done; - - SyncObserver(Observer actual, Consumer> progressConsumer) { - this.downstream = actual; - this.progressConsumer = progressConsumer; - } - - @Override - public void onSubscribe(@NotNull Disposable d) { - if (DisposableHelper.validate(this.upstream, d)) { - this.upstream = d; - downstream.onSubscribe(this); - } - } - - // upload/download progress callback - @Override - public void onProgress(long currentSize, long totalSize, long speed) { - if (done) { - return; - } - try { - progressConsumer.accept(new Progress<>(currentSize, totalSize, speed)); - } catch (Throwable t) { - fail(t); - } - } - - @Override - public void onNext(@NotNull T t) { - if (done) { - return; - } - downstream.onNext(t); - } - - @Override - public void onError(@NotNull Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - return; - } - done = true; - downstream.onError(t); - } - - @Override - public void onComplete() { - if (done) { - return; - } - done = true; - downstream.onComplete(); - } - - @Override - public void dispose() { - upstream.dispose(); - } - - @Override - public boolean isDisposed() { - return upstream.isDisposed(); - } - - private void fail(Throwable t) { - Exceptions.throwIfFatal(t); - upstream.dispose(); - onError(t); - } - } - - - private static final class AsyncObserver extends AtomicInteger implements Observer, - Disposable, ProgressCallback, Runnable { - - private final Observer downstream; - private final Queue queue; - private final Scheduler.Worker worker; - private final Consumer> progressConsumer; - private Disposable upstream; - private Throwable error; - private volatile boolean done; - private volatile boolean disposed; - - AsyncObserver(Scheduler.Worker worker, Observer actual, int capacity, Consumer> progressConsumer) { - this.downstream = actual; - this.worker = worker; - this.progressConsumer = progressConsumer; - queue = new LinkedBlockingQueue<>(capacity); - } - - @Override - public void onSubscribe(@NotNull Disposable d) { - if (DisposableHelper.validate(this.upstream, d)) { - this.upstream = d; - downstream.onSubscribe(this); - } - } - - // upload/download progress callback - @Override - public void onProgress(long currentSize, long totalSize, long speed) { - if (done) { - return; - } - offer(new Progress<>(currentSize, totalSize, speed)); - } - - @Override - public void onNext(@NotNull T t) { - if (done) { - return; - } - offer(t); - } - - private void offer(Object o) { - while (!queue.offer(o)) { - queue.poll(); - } - schedule(); - } - - @Override - public void onError(@NotNull Throwable t) { - if (done) { - RxJavaPlugins.onError(t); - return; - } - error = t; - done = true; - schedule(); - } - - @Override - public void onComplete() { - if (done) { - return; - } - done = true; - schedule(); - } - - - void schedule() { - if (getAndIncrement() == 0) { - worker.schedule(this); - } - } - - @SuppressWarnings("unchecked") - @Override - public void run() { - int missed = 1; - - final Queue q = queue; - final Observer a = downstream; - while (!checkTerminated(done, q.isEmpty(), a)) { - for (; ; ) { - boolean d = done; - Object o; - try { - o = q.poll(); - - boolean empty = o == null; - - if (checkTerminated(d, empty, a)) { - return; - } - if (empty) { - break; - } - if (o instanceof Progress) { - progressConsumer.accept((Progress) o); - } else { - a.onNext((T) o); - } - } catch (Throwable ex) { - Exceptions.throwIfFatal(ex); - disposed = true; - upstream.dispose(); - q.clear(); - a.onError(ex); - worker.dispose(); - return; - } - } - missed = addAndGet(-missed); - if (missed == 0) { - break; - } - } - } - - boolean checkTerminated(boolean d, boolean empty, Observer a) { - if (isDisposed()) { - queue.clear(); - return true; - } - if (d) { - Throwable e = error; - if (e != null) { - disposed = true; - queue.clear(); - a.onError(e); - worker.dispose(); - return true; - } else if (empty) { - disposed = true; - a.onComplete(); - worker.dispose(); - return true; - } - } - return false; - } - - @Override - public void dispose() { - if (!disposed) { - disposed = true; - upstream.dispose(); - worker.dispose(); - if (getAndIncrement() == 0) { - queue.clear(); - } - } - } - - @Override - public boolean isDisposed() { - return disposed; - } - } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/BaseRxHttp.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/BaseRxHttp.kt deleted file mode 100644 index 3b924d4e..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/BaseRxHttp.kt +++ /dev/null @@ -1,124 +0,0 @@ -package rxhttp.wrapper.`param` - -import android.content.Context -import android.net.Uri -import com.example.httpsender.entity.PageList -import com.example.httpsender.parser.ResponseParser -import io.reactivex.rxjava3.plugins.RxJavaPlugins -import java.io.IOException -import java.lang.Class -import java.lang.reflect.Type -import kotlin.Boolean -import kotlin.String -import kotlin.Suppress -import kotlin.collections.List -import kotlin.jvm.JvmOverloads -import kotlin.jvm.Throws -import okhttp3.Response -import rxhttp.wrapper.CallFactory -import rxhttp.wrapper.ITag -import rxhttp.wrapper.callback.FileOutputStreamFactory -import rxhttp.wrapper.callback.OutputStreamFactory -import rxhttp.wrapper.callback.UriOutputStreamFactory -import rxhttp.wrapper.parse.OkResponseParser -import rxhttp.wrapper.parse.Parser -import rxhttp.wrapper.parse.SmartParser -import rxhttp.wrapper.parse.StreamParser -import rxhttp.wrapper.utils.LogUtil -import rxhttp.wrapper.utils.TypeUtil -import rxhttp.wrapper.utils.parameterizedBy - -/** - * User: ljx - * Date: 2020/4/11 - * Time: 18:15 - */ -public abstract class BaseRxHttp : ITag, CallFactory { - public fun toObservable(parser: Parser): ObservableCall = ObservableCall(this, parser) - - public fun toObservable(type: Type): ObservableCall = toObservable(SmartParser.wrap(type)) - - public fun toObservable(clazz: Class): ObservableCall = toObservable(clazz as Type) - - public fun toObservableString(): ObservableCall = toObservable(String::class.java) - - public fun toObservableList(clazz: Class): ObservableCall> { - val typeList = List::class.parameterizedBy(clazz) - return toObservable(typeList) - } - - @JvmOverloads - public fun toDownloadObservable(destPath: String, append: Boolean = false): ObservableCall = toDownloadObservable(FileOutputStreamFactory(destPath), append) - - @JvmOverloads - public fun toDownloadObservable( - context: Context, - uri: Uri, - append: Boolean = false, - ): ObservableCall = toDownloadObservable(UriOutputStreamFactory(context, uri), append) - - @JvmOverloads - public fun toDownloadObservable(osFactory: OutputStreamFactory, append: Boolean = false): ObservableCall { - if (append) { - tag(OutputStreamFactory::class.java, osFactory) - } - return toObservable(StreamParser(osFactory)) - } - - public fun toObservableResponse(type: Type): ObservableCall = toObservable(wrapResponseParser(type)) - - public fun toObservableResponse(type: Class): ObservableCall = toObservableResponse(type as Type) - - public fun toObservableResponseList(type: Class): ObservableCall> { - val typeList = List::class.parameterizedBy(type) - return toObservableResponse(typeList) - } - - public fun toObservableResponsePageList(type: Class): ObservableCall> { - val typePageList = PageList::class.parameterizedBy(type) - return toObservableResponse(typePageList) - } - - @Throws(IOException::class) - public fun execute(): Response = newCall().execute() - - @Throws(IOException::class) - public fun execute(parser: Parser): T = parser.onParse(execute()) - - @Throws(IOException::class) - public fun executeClass(type: Type): T = execute(SmartParser.wrap(type)) - - @Throws(IOException::class) - public fun executeClass(clazz: Class): T = executeClass(clazz as Type) - - @Throws(IOException::class) - public fun executeString(): String = executeClass(String::class.java) - - @Throws(IOException::class) - public fun executeList(clazz: Class): List { - val typeList = List::class.parameterizedBy(clazz) - return executeClass(typeList) - } - - public companion object { - init { - val errorHandler = RxJavaPlugins.getErrorHandler() - if (errorHandler == null) { - /* - RxJava的一个重要的设计理念是:不吃掉任何一个异常, 即抛出的异常无人处理,便会导致程序崩溃 - 这就会导致一个问题,当RxJava“downStream”取消订阅后,“upStream”仍有可能抛出异常, - 这时由于已经取消订阅,“downStream”无法处理异常,此时的异常无人处理,便会导致程序崩溃 - */ - RxJavaPlugins.setErrorHandler { LogUtil.logRxJavaError(it) } - } - } - - @Suppress("UNCHECKED_CAST") - public fun wrapResponseParser(type: Type): Parser { - val actualType = TypeUtil.getActualType(type) ?: type - val parser = ResponseParser(actualType) - val actualParser = if (actualType == type) parser else OkResponseParser(parser) - return actualParser as Parser - } - } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttp.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttp.kt deleted file mode 100644 index 776f4207..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttp.kt +++ /dev/null @@ -1,542 +0,0 @@ -package rxhttp.wrapper.`param` - -import com.example.httpsender.RxHttpManager.fastJsonConverter -import com.example.httpsender.RxHttpManager.simpleClient -import com.example.httpsender.RxHttpManager.xmlConverter -import com.example.httpsender.`param`.GetEncryptParam -import com.example.httpsender.`param`.PostEncryptFormParam -import com.example.httpsender.`param`.PostEncryptJsonParam -import com.example.httpsender.`param`.PostEncryptJsonParam1 -import com.example.httpsender.entity.Url.baseUrl -import java.lang.Class -import java.util.concurrent.TimeUnit -import kotlin.Any -import kotlin.Boolean -import kotlin.Long -import kotlin.String -import kotlin.Suppress -import kotlin.collections.List -import kotlin.collections.Map -import kotlin.jvm.JvmName -import kotlin.jvm.JvmOverloads -import kotlin.jvm.JvmStatic -import okhttp3.CacheControl -import okhttp3.Call -import okhttp3.Headers -import okhttp3.Headers.Builder -import okhttp3.OkHttpClient -import okhttp3.Request -import rxhttp.RxHttpPlugins -import rxhttp.wrapper.cache.CacheMode -import rxhttp.wrapper.cache.CacheStrategy -import rxhttp.wrapper.callback.IConverter -import rxhttp.wrapper.callback.OutputStreamFactory -import rxhttp.wrapper.entity.DownloadOffSize -import rxhttp.wrapper.intercept.CacheInterceptor -import rxhttp.wrapper.intercept.LogInterceptor -import rxhttp.wrapper.intercept.RangeInterceptor -import rxhttp.wrapper.utils.LogUtil - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - * https://github.com/liujingxing/rxhttp/wiki/FAQ - * https://github.com/liujingxing/rxhttp/wiki/更新日志 - */ -public open class RxHttp

, R : RxHttp> protected constructor( - public val `param`: P, -) : BaseRxHttp() { - private var connectTimeoutMillis: Long = 0L - - private var readTimeoutMillis: Long = 0L - - private var writeTimeoutMillis: Long = 0L - - private var converter: IConverter = RxHttpPlugins.getConverter() - - private var okClient: OkHttpClient = RxHttpPlugins.getOkHttpClient() - - public var request: Request? = null - - @get:JvmName("getUrl") - public val url: String - get() { - addDefaultDomainIfAbsent() - return param.url - } - - @get:JvmName("getSimpleUrl") - public val simpleUrl: String - get() = param.simpleUrl - - @get:JvmName("getHeaders") - public val headers: Headers - get() = param.headers - - @get:JvmName("getHeadersBuilder") - public val headersBuilder: Headers.Builder - get() = param.headersBuilder - - @get:JvmName("getCacheStrategy") - public val cacheStrategy: CacheStrategy - get() = param.cacheStrategy - - private var _okHttpClient: OkHttpClient? = null - - @get:JvmName("getOkHttpClient") - public val okHttpClient: OkHttpClient - get() { - if (_okHttpClient != null) return _okHttpClient!! - val okClient = this.okClient - var builder: OkHttpClient.Builder? = null - - if (LogUtil.isDebug()) { - val b = builder ?: okClient.newBuilder().also { builder = it } - b.addInterceptor(LogInterceptor(okClient)) - } - - if (connectTimeoutMillis != 0L) { - val b = builder ?: okClient.newBuilder().also { builder = it } - b.connectTimeout(connectTimeoutMillis, TimeUnit.MILLISECONDS) - } - - if (readTimeoutMillis != 0L) { - val b = builder ?: okClient.newBuilder().also { builder = it } - b.readTimeout(readTimeoutMillis, TimeUnit.MILLISECONDS) - } - - if (writeTimeoutMillis != 0L) { - val b = builder ?: okClient.newBuilder().also { builder = it } - b.writeTimeout(writeTimeoutMillis, TimeUnit.MILLISECONDS) - } - - if (param.cacheMode != CacheMode.ONLY_NETWORK) { - val b = builder ?: okClient.newBuilder().also { builder = it } - b.addInterceptor(CacheInterceptor(cacheStrategy)) - } - - _okHttpClient = builder?.build() ?: okClient - return _okHttpClient!! - } - - public fun connectTimeout(connectTimeout: Long): R { - connectTimeoutMillis = connectTimeout - return self() - } - - public fun readTimeout(readTimeout: Long): R { - readTimeoutMillis = readTimeout - return self() - } - - public fun writeTimeout(writeTimeout: Long): R { - writeTimeoutMillis = writeTimeout - return self() - } - - public fun setUrl(url: String): R { - param.url = url - return self() - } - - /** - * For example: - * - * ``` - * RxHttp.get("/service/{page}/...") - * .addPath("page", 1) - * ... - * ``` - * url = /service/1/... - */ - public fun addPath(name: String, `value`: Any): R { - param.addPath(name, value) - return self() - } - - public fun addEncodedPath(name: String, `value`: Any): R { - param.addEncodedPath(name, value) - return self() - } - - @JvmOverloads - public fun setQuery( - key: String, - `value`: Any?, - add: Boolean = true, - ): R { - if (add) param.setQuery(key, value) - return self() - } - - @JvmOverloads - public fun setEncodedQuery( - key: String, - `value`: Any?, - add: Boolean = true, - ): R { - if (add) param.setEncodedQuery(key, value) - return self() - } - - public fun removeAllQuery(key: String): R { - param.removeAllQuery(key) - return self() - } - - public fun addQuery(key: String): R { - param.addQuery(key, null) - return self() - } - - public fun addEncodedQuery(key: String): R { - param.addEncodedQuery(key, null) - return self() - } - - @JvmOverloads - public fun addQuery( - key: String, - `value`: Any?, - add: Boolean = true, - ): R { - if (add) param.addQuery(key, value) - return self() - } - - @JvmOverloads - public fun addEncodedQuery( - key: String, - `value`: Any?, - add: Boolean = true, - ): R { - if (add) param.addEncodedQuery(key, value) - return self() - } - - public fun addAllQuery(key: String, list: List<*>): R { - param.addAllQuery(key, list) - return self() - } - - public fun addAllEncodedQuery(key: String, list: List<*>): R { - param.addAllEncodedQuery(key, list) - return self() - } - - public fun addAllQuery(map: Map): R { - param.addAllQuery(map) - return self() - } - - public fun addAllEncodedQuery(map: Map): R { - param.addAllEncodedQuery(map) - return self() - } - - @JvmOverloads - public fun addHeader(line: String, add: Boolean = true): R { - if (add) param.addHeader(line) - return self() - } - - /** - * Add a header with the specified name and value. Does validation of header names, allowing non-ASCII values. - */ - public fun addNonAsciiHeader(key: String, `value`: String): R { - param.addNonAsciiHeader(key, value) - return self() - } - - /** - * Set a header with the specified name and value. Does validation of header names, allowing non-ASCII values. - */ - public fun setNonAsciiHeader(key: String, `value`: String): R { - param.setNonAsciiHeader(key, value) - return self() - } - - @JvmOverloads - public fun addHeader( - key: String, - `value`: String, - add: Boolean = true, - ): R { - if (add) param.addHeader(key, value) - return self() - } - - public fun addAllHeader(headers: Map): R { - param.addAllHeader(headers) - return self() - } - - public fun addAllHeader(headers: Headers): R { - param.addAllHeader(headers) - return self() - } - - public fun setHeader(key: String, `value`: String): R { - param.setHeader(key, value) - return self() - } - - public fun setAllHeader(headers: Map): R { - param.setAllHeader(headers) - return self() - } - - @JvmOverloads - public fun setRangeHeader(startIndex: Long, endIndex: Long = -1L): R = setRangeHeader(startIndex, endIndex, false) - - public fun setRangeHeader(startIndex: Long, connectLastProgress: Boolean): R = setRangeHeader(startIndex, -1, connectLastProgress) - - /** - * 设置断点下载开始/结束位置 - * @param startIndex 断点下载开始位置 - * @param endIndex 断点下载结束位置,默认为-1,即默认结束位置为文件末尾 - * @param connectLastProgress 是否衔接上次的下载进度,该参数仅在带进度断点下载时生效 - */ - public fun setRangeHeader( - startIndex: Long, - endIndex: Long, - connectLastProgress: Boolean, - ): R { - param.setRangeHeader(startIndex, endIndex) - if (connectLastProgress && startIndex >= 0) - param.tag(DownloadOffSize::class.java, DownloadOffSize(startIndex)) - return self() - } - - public fun removeAllHeader(key: String): R { - param.removeAllHeader(key) - return self() - } - - public fun setHeadersBuilder(builder: Headers.Builder): R { - param.headersBuilder = builder - return self() - } - - /** - * 设置单个接口是否需要添加公共参数, - * 即是否回调[RxHttpPlugins.setOnParamAssembly]方法设置的接口, 默认为true - */ - public fun setAssemblyEnabled(enabled: Boolean): R { - param.isAssemblyEnabled = enabled - return self() - } - - /** - * 设置单个接口是否需要对Http返回的数据进行解码/解密, - * 即是否回调[RxHttpPlugins.setResultDecoder]方法设置的接口, 默认为true - */ - public fun setDecoderEnabled(enabled: Boolean): R { - param.addHeader(Param.DATA_DECRYPT, enabled.toString()) - return self() - } - - public fun isAssemblyEnabled(): Boolean = param.isAssemblyEnabled - - public fun getHeader(key: String): String = param.getHeader(key) - - public fun tag(tag: Any): R { - param.tag(tag) - return self() - } - - public override fun tag(type: Class, tag: T): R { - param.tag(type, tag) - if (type === OutputStreamFactory::class.java) { - okClient = okClient.newBuilder() - .addInterceptor(RangeInterceptor()) - .build() - } - return self() - } - - public fun cacheControl(cacheControl: CacheControl): R { - param.cacheControl(cacheControl) - return self() - } - - public fun setCacheKey(cacheKey: String): R { - param.cacheKey = cacheKey - return self() - } - - public fun setCacheValidTime(cacheValidTime: Long): R { - param.cacheValidTime = cacheValidTime - return self() - } - - public fun setCacheMode(cacheMode: CacheMode): R { - param.cacheMode = cacheMode - return self() - } - - public override fun newCall(): Call { - val request = buildRequest() - return okHttpClient.newCall(request) - } - - public fun buildRequest(): Request { - if (request == null) { - doOnStart() - request = param.buildRequest() - } - return request!! - } - - /** - * 请求开始前内部调用,用于添加默认域名等操作 - */ - private fun doOnStart() { - setConverterToParam(converter) - addDefaultDomainIfAbsent() - } - - public fun setXmlConverter(): R = setConverter(xmlConverter) - - public fun setFastJsonConverter(): R = setConverter(fastJsonConverter) - - public fun setConverter(converter: IConverter): R { - this.converter = converter - return self() - } - - /** - * 给Param设置转换器,此方法会在请求发起前,被RxHttp内部调用 - */ - private fun setConverterToParam(converter: IConverter) { - param.tag(IConverter::class.java, converter) - } - - public fun setOkClient(okClient: OkHttpClient): R { - this.okClient = okClient - return self() - } - - public fun setSimpleClient(): R = setOkClient(simpleClient) - - /** - * 给Param设置默认域名(如果缺席的话),此方法会在请求发起前,被RxHttp内部调用 - */ - private fun addDefaultDomainIfAbsent() { - val originUrl = param.simpleUrl - if (originUrl.startsWith("http")) return - setDomainIfAbsent(baseUrl) - } - - public fun setDomainIfAbsent(domain: String): R { - val newUrl = addDomainIfAbsent(param.simpleUrl, domain) - param.url = newUrl - return self() - } - - private fun addDomainIfAbsent(url: String, domain: String): String = - if (url.startsWith("http")) { - url - } else if (url.startsWith("/")) { - val finalUrl = if (domain.endsWith("/")) url.substring(1) else url - "$domain$finalUrl" - } else if (domain.endsWith("/")) { - "$domain$url" - } else { - "$domain/$url" - } - - @Suppress("UNCHECKED_CAST") - private fun self(): R = this as R - - public companion object { - /** - * For example: - * - * ``` - * RxHttp.get("/service/%d/...", 1) - * .addQuery("size", 20) - * ... - * ``` - * url = /service/1/...?size=20 - */ - @JvmStatic - public fun `get`(url: String, vararg formatArgs: Any?): RxHttpNoBodyParam = RxHttpNoBodyParam(Param.get(format(url, *formatArgs))) - - @JvmStatic - public fun head(url: String, vararg formatArgs: Any?): RxHttpNoBodyParam = RxHttpNoBodyParam(Param.head(format(url, *formatArgs))) - - @JvmStatic - public fun postBody(url: String, vararg formatArgs: Any?): RxHttpBodyParam = RxHttpBodyParam(Param.postBody(format(url, *formatArgs))) - - @JvmStatic - public fun putBody(url: String, vararg formatArgs: Any?): RxHttpBodyParam = RxHttpBodyParam(Param.putBody(format(url, *formatArgs))) - - @JvmStatic - public fun patchBody(url: String, vararg formatArgs: Any?): RxHttpBodyParam = RxHttpBodyParam(Param.patchBody(format(url, *formatArgs))) - - @JvmStatic - public fun deleteBody(url: String, vararg formatArgs: Any?): RxHttpBodyParam = RxHttpBodyParam(Param.deleteBody(format(url, *formatArgs))) - - @JvmStatic - public fun postForm(url: String, vararg formatArgs: Any?): RxHttpFormParam = RxHttpFormParam(Param.postForm(format(url, *formatArgs))) - - @JvmStatic - public fun putForm(url: String, vararg formatArgs: Any?): RxHttpFormParam = RxHttpFormParam(Param.putForm(format(url, *formatArgs))) - - @JvmStatic - public fun patchForm(url: String, vararg formatArgs: Any?): RxHttpFormParam = RxHttpFormParam(Param.patchForm(format(url, *formatArgs))) - - @JvmStatic - public fun deleteForm(url: String, vararg formatArgs: Any?): RxHttpFormParam = RxHttpFormParam(Param.deleteForm(format(url, *formatArgs))) - - @JvmStatic - public fun postJson(url: String, vararg formatArgs: Any?): RxHttpJsonParam = RxHttpJsonParam(Param.postJson(format(url, *formatArgs))) - - @JvmStatic - public fun putJson(url: String, vararg formatArgs: Any?): RxHttpJsonParam = RxHttpJsonParam(Param.putJson(format(url, *formatArgs))) - - @JvmStatic - public fun patchJson(url: String, vararg formatArgs: Any?): RxHttpJsonParam = RxHttpJsonParam(Param.patchJson(format(url, *formatArgs))) - - @JvmStatic - public fun deleteJson(url: String, vararg formatArgs: Any?): RxHttpJsonParam = RxHttpJsonParam(Param.deleteJson(format(url, *formatArgs))) - - @JvmStatic - public fun postJsonArray(url: String, vararg formatArgs: Any?): RxHttpJsonArrayParam = RxHttpJsonArrayParam(Param.postJsonArray(format(url, *formatArgs))) - - @JvmStatic - public fun putJsonArray(url: String, vararg formatArgs: Any?): RxHttpJsonArrayParam = RxHttpJsonArrayParam(Param.putJsonArray(format(url, *formatArgs))) - - @JvmStatic - public fun patchJsonArray(url: String, vararg formatArgs: Any?): RxHttpJsonArrayParam = RxHttpJsonArrayParam(Param.patchJsonArray(format(url, *formatArgs))) - - @JvmStatic - public fun deleteJsonArray(url: String, vararg formatArgs: Any?): RxHttpJsonArrayParam = RxHttpJsonArrayParam(Param.deleteJsonArray(format(url, *formatArgs))) - - @JvmStatic - public fun postEncryptJson(url: String, vararg formatArgs: Any?): RxHttpPostEncryptJsonParam = RxHttpPostEncryptJsonParam(PostEncryptJsonParam(format(url, *formatArgs))) - - @JvmStatic - public fun postEncryptForm(url: String, vararg formatArgs: Any?): RxHttpPostEncryptFormParam = RxHttpPostEncryptFormParam(PostEncryptFormParam(format(url, *formatArgs))) - - @JvmStatic - public fun postEncryptForm( - url: String, - method: Method, - vararg formatArgs: Any?, - ): RxHttpPostEncryptFormParam = RxHttpPostEncryptFormParam(PostEncryptFormParam(format(url, *formatArgs), method)) - - @JvmStatic - public fun getEncrypt(url: String, vararg formatArgs: Any?): RxHttpGetEncryptParam = RxHttpGetEncryptParam(GetEncryptParam(format(url, *formatArgs))) - - @JvmStatic - public fun postEncryptJson1(url: String, vararg formatArgs: Any?): RxHttpPostEncryptJsonParam1 = RxHttpPostEncryptJsonParam1(PostEncryptJsonParam1(format(url, *formatArgs))) - - /** - * Returns a formatted string using the specified format string and arguments. - */ - private fun format(url: String, vararg formatArgs: Any?): String = if(formatArgs.isEmpty()) url else String.format(url, *formatArgs) - } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpAbstractBodyParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpAbstractBodyParam.kt deleted file mode 100644 index a68bc7d7..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpAbstractBodyParam.kt +++ /dev/null @@ -1,18 +0,0 @@ -package rxhttp.wrapper.param - -import rxhttp.wrapper.BodyParamFactory -import rxhttp.wrapper.param.AbstractBodyParam - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - * https://github.com/liujingxing/rxhttp/wiki/FAQ - * https://github.com/liujingxing/rxhttp/wiki/更新日志 - */ -open class RxHttpAbstractBodyParam

, R : RxHttpAbstractBodyParam> -protected constructor( - param: P -) : RxHttp(param), BodyParamFactory { - -} \ No newline at end of file diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpBodyParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpBodyParam.kt deleted file mode 100644 index ab63d872..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpBodyParam.kt +++ /dev/null @@ -1,54 +0,0 @@ -package rxhttp.wrapper.param - -import android.content.Context -import android.net.Uri -import rxhttp.wrapper.entity.UriRequestBody - -import okhttp3.MediaType -import okhttp3.RequestBody -import okio.ByteString -import rxhttp.wrapper.param.BodyParam -import rxhttp.wrapper.OkHttpCompat -import rxhttp.wrapper.entity.FileRequestBody -import rxhttp.wrapper.utils.BuildUtil -import java.io.File - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - */ -open class RxHttpBodyParam(param: BodyParam) : RxHttpAbstractBodyParam(param) { - - fun setBody(content: String, contentType: MediaType? = null) = - setBody(OkHttpCompat.create(contentType, content)) - - fun setBody(content: ByteString, contentType: MediaType? = null) = - setBody(OkHttpCompat.create(contentType, content)) - - @JvmOverloads - fun setBody( - content: ByteArray, - contentType: MediaType? = null, - offset: Int = 0, - byteCount: Int = content.size, - ) = setBody(OkHttpCompat.create(contentType, content, offset, byteCount)) - - @JvmOverloads - fun setBody( - file: File, - contentType: MediaType? = BuildUtil.getMediaType(file.name), - ) = setBody(FileRequestBody(file, 0, contentType)) - - @JvmOverloads - fun setBody( - context: Context, - uri: Uri, - contentType: MediaType? = BuildUtil.getMediaTypeByUri(context, uri), - ) = setBody(UriRequestBody(context, uri, 0, contentType)) - - //Content-Type: application/json; charset=utf-8 - fun setBody(any: Any) = apply { param.setBody(any) } - - fun setBody(requestBody: RequestBody) = apply { param.setBody(requestBody) } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpExtension.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpExtension.kt deleted file mode 100644 index de90f36f..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpExtension.kt +++ /dev/null @@ -1,23 +0,0 @@ -package rxhttp.wrapper.`param` - -import kotlin.collections.List -import rxhttp.toAwait -import rxhttp.toFlow -import rxhttp.wrapper.CallFactory -import rxhttp.wrapper.coroutines.CallAwait -import rxhttp.wrapper.coroutines.CallFlow -import rxhttp.wrapper.utils.javaTypeOf - -public inline fun BaseRxHttp.executeList(): List = executeClass>() - -public inline fun BaseRxHttp.executeClass(): T = executeClass(javaTypeOf()) - -public inline fun BaseRxHttp.toObservableList(): ObservableCall> = toObservable>() - -public inline fun BaseRxHttp.toObservable(): ObservableCall = toObservable(javaTypeOf()) - -public inline fun BaseRxHttp.toObservableResponse(): ObservableCall = toObservableResponse(javaTypeOf()) - -public inline fun CallFactory.toAwaitResponse(): CallAwait = toAwait(BaseRxHttp.wrapResponseParser(javaTypeOf())) - -public inline fun CallFactory.toFlowResponse(): CallFlow = toFlow(BaseRxHttp.wrapResponseParser(javaTypeOf())) diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpFormParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpFormParam.kt deleted file mode 100644 index 0a3b33e2..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpFormParam.kt +++ /dev/null @@ -1,169 +0,0 @@ -package rxhttp.wrapper.param - -import android.content.Context -import android.net.Uri -import rxhttp.wrapper.entity.UriRequestBody -import okhttp3.Headers -import okhttp3.MediaType -import okhttp3.MultipartBody -import okhttp3.RequestBody -import rxhttp.wrapper.OkHttpCompat -import rxhttp.wrapper.entity.FileRequestBody -import rxhttp.wrapper.entity.UpFile -import rxhttp.wrapper.param.FormParam -import rxhttp.wrapper.utils.BuildUtil -import rxhttp.wrapper.utils.displayName -import java.io.File - - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - * https://github.com/liujingxing/rxhttp/wiki/FAQ - * https://github.com/liujingxing/rxhttp/wiki/更新日志 - */ -open class RxHttpFormParam(param: FormParam) : RxHttpAbstractBodyParam(param) { - - @JvmOverloads - fun add(key: String, value: Any?, add: Boolean = true) = apply { - if (add) param.add(key, value) - } - - fun addAll(map: Map) = apply { param.addAll(map) } - - fun addEncoded(key: String, value: Any?) = apply { param.addEncoded(key, value) } - - fun addAllEncoded(map: Map) = apply { param.addAllEncoded(map) } - - fun removeAllBody() = apply { param.removeAllBody() } - - fun removeAllBody(key: String) = apply { param.removeAllBody(key) } - - fun set(key: String, value: Any?) = apply { param[key] = value } - - fun setEncoded(key: String, value: Any?) = apply { param.setEncoded(key, value) } - - fun addFile(key: String, filePath: String?) = - if (filePath == null) this else addFile(key, File(filePath)) - - @JvmOverloads - fun addFile(key: String, file: File?, filename: String? = file?.name) = - if (file == null) this else addFile(UpFile(key, file, filename)) - - fun addFiles(fileList: List) = apply { fileList.forEach { addFile(it) } } - - fun addFiles(fileMap: Map) = apply { - fileMap.forEach { (key, value) -> addFile(key, value) } - } - - fun addFiles(key: String, files: List) = apply { - files.forEach { addFile(key, it) } - } - - private fun addFile(key: String, file: Any?) { - if (file is File) { - addFile(key, file) - } else if (file is String) { - addFile(key, file) - } else if (file != null) { - throw IllegalArgumentException("Incoming data type exception, it must be String or File") - } - } - - fun addFile(upFile: UpFile) = apply { - val requestBody = FileRequestBody(upFile.file, upFile.skipSize, BuildUtil.getMediaType(upFile.filename)) - return addFormDataPart(upFile.key, upFile.filename, requestBody) - } - - @JvmOverloads - fun addPart( - content: ByteArray, - contentType: MediaType? = null, - offset: Int = 0, - byteCount: Int = content.size - ) = addPart(OkHttpCompat.create(contentType, content, offset, byteCount)) - - @JvmOverloads - fun addPart( - context: Context, - uri: Uri, - contentType: MediaType? = BuildUtil.getMediaTypeByUri(context, uri) - ) = addPart(UriRequestBody(context, uri, 0, contentType)) - - @JvmOverloads - fun addPart( - context: Context, - key: String, - uri: Uri, - contentType: MediaType? = BuildUtil.getMediaTypeByUri(context, uri) - ) = addPart(context, key, uri.displayName(context), uri, contentType) - - @JvmOverloads - fun addPart( - context: Context, - key: String, - filename: String?, - uri: Uri, - contentType: MediaType? = BuildUtil.getMediaTypeByUri(context, uri) - ) = addFormDataPart(key, filename, UriRequestBody(context, uri, 0, contentType)) - - fun addParts(context: Context, uriMap: Map) = apply { - uriMap.forEach { (key, value) -> addPart(context, key, value) } - } - - fun addParts(context: Context, uris: List) = apply { - uris.forEach { addPart(context, it) } - } - - fun addParts(context: Context, uris: List, contentType: MediaType?) = apply { - uris.forEach { addPart(context, it, contentType) } - } - - fun addParts(context: Context, key: String, uris: List) = apply { - uris.forEach { addPart(context, key, it) } - } - - fun addParts(context: Context, key: String, uris: List, contentType: MediaType?) = apply { - uris.forEach { addPart(context, key, it, contentType) } - } - - fun addPart(requestBody: RequestBody) = addPart(OkHttpCompat.part(requestBody)) - - fun addPart(headers: Headers?, requestBody: RequestBody) = - addPart(OkHttpCompat.part(headers, requestBody)) - - fun addFormDataPart( - key: String, - fileName: String?, - requestBody: RequestBody - ) = addPart(OkHttpCompat.part(key, fileName, requestBody)) - - fun addPart(key: String, requestBody: RequestBody) = addFormDataPart(key, null, requestBody) - - fun addParts(partMap: Map) = apply { - partMap.forEach { (key, value) -> addPart(key, value) } - } - - fun addPart(part: MultipartBody.Part) = apply { param.addPart(part) } - - fun addParts(parts: List) = apply { parts.forEach { addPart(it) } } - - //Set content-type to multipart/form-data - fun setMultiForm() = setMultiType(MultipartBody.FORM) - - //Set content-type to multipart/mixed - fun setMultiMixed() = setMultiType(MultipartBody.MIXED) - - //Set content-type to multipart/alternative - fun setMultiAlternative() = setMultiType(MultipartBody.ALTERNATIVE) - - //Set content-type to multipart/digest - fun setMultiDigest() = setMultiType(MultipartBody.DIGEST) - - //Set content-type to multipart/parallel - fun setMultiParallel() = setMultiType(MultipartBody.PARALLEL) - - //Set the MIME type - fun setMultiType(multiType: MediaType?) = apply { param.multiType = multiType } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpGetEncryptParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpGetEncryptParam.kt deleted file mode 100644 index 1025384e..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpGetEncryptParam.kt +++ /dev/null @@ -1,32 +0,0 @@ -package rxhttp.wrapper.`param` - -import android.graphics.Point -import com.example.httpsender.`param`.GetEncryptParam -import java.io.IOException -import java.lang.IllegalArgumentException -import kotlin.Array -import kotlin.CharSequence -import kotlin.collections.MutableList -import kotlin.collections.MutableMap -import kotlin.jvm.Throws - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - */ -public class RxHttpGetEncryptParam : RxHttpNoBodyParam { - public constructor(`param`: GetEncryptParam) : super(param) - - @Throws( - IOException::class, - IllegalArgumentException::class, - ) - public final fun test( - a: MutableList, - map: MutableMap, - vararg b: Array, - ): RxHttpGetEncryptParam = apply { - (param as GetEncryptParam).test(a, map, *b) - } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpJsonArrayParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpJsonArrayParam.kt deleted file mode 100644 index 6f17991a..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpJsonArrayParam.kt +++ /dev/null @@ -1,48 +0,0 @@ -package rxhttp.wrapper.param - -import com.google.gson.JsonArray -import com.google.gson.JsonObject - -import rxhttp.wrapper.param.JsonArrayParam - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - * https://github.com/liujingxing/rxhttp/wiki/FAQ - * https://github.com/liujingxing/rxhttp/wiki/更新日志 - */ -open class RxHttpJsonArrayParam(param: JsonArrayParam) : RxHttpAbstractBodyParam(param) { - - @JvmOverloads - fun add(key: String, value: Any?, add: Boolean = true) = apply { - if (add) param.add(key, value) - } - - fun addAll(map: Map) = apply { param.addAll(map) } - - fun add(any: Any) = apply { param.add(any) } - - fun addAll(list: List<*>) = apply { param.addAll(list) } - - /** - * 添加多个对象,将字符串转JsonElement对象,并根据不同类型,执行不同操作,可输入任意非空字符串 - */ - fun addAll(jsonElement: String) = apply { param.addAll(jsonElement) } - - fun addAll(jsonArray: JsonArray) = apply { param.addAll(jsonArray) } - - /** - * 将Json对象里面的key-value逐一取出,添加到Json数组中,成为单独的对象 - */ - fun addAll(jsonObject: JsonObject) = apply { param.addAll(jsonObject) } - - fun addJsonElement(jsonElement: String) = apply { param.addJsonElement(jsonElement) } - - /** - * 添加一个JsonElement对象(Json对象、json数组等) - */ - fun addJsonElement(key: String, jsonElement: String) = apply { - param.addJsonElement(key, jsonElement) - } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpJsonParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpJsonParam.kt deleted file mode 100644 index b7f4a2c2..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpJsonParam.kt +++ /dev/null @@ -1,39 +0,0 @@ -package rxhttp.wrapper.param - -import com.google.gson.JsonObject - -import rxhttp.wrapper.param.JsonParam -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - * https://github.com/liujingxing/rxhttp/wiki/FAQ - * https://github.com/liujingxing/rxhttp/wiki/更新日志 - */ -open class RxHttpJsonParam(param: JsonParam) : RxHttpAbstractBodyParam(param) { - - @JvmOverloads - fun add(key: String, value: Any?, add: Boolean = true) = apply { - if (add) param.add(key, value) - } - - fun addAll(map: Map) = apply { param.addAll(map) } - - /** - * 将Json对象里面的key-value逐一取出,添加到另一个Json对象中, - * 输入非Json对象将抛出[IllegalStateException]异常 - */ - fun addAll(jsonObject: String) = apply { param.addAll(jsonObject) } - - /** - * 将Json对象里面的key-value逐一取出,添加到另一个Json对象中 - */ - fun addAll(jsonObject: JsonObject) = apply { param.addAll(jsonObject) } - - /** - * 添加一个JsonElement对象(Json对象、json数组等) - */ - fun addJsonElement(key: String, jsonElement: String) = apply { - param.addJsonElement(key, jsonElement) - } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpNoBodyParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpNoBodyParam.kt deleted file mode 100644 index 3144e3ef..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpNoBodyParam.kt +++ /dev/null @@ -1,24 +0,0 @@ -package rxhttp.wrapper.param - -import rxhttp.wrapper.param.NoBodyParam - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - * https://github.com/liujingxing/rxhttp/wiki/FAQ - * https://github.com/liujingxing/rxhttp/wiki/更新日志 - */ -open class RxHttpNoBodyParam(param: NoBodyParam) : RxHttp(param) { - - @JvmOverloads - fun add(key: String, value: Any?, add: Boolean = true) = apply { - if (add) addQuery(key, value) - } - - fun addAll(map: Map) = addAllQuery(map) - - fun addEncoded(key: String, value: Any?) = addEncodedQuery(key, value) - - fun addAllEncoded(map: Map) = addAllEncodedQuery(map) -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptFormParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptFormParam.kt deleted file mode 100644 index aa98181d..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptFormParam.kt +++ /dev/null @@ -1,26 +0,0 @@ -package rxhttp.wrapper.`param` - -import com.example.httpsender.`param`.PostEncryptFormParam -import kotlin.Float -import kotlin.Int -import kotlin.Long -import kotlin.String - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - */ -public class RxHttpPostEncryptFormParam : RxHttpFormParam { - public constructor(`param`: PostEncryptFormParam) : super(param) - - public fun test1(s: String): RxHttpPostEncryptFormParam = apply { - (param as PostEncryptFormParam).test1(s) - } - - public fun test2(a: Long, b: Float): RxHttpPostEncryptFormParam = apply { - (param as PostEncryptFormParam).test2(a, b) - } - - public fun add(a: Int, b: Int): Int = (param as PostEncryptFormParam).add(a, b) -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptJsonParam.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptJsonParam.kt deleted file mode 100644 index a9a9bcee..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptJsonParam.kt +++ /dev/null @@ -1,12 +0,0 @@ -package rxhttp.wrapper.`param` - -import com.example.httpsender.`param`.PostEncryptJsonParam - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - */ -public class RxHttpPostEncryptJsonParam : RxHttpJsonParam { - public constructor(`param`: PostEncryptJsonParam) : super(param) -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptJsonParam1.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptJsonParam1.kt deleted file mode 100644 index a7fec587..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxHttpPostEncryptJsonParam1.kt +++ /dev/null @@ -1,16 +0,0 @@ -package rxhttp.wrapper.`param` - -import com.example.httpsender.`param`.PostEncryptJsonParam1 - -/** - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - */ -public class RxHttpPostEncryptJsonParam1 : RxHttp { - public constructor(`param`: PostEncryptJsonParam1) : super(param) - - public fun test() { - param.test() - } -} diff --git a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxSimpleHttp.kt b/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxSimpleHttp.kt deleted file mode 100644 index 53c8bf33..00000000 --- a/app/build/generated/ksp/debug/kotlin/rxhttp/wrapper/param/RxSimpleHttp.kt +++ /dev/null @@ -1,96 +0,0 @@ -package rxhttp.wrapper.`param` - -import kotlin.Any -import kotlin.String -import kotlin.jvm.JvmStatic - -/** - * 本类由@Converter、@Domain、@OkClient注解中的className字段生成 类命名方式: Rx + {className字段值} + Http - * Github - * https://github.com/liujingxing/rxhttp - * https://github.com/liujingxing/rxlife - * https://github.com/liujingxing/rxhttp/wiki/FAQ - * https://github.com/liujingxing/rxhttp/wiki/更新日志 - */ -public object RxSimpleHttp { - /** - * 本类所有方法都会调用本方法 - */ - private fun > R.wrapper(): R { - setSimpleClient() - return this - } - - @JvmStatic - public fun `get`(url: String, vararg formatArgs: Any?): RxHttpNoBodyParam = RxHttp.get(url, *formatArgs).wrapper() - - @JvmStatic - public fun head(url: String, vararg formatArgs: Any?): RxHttpNoBodyParam = RxHttp.head(url, *formatArgs).wrapper() - - @JvmStatic - public fun postBody(url: String, vararg formatArgs: Any?): RxHttpBodyParam = RxHttp.postBody(url, *formatArgs).wrapper() - - @JvmStatic - public fun putBody(url: String, vararg formatArgs: Any?): RxHttpBodyParam = RxHttp.putBody(url, *formatArgs).wrapper() - - @JvmStatic - public fun patchBody(url: String, vararg formatArgs: Any?): RxHttpBodyParam = RxHttp.patchBody(url, *formatArgs).wrapper() - - @JvmStatic - public fun deleteBody(url: String, vararg formatArgs: Any?): RxHttpBodyParam = RxHttp.deleteBody(url, *formatArgs).wrapper() - - @JvmStatic - public fun postForm(url: String, vararg formatArgs: Any?): RxHttpFormParam = RxHttp.postForm(url, *formatArgs).wrapper() - - @JvmStatic - public fun putForm(url: String, vararg formatArgs: Any?): RxHttpFormParam = RxHttp.putForm(url, *formatArgs).wrapper() - - @JvmStatic - public fun patchForm(url: String, vararg formatArgs: Any?): RxHttpFormParam = RxHttp.patchForm(url, *formatArgs).wrapper() - - @JvmStatic - public fun deleteForm(url: String, vararg formatArgs: Any?): RxHttpFormParam = RxHttp.deleteForm(url, *formatArgs).wrapper() - - @JvmStatic - public fun postJson(url: String, vararg formatArgs: Any?): RxHttpJsonParam = RxHttp.postJson(url, *formatArgs).wrapper() - - @JvmStatic - public fun putJson(url: String, vararg formatArgs: Any?): RxHttpJsonParam = RxHttp.putJson(url, *formatArgs).wrapper() - - @JvmStatic - public fun patchJson(url: String, vararg formatArgs: Any?): RxHttpJsonParam = RxHttp.patchJson(url, *formatArgs).wrapper() - - @JvmStatic - public fun deleteJson(url: String, vararg formatArgs: Any?): RxHttpJsonParam = RxHttp.deleteJson(url, *formatArgs).wrapper() - - @JvmStatic - public fun postJsonArray(url: String, vararg formatArgs: Any?): RxHttpJsonArrayParam = RxHttp.postJsonArray(url, *formatArgs).wrapper() - - @JvmStatic - public fun putJsonArray(url: String, vararg formatArgs: Any?): RxHttpJsonArrayParam = RxHttp.putJsonArray(url, *formatArgs).wrapper() - - @JvmStatic - public fun patchJsonArray(url: String, vararg formatArgs: Any?): RxHttpJsonArrayParam = RxHttp.patchJsonArray(url, *formatArgs).wrapper() - - @JvmStatic - public fun deleteJsonArray(url: String, vararg formatArgs: Any?): RxHttpJsonArrayParam = RxHttp.deleteJsonArray(url, *formatArgs).wrapper() - - @JvmStatic - public fun postEncryptJson(url: String, vararg formatArgs: Any?): RxHttpPostEncryptJsonParam = RxHttp.postEncryptJson(url, *formatArgs).wrapper() - - @JvmStatic - public fun postEncryptForm(url: String, vararg formatArgs: Any?): RxHttpPostEncryptFormParam = RxHttp.postEncryptForm(url, *formatArgs).wrapper() - - @JvmStatic - public fun postEncryptForm( - url: String, - method: Method, - vararg formatArgs: Any?, - ): RxHttpPostEncryptFormParam = RxHttp.postEncryptForm(url, method, *formatArgs).wrapper() - - @JvmStatic - public fun getEncrypt(url: String, vararg formatArgs: Any?): RxHttpGetEncryptParam = RxHttp.getEncrypt(url, *formatArgs).wrapper() - - @JvmStatic - public fun postEncryptJson1(url: String, vararg formatArgs: Any?): RxHttpPostEncryptJsonParam1 = RxHttp.postEncryptJson1(url, *formatArgs).wrapper() -} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index a390ba34..30cb0f6c 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -20,50 +20,8 @@ # hide the original source file name. #-renamesourcefileattribute SourceFile --keepclassmembers class com.example.httpsender.entity.** { - (); #R8 full mode下, 默认构造方法不保留 - !transient ; -} -# With R8 full mode generic signatures are stripped for classes that are not kept. --keep,allowobfuscation,allowshrinking class com.example.httpsender.entity.Response --keep,allowobfuscation,allowshrinking class com.example.httpsender.entity.PageList - - -#依赖simple-xml后打包失败,需加入以下规则 --dontwarn android.content.res.** - -#依赖fastjson后打包失败,需加入以下规则 --dontwarn javax.ws.rs.** - -# Please add these rules to your existing keep rules in order to suppress warnings. -# This is generated automatically by the Android Gradle plugin. --dontwarn com.google.common.collect.ArrayListMultimap --dontwarn com.google.common.collect.Multimap --dontwarn java.awt.Color --dontwarn java.awt.Font --dontwarn java.awt.Point --dontwarn java.awt.Rectangle --dontwarn javax.money.CurrencyUnit --dontwarn javax.money.Monetary --dontwarn javax.ws.rs.Consumes --dontwarn javax.ws.rs.Produces --dontwarn javax.ws.rs.core.Response --dontwarn javax.ws.rs.core.StreamingOutput --dontwarn javax.ws.rs.ext.MessageBodyReader --dontwarn javax.ws.rs.ext.MessageBodyWriter --dontwarn javax.ws.rs.ext.Provider --dontwarn org.glassfish.jersey.internal.spi.AutoDiscoverable --dontwarn org.javamoney.moneta.Money --dontwarn org.joda.time.DateTime --dontwarn org.joda.time.DateTimeZone --dontwarn org.joda.time.Duration --dontwarn org.joda.time.Instant --dontwarn org.joda.time.LocalDate --dontwarn org.joda.time.LocalDateTime --dontwarn org.joda.time.LocalTime --dontwarn org.joda.time.Period --dontwarn org.joda.time.ReadablePartial --dontwarn org.joda.time.format.DateTimeFormat --dontwarn org.joda.time.format.DateTimeFormatter --dontwarn springfox.documentation.spring.web.json.Json \ No newline at end of file +#RxHttp +-keep class rxhttp.**{*;} +#RxLife +-keep class com.rxjava.rxlife.**{*;} \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/httpsender/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/httpsender/ExampleInstrumentedTest.java deleted file mode 100644 index e73af3a0..00000000 --- a/app/src/androidTest/java/com/example/httpsender/ExampleInstrumentedTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.example.httpsender; - -import android.content.Context; -import android.util.Log; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import io.reactivex.rxjava3.disposables.Disposable; -import rxhttp.wrapper.param.RxHttp; - - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); - Disposable subscribe = RxHttp.get("https://www.wanandroid.com/article/list/0/json") - .toObservableString() - .subscribe(s -> { - System.out.println(s); - }, throwable -> { - Log.e("LJX", "useAppContext"); - }); - - while (!subscribe.isDisposed()) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 01ec62d9..b7c8726a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + @@ -7,23 +8,22 @@ - + + \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/AppHolder.java b/app/src/main/java/com/example/httpsender/AppHolder.java index 706f587f..3a6e84e7 100644 --- a/app/src/main/java/com/example/httpsender/AppHolder.java +++ b/app/src/main/java/com/example/httpsender/AppHolder.java @@ -10,7 +10,6 @@ * Date: 2019/3/31 * Time: 09:11 */ -//@HiltAndroidApp public class AppHolder extends Application { private static AppHolder instance; diff --git a/app/src/main/java/com/example/httpsender/DownloadMultiActivity.java b/app/src/main/java/com/example/httpsender/DownloadMultiActivity.java new file mode 100644 index 00000000..ce1cbaa3 --- /dev/null +++ b/app/src/main/java/com/example/httpsender/DownloadMultiActivity.java @@ -0,0 +1,185 @@ +package com.example.httpsender; + +import android.os.Bundle; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.recyclerview.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +import com.example.httpsender.DownloadMultiAdapter.OnItemClickListener; +import com.example.httpsender.entity.DownloadInfo; +import com.rxjava.rxlife.RxLife; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import rxhttp.wrapper.param.RxHttp; + +/** + * 多任务下载 + * User: ljx + * Date: 2019-06-07 + * Time: 11:02 + */ +public class DownloadMultiActivity extends AppCompatActivity implements OnItemClickListener { + + public static final int MAX_TASK_COUNT = 3; //最大并发数 + + private DownloadMultiAdapter mAdapter; + + private String[] downloadUrl = { + "http://update.9158.com/miaolive/Miaolive.apk",//喵播 + "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk",//探探 + "https://o8g2z2sa4.qnssl.com/android/momo_8.18.5_c1.apk",//陌陌 + "http://s9.pstatp.com/package/apk/aweme/app_aweGW_v6.6.0_2905d5c.apk"//抖音 + }; + + private List waitTask = new ArrayList<>(); //等待下载的任务 + private List downloadingTask = new ArrayList<>(); //等待下载的任务 + + private List downloadInfos = new ArrayList<>(); + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.download_multi_activity); + + for (int i = 0; i < 20; i++) { + DownloadInfo downloadInfo = new DownloadInfo(downloadUrl[i % downloadUrl.length]); + downloadInfo.setTaskId(i); + downloadInfos.add(downloadInfo); + } + mAdapter = new DownloadMultiAdapter(downloadInfos); + mAdapter.setOnItemClickListener(this); + mAdapter.setHasStableIds(true); + + RecyclerView recyclerView = findViewById(R.id.recycler_view); + recyclerView.setAdapter(mAdapter); + } + + public static long lastChangedTime; + + //500毫秒刷新一次列表 + private void notifyDataSetChanged(boolean force) { + long time = System.currentTimeMillis(); + if (time - lastChangedTime > 500 || force) { + mAdapter.notifyDataSetChanged(); + lastChangedTime = time; + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.download, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.download_all) { + if ("全部下载".contentEquals(item.getTitle())) { + for (DownloadInfo info : downloadInfos) { + download(info); + } + item.setTitle("全部取消"); + } else if ("全部取消".contentEquals(item.getTitle())) { + Iterator iterator = waitTask.iterator(); + while (iterator.hasNext()) { + DownloadInfo next = iterator.next(); + next.setState(6); + iterator.remove(); + } + + iterator = downloadingTask.iterator(); + while (iterator.hasNext()) { + DownloadInfo next = iterator.next(); + iterator.remove(); + Disposable disposable = next.getDisposable(); + if (disposable != null && !disposable.isDisposed()) { + disposable.dispose(); + } + next.setState(6); + } + item.setTitle("全部下载"); + notifyDataSetChanged(true); + } + + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onItemClick(View view, DownloadInfo data, int position) { + switch (view.getId()) { + case R.id.bt_pause: + int state = data.getState(); + if (state == 0) { + download(data); + } else if (state == 1) { + waitTask.remove(data); + data.setState(6); + } else if (state == 2) { + Disposable disposable = data.getDisposable(); + if (disposable != null && !disposable.isDisposed()) { + disposable.dispose(); + data.setState(3); + notifyDataSetChanged(true); + } + } else if (state == 3) { + download(data); + } else if (state == 4) { + Tip.show("该任务已完成"); + } else if (state == 5) { + Tip.show("该任务下载失败"); + } else if (state == 6) { + download(data); + } + break; + } + } + + private void download(DownloadInfo data) { + if (downloadingTask.size() >= MAX_TASK_COUNT) { + data.setState(1); + waitTask.add(data); + return; + } + String destPath = getExternalCacheDir() + "/" + data.getTaskId() + ".apk"; + long length = new File(destPath).length(); + Disposable disposable = RxHttp.get(data.getUrl()) + .setRangeHeader(length) //设置开始下载位置,结束位置默认为文件末尾 + .asDownload(destPath, length, progress -> { //如果需要衔接上次的下载进度,则需要传入上次已下载的字节数length + //下载进度回调,0-100,仅在进度有更新时才会回调 + if (!progress.isCompleted()) { + data.setProgress(progress.getProgress());//当前进度 0-100 + data.setCurrentSize(progress.getCurrentSize());//当前已下载的字节大小 + data.setTotalSize(progress.getTotalSize()); //要下载的总字节大小 + notifyDataSetChanged(false); + } + }, AndroidSchedulers.mainThread()) + .doFinally(() -> {//不管任务成功还是失败,如果还有在等待的任务,都开启下一个任务 + downloadingTask.remove(data); + if (waitTask.size() > 0) + download(waitTask.remove(0)); + }) + .as(RxLife.as(this)) //加入感知生命周期的观察者 + .subscribe(s -> { //s为String类型 + Tip.show("下载完成" + s); + data.setState(4); + notifyDataSetChanged(true); + //下载成功,处理相关逻辑 + }, (OnError) error -> { + data.setState(5); + //下载失败,处理相关逻辑 + }); + data.setState(2); + downloadingTask.add(data); + data.setDisposable(disposable); + } +} diff --git a/app/src/main/java/com/example/httpsender/DownloadMultiAdapter.java b/app/src/main/java/com/example/httpsender/DownloadMultiAdapter.java index 51199267..9391fc57 100644 --- a/app/src/main/java/com/example/httpsender/DownloadMultiAdapter.java +++ b/app/src/main/java/com/example/httpsender/DownloadMultiAdapter.java @@ -1,6 +1,8 @@ package com.example.httpsender; import android.annotation.SuppressLint; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -8,16 +10,11 @@ import android.widget.ProgressBar; import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - import com.example.httpsender.DownloadMultiAdapter.MyViewHolder; -import com.example.httpsender.entity.DownloadTask; -import com.example.httpsender.vm.MultiTaskDownloader; +import com.example.httpsender.entity.DownloadInfo; import java.text.DecimalFormat; import java.util.List; -import java.util.Locale; /** * User: ljx @@ -26,12 +23,12 @@ */ public class DownloadMultiAdapter extends RecyclerView.Adapter { - private OnItemClickListener mOnItemClickListener; + private OnItemClickListener mOnItemClickListener; - private List mDownloadTasks; + private List mDownloadInfos; - public DownloadMultiAdapter(List downloadTasks) { - mDownloadTasks = downloadTasks; + public DownloadMultiAdapter(List downloadInfos) { + mDownloadInfos = downloadInfos; } @NonNull @@ -44,9 +41,9 @@ public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { @SuppressLint("DefaultLocale") @Override public void onBindViewHolder(@NonNull MyViewHolder viewHolder, int i) { - DownloadTask data = mDownloadTasks.get(i); - viewHolder.progressBar.setProgress((int) (data.getProgress()*100)); - viewHolder.tvProgress.setText(String.format("%.1f%%", data.getProgress() * 100)); + DownloadInfo data = mDownloadInfos.get(i); + viewHolder.progressBar.setProgress(data.getProgress()); + viewHolder.tvProgress.setText(String.format("%d%%", data.getProgress())); viewHolder.btPause.setOnClickListener(v -> { mOnItemClickListener.onItemClick(v, data, i); }); @@ -55,64 +52,42 @@ public void onBindViewHolder(@NonNull MyViewHolder viewHolder, int i) { viewHolder.tvSize.setText(String.format("%sM/%sM", currentSize, totalSize)); int state = data.getState(); - if (state == MultiTaskDownloader.IDLE) { + if (state == 0) { viewHolder.tvWaiting.setText("未开始"); viewHolder.btPause.setText("开始"); - } else if (state == MultiTaskDownloader.WAITING) { + } else if (state == 1) { viewHolder.tvWaiting.setText("等待中.."); viewHolder.btPause.setText("取消"); - } else if (state == MultiTaskDownloader.DOWNLOADING) { - viewHolder.tvWaiting.setText(getSpeed(data.getSpeed())); - long remainingTime = data.getRemainingTime(); - if (remainingTime > 0) { - viewHolder.tvWaiting.append(" 预估还需" + getRemainingTime(remainingTime)); - } + } else if (state == 2) { + viewHolder.tvWaiting.setText("下载中.."); viewHolder.btPause.setText("暂停"); - } else if (state == MultiTaskDownloader.PAUSED) { + } else if (state == 3) { viewHolder.tvWaiting.setText("已暂停"); - viewHolder.btPause.setText("继续下载"); - } else if (state == MultiTaskDownloader.COMPLETED) { + viewHolder.btPause.setText("继续"); + } else if (state == 4) { viewHolder.tvWaiting.setText("已完成"); viewHolder.btPause.setText("已完成"); - } else if (state == MultiTaskDownloader.FAIL) { + } else if (state == 5) { viewHolder.tvWaiting.setText("下载失败"); - viewHolder.btPause.setText("重新下载"); - } else if (state == MultiTaskDownloader.CANCEL) { + viewHolder.btPause.setText("下载失败"); + } else if (state == 6) { viewHolder.tvWaiting.setText("已取消"); - viewHolder.btPause.setText("继续下载"); - } - } - - private String getSpeed(long speed) { - float kb = speed * 1.0f / 1024; - if (kb > 1000) { - return String.format(Locale.getDefault(), "%.2fMB/s", kb / 1024); - } else { - return ((int) kb) + "KB/s"; + viewHolder.btPause.setText("重新下载"); } - } - private String getRemainingTime(long time) { - if (time < 300) { - return time + "秒"; - } else { - long minute = time / 60; - if (time % 60 > 0) minute++; - return minute + "分"; - } } @Override public int getItemCount() { - return mDownloadTasks.size(); + return mDownloadInfos.size(); } @Override public long getItemId(int position) { - return mDownloadTasks.get(position).hashCode(); + return mDownloadInfos.get(position).hashCode(); } - static class MyViewHolder extends RecyclerView.ViewHolder { + class MyViewHolder extends RecyclerView.ViewHolder { ProgressBar progressBar; TextView tvProgress; @@ -132,7 +107,7 @@ static class MyViewHolder extends RecyclerView.ViewHolder { } - public void setOnItemClickListener(OnItemClickListener onItemClickListener) { + public void setOnItemClickListener(OnItemClickListener onItemClickListener) { mOnItemClickListener = onItemClickListener; } diff --git a/app/src/main/java/com/example/httpsender/ExceptionHelper.java b/app/src/main/java/com/example/httpsender/ExceptionHelper.java index d3e43e0b..b21519cc 100644 --- a/app/src/main/java/com/example/httpsender/ExceptionHelper.java +++ b/app/src/main/java/com/example/httpsender/ExceptionHelper.java @@ -5,6 +5,11 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.concurrent.TimeoutException; + /** * 异常处理帮助类 * User: ljx @@ -13,10 +18,27 @@ */ public class ExceptionHelper { - @SuppressWarnings("deprecation") + //处理网络异常 + public static String handleNetworkException(T throwable) { + int stringId = -1; + if (throwable instanceof UnknownHostException) { + if (!isNetworkConnected(AppHolder.getInstance())) { + stringId = R.string.network_error; + } else { + stringId = R.string.notify_no_network; + } + } else if (throwable instanceof SocketTimeoutException || throwable instanceof TimeoutException) { + //前者是通过OkHttpClient设置的超时引发的异常,后者是对单个请求调用timeout方法引发的超时异常 + stringId = R.string.time_out_please_try_again_later; + } else if (throwable instanceof ConnectException) { + stringId = R.string.esky_service_exception; + } + return stringId == -1 ? null : AppHolder.getInstance().getString(stringId); + } + public static boolean isNetworkConnected(Context context) { if (context != null) { - ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + ConnectivityManager mConnectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); if (mNetworkInfo != null) { return mNetworkInfo.isAvailable(); diff --git a/app/src/main/java/com/example/httpsender/LoggingEventListener.kt b/app/src/main/java/com/example/httpsender/LoggingEventListener.kt deleted file mode 100644 index a34a40b2..00000000 --- a/app/src/main/java/com/example/httpsender/LoggingEventListener.kt +++ /dev/null @@ -1,159 +0,0 @@ -package com.example.httpsender - -import android.util.Log -import okhttp3.Call -import okhttp3.Connection -import okhttp3.EventListener -import okhttp3.Handshake -import okhttp3.HttpUrl -import okhttp3.Protocol -import okhttp3.Request -import okhttp3.Response -import java.io.IOException -import java.net.InetAddress -import java.net.InetSocketAddress -import java.net.Proxy -import java.util.concurrent.TimeUnit - -/** - * User: ljx - * Date: 2022/4/22 - * Time: 16:05 - */ -class LoggingEventListener : EventListener() { - private var startNs: Long = 0 - - override fun callStart(call: Call) { - startNs = System.nanoTime() - - logWithTime("callStart: ${call.request()}") - } - - override fun proxySelectStart(call: Call, url: HttpUrl) { - logWithTime("proxySelectStart: $url") - } - - override fun proxySelectEnd(call: Call, url: HttpUrl, proxies: List) { - logWithTime("proxySelectEnd: $proxies") - } - - override fun dnsStart(call: Call, domainName: String) { - logWithTime("dnsStart: $domainName") - } - - override fun dnsEnd(call: Call, domainName: String, inetAddressList: List) { - logWithTime("dnsEnd: $inetAddressList") - } - - override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) { - logWithTime("connectStart: $inetSocketAddress $proxy") - } - - override fun secureConnectStart(call: Call) { - logWithTime("secureConnectStart") - } - - override fun secureConnectEnd(call: Call, handshake: Handshake?) { - logWithTime("secureConnectEnd: $handshake") - } - - override fun connectEnd( - call: Call, - inetSocketAddress: InetSocketAddress, - proxy: Proxy, - protocol: Protocol? - ) { - logWithTime("connectEnd: $protocol") - } - - override fun connectFailed( - call: Call, - inetSocketAddress: InetSocketAddress, - proxy: Proxy, - protocol: Protocol?, - ioe: IOException - ) { - logWithTime("connectFailed: $protocol $ioe") - } - - override fun connectionAcquired(call: Call, connection: Connection) { - logWithTime("connectionAcquired: $connection") - } - - override fun connectionReleased(call: Call, connection: Connection) { - logWithTime("connectionReleased") - } - - override fun requestHeadersStart(call: Call) { - logWithTime("requestHeadersStart") - } - - override fun requestHeadersEnd(call: Call, request: Request) { - logWithTime("requestHeadersEnd") - } - - override fun requestBodyStart(call: Call) { - logWithTime("requestBodyStart") - } - - override fun requestBodyEnd(call: Call, byteCount: Long) { - logWithTime("requestBodyEnd: byteCount=$byteCount") - } - - override fun requestFailed(call: Call, ioe: IOException) { - logWithTime("requestFailed: $ioe") - } - - override fun responseHeadersStart(call: Call) { - logWithTime("responseHeadersStart") - } - - override fun responseHeadersEnd(call: Call, response: Response) { - logWithTime("responseHeadersEnd: $response") - } - - override fun responseBodyStart(call: Call) { - logWithTime("responseBodyStart") - } - - override fun responseBodyEnd(call: Call, byteCount: Long) { - logWithTime("responseBodyEnd: byteCount=$byteCount") - } - - override fun responseFailed(call: Call, ioe: IOException) { - logWithTime("responseFailed: $ioe") - } - - override fun callEnd(call: Call) { - logWithTime("callEnd") - } - - override fun callFailed(call: Call, ioe: IOException) { - logWithTime("callFailed: $ioe") - } - - override fun canceled(call: Call) { - logWithTime("canceled") - } - - override fun satisfactionFailure(call: Call, response: Response) { - logWithTime("satisfactionFailure: $response") - } - - override fun cacheHit(call: Call, response: Response) { - logWithTime("cacheHit: $response") - } - - override fun cacheMiss(call: Call) { - logWithTime("cacheMiss") - } - - override fun cacheConditionalHit(call: Call, cachedResponse: Response) { - logWithTime("cacheConditionalHit: $cachedResponse") - } - - private fun logWithTime(message: String) { - val timeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs) - Log.e("LJX", "[$timeMs ms] $message") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/MainActivity.java b/app/src/main/java/com/example/httpsender/MainActivity.java index a158c812..43e98b5a 100644 --- a/app/src/main/java/com/example/httpsender/MainActivity.java +++ b/app/src/main/java/com/example/httpsender/MainActivity.java @@ -1,123 +1,338 @@ package com.example.httpsender; -import android.content.Context; +import android.content.Intent; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; +import android.os.Environment; import android.view.View; -import android.view.ViewGroup.LayoutParams; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; -import androidx.activity.EdgeToEdge; -import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.graphics.Insets; -import androidx.core.view.OnApplyWindowInsetsListener; -import androidx.core.view.ViewCompat; -import androidx.core.view.WindowInsetsCompat; import androidx.databinding.DataBindingUtil; -import androidx.fragment.app.Fragment; -import com.example.httpsender.adapter.FragmentPageAdapter; import com.example.httpsender.databinding.MainActivityBinding; -import com.example.httpsender.fragment.AwaitFragment; -import com.example.httpsender.fragment.FlowFragment; -import com.example.httpsender.fragment.MultiDownloadFragment; -import com.example.httpsender.fragment.RxJavaFragment; -import com.example.httpsender.view.ScaleTransitionPagerTitleView; - -import net.lucode.hackware.magicindicator.MagicIndicator; -import net.lucode.hackware.magicindicator.ViewPagerHelper; -import net.lucode.hackware.magicindicator.buildins.UIUtil; -import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator; -import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.CommonNavigatorAdapter; -import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerIndicator; -import net.lucode.hackware.magicindicator.buildins.commonnavigator.abs.IPagerTitleView; -import net.lucode.hackware.magicindicator.buildins.commonnavigator.indicators.LinePagerIndicator; -import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.SimplePagerTitleView; +import com.example.httpsender.entity.Article; +import com.example.httpsender.entity.Location; +import com.example.httpsender.entity.Name; +import com.example.httpsender.entity.NewsDataXml; +import com.google.gson.Gson; +import com.rxjava.rxlife.RxLife; +import java.io.File; import java.util.ArrayList; import java.util.List; +import io.reactivex.android.schedulers.AndroidSchedulers; +import rxhttp.wrapper.param.RxHttp; + public class MainActivity extends AppCompatActivity { private MainActivityBinding mBinding; - private final List mDataList = new ArrayList<>(); - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - EdgeToEdge.enable(this); mBinding = DataBindingUtil.setContentView(this, R.layout.main_activity); - ViewCompat.setOnApplyWindowInsetsListener(mBinding.getRoot(), new OnApplyWindowInsetsListener() { - @NonNull - @Override - public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { - Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); - mBinding.magicIndicator.setPadding(0, systemBars.top, 0, 0); - LayoutParams layoutParams = mBinding.magicIndicator.getLayoutParams(); - layoutParams.height = (int) (getResources().getDisplayMetrics().density * 50 + systemBars.top); - mBinding.magicIndicator.setLayoutParams(layoutParams); - return insets; - } - }); - mBinding.viewPager.setOffscreenPageLimit(4); - mDataList.add("RxJava"); - mDataList.add("Await"); - mDataList.add("Flow"); - mDataList.add("Multi Download"); - List fragments = new ArrayList<>(); - fragments.add(new RxJavaFragment()); - fragments.add(new AwaitFragment()); - fragments.add(new FlowFragment()); - fragments.add(new MultiDownloadFragment()); - mBinding.viewPager.setAdapter(new FragmentPageAdapter(getSupportFragmentManager(), fragments, mDataList)); - initMagicIndicator(); - } - - public void initMagicIndicator() { - MagicIndicator magicIndicator = mBinding.magicIndicator; - CommonNavigator commonNavigator = new CommonNavigator(this); - commonNavigator.setAdapter(new CommonNavigatorAdapter() { - @Override - public int getCount() { - return mDataList == null ? 0 : mDataList.size(); - } - - @Override - public IPagerTitleView getTitleView(Context context, final int index) { - SimplePagerTitleView simplePagerTitleView = new ScaleTransitionPagerTitleView(context); - simplePagerTitleView.setPadding(dp2px(5), 0, 0, 0); - simplePagerTitleView.setText(mDataList.get(index)); - simplePagerTitleView.setTextSize(18); - simplePagerTitleView.setNormalColor(Color.parseColor("#c8e6c9")); - simplePagerTitleView.setSelectedColor(Color.WHITE); - simplePagerTitleView.setOnClickListener(v -> mBinding.viewPager.setCurrentItem(index)); - return simplePagerTitleView; - } - - @Override - public IPagerIndicator getIndicator(Context context) { - LinePagerIndicator indicator = new LinePagerIndicator(context); - indicator.setMode(LinePagerIndicator.MODE_EXACTLY); - indicator.setLineHeight(UIUtil.dip2px(context, 3)); - indicator.setLineWidth(UIUtil.dip2px(context, 30)); - indicator.setRoundRadius(UIUtil.dip2px(context, 3)); - indicator.setStartInterpolator(new AccelerateInterpolator()); - indicator.setYOffset(dp2px(3)); - indicator.setEndInterpolator(new DecelerateInterpolator(2.0f)); - indicator.setColors(Color.WHITE); - return indicator; - } - }); - magicIndicator.setNavigator(commonNavigator); - ViewPagerHelper.bind(magicIndicator, mBinding.viewPager); - } - - private int dp2px(double dpValue) { - float density = getResources().getDisplayMetrics().density; - return (int) (dpValue * density + 0.5); } + + public void bitmap(View view) { + String imageUrl = "http://img2.shelinkme.cn/d3/photos/0/017/022/755_org.jpg@!normal_400_400?1558517697888"; + RxHttp.get(imageUrl) //Get请求 + .asBitmap() //这里返回Observable 对象 + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(bitmap -> { + mBinding.tvResult.setBackground(new BitmapDrawable(bitmap)); + //成功回调 + }, (OnError) error -> { + mBinding.tvResult.setText(error.getErrorMsg()); + //失败回调 + error.show("图片加载失败,请稍后再试!"); + }); + } + + //发送Get请求,获取文章列表 + public void sendGet(View view) { + RxHttp.get("/article/list/0/json") + .asResponsePageList(Article.class) + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(pageList -> { + mBinding.tvResult.setText(new Gson().toJson(pageList)); + //成功回调 + }, (OnError) error -> { + mBinding.tvResult.setText(error.getErrorMsg()); + //失败回调 + error.show("发送失败,请稍后再试!"); + }); + } + + //发送Post表单请求,根据关键字查询文章 + public void sendPostForm(View view) { + RxHttp.postForm("/article/query/0/json") + .add("k", "性能优化") + .asResponsePageList(Article.class) + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(pageList -> { + mBinding.tvResult.setText(new Gson().toJson(pageList)); + //成功回调 + }, (OnError) error -> { + mBinding.tvResult.setText(error.getErrorMsg()); + //失败回调 + error.show("发送失败,请稍后再试!"); + }); + } + + //发送Post Json请求,此接口不通,仅用于调试参数 + public void sendPostJson(View view) { + //发送以下User对象 + /* + { + "name": "张三", + "sex": 1, + "height": 180, + "weight": 70, + "interest": [ + "羽毛球", + "游泳" + ], + "location": { + "latitude": 30.7866, + "longitude": 120.6788 + }, + "address": { + "street": "科技园路.", + "city": "江苏苏州", + "country": "中国" + } + } + */ + List interestList = new ArrayList<>();//爱好 + interestList.add("羽毛球"); + interestList.add("游泳"); + String address = "{\"street\":\"科技园路.\",\"city\":\"江苏苏州\",\"country\":\"中国\"}"; + + RxHttp.postJson("/article/list/0/json") + .add("name", "张三") + .add("sex", 1) + .addAll("{\"height\":180,\"weight\":70}") //通过addAll系列方法添加多个参数 + .add("interest", interestList) //添加数组对象 + .add("location", new Location(120.6788, 30.7866)) //添加位置对象 + .addJsonElement("address", address) //通过字符串添加一个对象 + .asString() + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(s -> { + mBinding.tvResult.setText(s); + //成功回调 + }, (OnError) error -> { + mBinding.tvResult.setText(error.getErrorMsg()); + //失败回调 + error.show("发送失败,请稍后再试!"); + }); + } + + + //发送Post JsonArray请求,此接口不通,仅用于调试参数 + public void sendPostJsonArray(View view) { + //发送以下Json数组 + /* + [ + { + "name": "张三" + }, + { + "name": "李四" + }, + { + "name": "王五" + }, + { + "name": "赵六" + }, + { + "name": "杨七" + } + ] + */ + List names = new ArrayList<>(); + names.add(new Name("赵六")); + names.add(new Name("杨七")); + RxHttp.postJsonArray("/article/list/0/json") + .add("name", "张三") + .add(new Name("李四")) + .addJsonElement("{\"name\":\"王五\"}") + .addAll(names) + .asString() + .as(RxLife.asOnMain(this)) + .subscribe(s -> { + mBinding.tvResult.setText(s); + }, (OnError) error -> { + mBinding.tvResult.setText(error.getErrorMsg()); + //失败回调 + error.show("发送失败,请稍后再试!"); + }); + } + + //使用XmlConverter解析数据,此接口返回数据太多,会有点慢 + public void xmlConverter(View view) { + RxHttp.get("http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=sf-muni") + .setXmlConverter() + .asObject(NewsDataXml.class) + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(dataXml -> { + mBinding.tvResult.setText(new Gson().toJson(dataXml)); + //成功回调 + }, (OnError) error -> { + mBinding.tvResult.setText(error.getErrorMsg()); + //失败回调 + error.show("发送失败,请稍后再试!"); + }); + } + + //使用XmlConverter解析数据 + public void fastJsonConverter(View view) { + RxHttp.get("/article/list/0/json") + .setFastJsonConverter() + .asResponsePageList(Article.class) + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(pageList -> { + mBinding.tvResult.setText(new Gson().toJson(pageList)); + //成功回调 + }, (OnError) error -> { + mBinding.tvResult.setText(error.getErrorMsg()); + //失败回调 + error.show("发送失败,请稍后再试!"); + }); + } + + //文件下载,不带进度 + public void download(View view) { + String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk"; + RxHttp.get("/miaolive/Miaolive.apk") + .setDomainToUpdateIfAbsent() //使用指定的域名 + .asDownload(destPath) + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(s -> { + //下载成功,回调文件下载路径 + }, (OnError) error -> { + //下载失败 + error.show("下载失败,请稍后再试!"); + }); + } + + //文件下载,带进度 + public void downloadAndProgress(View view) { + //文件存储路径 + String destPath = getExternalCacheDir() + "/" + System.currentTimeMillis() + ".apk"; + RxHttp.get("/miaolive/Miaolive.apk") + .setDomainToUpdateIfAbsent()//使用指定的域名 + .asDownload(destPath, progress -> { + //下载进度回调,0-100,仅在进度有更新时才会回调,最多回调101次,最后一次回调文件存储路径 + int currentProgress = progress.getProgress(); //当前进度 0-100 + long currentSize = progress.getCurrentSize(); //当前已下载的字节大小 + long totalSize = progress.getTotalSize(); //要下载的总字节大小 + mBinding.tvResult.append("\n" + progress.toString()); + }, AndroidSchedulers.mainThread()) //指定回调(进度/成功/失败)线程,不指定,默认在请求所在线程回调 + .as(RxLife.as(this)) //感知生命周期 + .subscribe(s -> { + //下载完成,处理相关逻辑 + mBinding.tvResult.append("\n下载成功 : " + s); + }, (OnError) error -> { + mBinding.tvResult.append("\n" + error.getErrorMsg()); + //下载失败,处理相关逻辑 + error.show("下载失败,请稍后再试!"); + }); + } + + //断点下载 + public void breakpointDownload(View view) { + String destPath = getExternalCacheDir() + "/" + "Miaobo.apk"; + long length = new File(destPath).length(); + RxHttp.get("/miaolive/Miaolive.apk") + .setDomainToUpdateIfAbsent() //使用指定的域名 + .setRangeHeader(length) //设置开始下载位置,结束位置默认为文件末尾 + .asDownload(destPath) //注意这里使用DownloadParser解析器,并传入本地路径 + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(s -> { + //下载成功,回调文件下载路径 + }, (OnError) error -> { + //下载失败 + error.show("下载失败,请稍后再试!"); + }); + } + + //断点下载,带进度 + public void breakpointDownloadAndProgress(View view) { + String destPath = getExternalCacheDir() + "/" + "Miaobo.apk"; + long length = new File(destPath).length(); + RxHttp.get("/miaolive/Miaolive.apk") + .setDomainToUpdateIfAbsent()//使用指定的域名 + .setRangeHeader(length) //设置开始下载位置,结束位置默认为文件末尾 + .asDownload(destPath, length, progress -> { //如果需要衔接上次的下载进度,则需要传入上次已下载的字节数length + //下载进度回调,0-100,仅在进度有更新时才会回调 + int currentProgress = progress.getProgress(); //当前进度 0-100 + long currentSize = progress.getCurrentSize(); //当前已下载的字节大小 + long totalSize = progress.getTotalSize(); //要下载的总字节大小 + mBinding.tvResult.append("\n" + progress.toString()); + }, AndroidSchedulers.mainThread()) //指定回调(进度/成功/失败)线程,不指定,默认在请求所在线程回调 + .as(RxLife.as(this)) //加入感知生命周期的观察者 + .subscribe(s -> { + //下载成功 + mBinding.tvResult.append("\n下载成功 : " + s); + }, (OnError) error -> { + //下载失败 + mBinding.tvResult.append("\n" + error.getErrorMsg()); + error.show("下载失败,请稍后再试!"); + }); + } + + + //文件上传,不带进度 + public void upload(View v) { + RxHttp.postForm("http://t.xinhuo.com/index.php/Api/Pic/uploadPic") + .addFile("uploaded_file", new File(Environment.getExternalStorageDirectory(), "1.jpg")) + .asString() //from操作符,是异步操作 + .as(RxLife.asOnMain(this)) //感知生命周期,并在主线程回调 + .subscribe(s -> { + mBinding.tvResult.append("\n"); + mBinding.tvResult.append(s); + //成功回调 + }, (OnError) error -> { + mBinding.tvResult.append("\n"); + mBinding.tvResult.append(error.getErrorMsg()); + //失败回调 + error.show("上传失败,请稍后再试!"); + }); + } + + //上传文件,带进度 + public void uploadAndProgress(View v) { + RxHttp.postForm("http://t.xinhuo.com/index.php/Api/Pic/uploadPic") + .addFile("uploaded_file", new File(Environment.getExternalStorageDirectory(), "1.jpg")) + .asUpload(progress -> { + //上传进度回调,0-100,仅在进度有更新时才会回调 + int currentProgress = progress.getProgress(); //当前进度 0-100 + long currentSize = progress.getCurrentSize(); //当前已上传的字节大小 + long totalSize = progress.getTotalSize(); //要上传的总字节大小 + mBinding.tvResult.append("\n" + progress.toString()); + }, AndroidSchedulers.mainThread()) //指定回调(进度/成功/失败)线程,不指定,默认在请求所在线程回调 + .as(RxLife.as(this)) //加入感知生命周期的观察者 + .subscribe(s -> { + //上传成功 + mBinding.tvResult.append("\n上传成功 : " + s); + }, (OnError) error -> { + //上传失败 + mBinding.tvResult.append("\n" + error.getErrorMsg()); + error.show("上传失败,请稍后再试!"); + }); + } + + //多任务下载 + public void multitaskDownload(View view) { + startActivity(new Intent(this, DownloadMultiActivity.class)); + } + + public void clearLog(View view) { + mBinding.tvResult.setText(""); + mBinding.tvResult.setBackgroundColor(Color.TRANSPARENT); + } } diff --git a/app/src/main/java/com/example/httpsender/MyViewModel.java b/app/src/main/java/com/example/httpsender/MyViewModel.java new file mode 100644 index 00000000..8f4dfc29 --- /dev/null +++ b/app/src/main/java/com/example/httpsender/MyViewModel.java @@ -0,0 +1,30 @@ +package com.example.httpsender; + +import android.app.Application; +import androidx.annotation.NonNull; +import android.util.Log; + +import com.rxjava.rxlife.RxLife; +import com.rxjava.rxlife.ScopeViewModel; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.Observable; + +/** + * User: ljx + * Date: 2019-05-31 + * Time: 21:50 + */ +public class MyViewModel extends ScopeViewModel { + + + public MyViewModel(@NonNull Application application) { + super(application); + Observable.interval(1, 1, TimeUnit.SECONDS) + .as(RxLife.asOnMain(this)) + .subscribe(aLong -> { + Log.e("LJX", "MyViewModel aLong=" + aLong); + }); + } +} diff --git a/app/src/main/java/com/example/httpsender/MyViewModel.kt b/app/src/main/java/com/example/httpsender/MyViewModel.kt deleted file mode 100644 index 2b69c2f6..00000000 --- a/app/src/main/java/com/example/httpsender/MyViewModel.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.example.httpsender - -import android.app.Application -import android.util.Log -import androidx.lifecycle.viewModelScope -import com.example.httpsender.entity.Article -import com.example.httpsender.entity.PageList -import com.rxjava.rxlife.ScopeViewModel -import com.rxjava.rxlife.lifeOnMain -import io.reactivex.rxjava3.core.Observable -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.launch -import rxhttp.awaitResult -import rxhttp.retry -import rxhttp.timeout -import rxhttp.wrapper.param.RxHttp -import rxhttp.wrapper.param.toAwaitResponse -import java.util.concurrent.TimeUnit - -/** - * User: ljx - * Date: 2019-05-31 - * Time: 21:50 - */ -class MyViewModel(application: Application) : ScopeViewModel(application) { - - fun startInterval() { - Observable.interval(1, 1, TimeUnit.SECONDS) - .lifeOnMain(this) - .subscribe { Log.e("LJX", "MyViewModel aLong=$it") } - } - - fun testRetry() = viewModelScope.launch { - RxHttp.get("/article/list/0/json") - .toAwaitResponse>() - .timeout(100) - .retry(2, 1000) { - it is TimeoutCancellationException - } - .awaitResult { - Log.e("LJX", "pageList=$it") - } - .onFailure { - Log.e("LJX", "it=$it") - } - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/OnError.java b/app/src/main/java/com/example/httpsender/OnError.java index 5fb27aac..6e113303 100644 --- a/app/src/main/java/com/example/httpsender/OnError.java +++ b/app/src/main/java/com/example/httpsender/OnError.java @@ -3,7 +3,7 @@ import com.example.httpsender.entity.ErrorInfo; -import io.reactivex.rxjava3.functions.Consumer; +import io.reactivex.functions.Consumer; /** diff --git a/app/src/main/java/com/example/httpsender/Presenter.java b/app/src/main/java/com/example/httpsender/Presenter.java new file mode 100644 index 00000000..f6236aa7 --- /dev/null +++ b/app/src/main/java/com/example/httpsender/Presenter.java @@ -0,0 +1,28 @@ +package com.example.httpsender; + +import androidx.lifecycle.LifecycleOwner; +import android.util.Log; + +import com.rxjava.rxlife.BaseScope; +import com.rxjava.rxlife.RxLife; + +import java.util.concurrent.TimeUnit; + +import io.reactivex.Observable; + +/** + * User: ljx + * Date: 2019-05-26 + * Time: 15:20 + */ +public class Presenter extends BaseScope { + + public Presenter(LifecycleOwner owner) { + super(owner); //添加生命周期监听 + Observable.interval(1, 1, TimeUnit.SECONDS) + .as(RxLife.as(this)) //这里的this 为Scope接口对象 + .subscribe(aLong -> { + Log.e("LJX", "accept aLong=" + aLong); + }); + } +} diff --git a/app/src/main/java/com/example/httpsender/Presenter.kt b/app/src/main/java/com/example/httpsender/Presenter.kt deleted file mode 100644 index 62735dff..00000000 --- a/app/src/main/java/com/example/httpsender/Presenter.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.example.httpsender - -import android.util.Log -import androidx.lifecycle.LifecycleOwner -import com.example.httpsender.entity.Article -import com.example.httpsender.entity.PageList -import com.rxjava.rxlife.BaseScope -import com.rxjava.rxlife.life -import io.reactivex.rxjava3.core.Observable -import kotlinx.coroutines.* -import rxhttp.* -import rxhttp.wrapper.cache.CacheMode -import rxhttp.wrapper.param.RxHttp -import rxhttp.wrapper.param.toAwaitResponse -import java.util.concurrent.TimeUnit - -/** - * User: ljx - * Date: 2019-05-26 - * Time: 15:20 - */ -class Presenter(owner: LifecycleOwner) : BaseScope(owner) { - - fun test() { - Observable.interval(1, 1, TimeUnit.SECONDS) - .life(this) //这里的this 为Scope接口对象 - .subscribe { Log.e("LJX", "accept aLong=$it") } - } - - fun testRetry() { - val coroutineScope: CoroutineScope = - CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) - coroutineScope.launch { - RxHttp.postForm("/article/query/0/json") - .add("k", "性能优化") - .setCacheMode(CacheMode.ONLY_NETWORK) - .toAwaitResponse>() - .delay(100) - .startDelay(100) - .onErrorReturnItem(PageList()) - .timeout(1000) - .retry(2, 1000) { - it is TimeoutCancellationException - } - .awaitResult { - Log.e("RxHttp", "onNext = $it") - }.onFailure { - Log.e("RxHttp", "onError = $it") - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/RxHttpManager.java b/app/src/main/java/com/example/httpsender/RxHttpManager.java index 81e3955f..ed262388 100644 --- a/app/src/main/java/com/example/httpsender/RxHttpManager.java +++ b/app/src/main/java/com/example/httpsender/RxHttpManager.java @@ -6,71 +6,76 @@ import java.io.File; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; + import okhttp3.OkHttpClient; -import rxhttp.RxHttpPlugins; import rxhttp.wrapper.annotation.Converter; -import rxhttp.wrapper.annotation.OkClient; import rxhttp.wrapper.callback.IConverter; import rxhttp.wrapper.converter.FastJsonConverter; import rxhttp.wrapper.converter.XmlConverter; import rxhttp.wrapper.cookie.CookieStore; import rxhttp.wrapper.param.Method; -import rxhttp.wrapper.ssl.HttpsUtils; -import rxhttp.wrapper.ssl.HttpsUtils.SSLParams; +import rxhttp.wrapper.param.RxHttp; +import rxhttp.wrapper.ssl.SSLSocketFactoryImpl; +import rxhttp.wrapper.ssl.X509TrustManagerImpl; /** - * 本类所有配置都是非必须的,根据自己需求选择就好 * User: ljx * Date: 2019-11-26 * Time: 20:44 */ public class RxHttpManager { - @Converter(name = "XmlConverter") //非必须 - public static IConverter xmlConverter = XmlConverter.create(); - @Converter(name = "FastJsonConverter") //非必须 + @Converter(name = "FastJsonConverter") public static IConverter fastJsonConverter = FastJsonConverter.create(); - - @OkClient(name = "SimpleClient", className = "Simple") //非必须 - public static OkHttpClient simpleClient = new OkHttpClient.Builder().build(); + @Converter(name = "XmlConverter") + public static IConverter xmlConverter = XmlConverter.create(); public static void init(Application context) { File file = new File(context.getExternalCacheDir(), "RxHttpCookie"); - SSLParams sslParams = HttpsUtils.getSslSocketFactory(); + X509TrustManager trustAllCert = new X509TrustManagerImpl(); + SSLSocketFactory sslSocketFactory = new SSLSocketFactoryImpl(trustAllCert); OkHttpClient client = new OkHttpClient.Builder() .cookieJar(new CookieStore(file)) .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) - .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager) //添加信任证书 + .sslSocketFactory(sslSocketFactory, trustAllCert) //添加信任证书 .hostnameVerifier((hostname, session) -> true) //忽略host验证 // .followRedirects(false) //禁制OkHttp的重定向操作,我们自己处理重定向 // .addInterceptor(new RedirectInterceptor()) // .addInterceptor(new TokenInterceptor()) .build(); + //RxHttp初始化,自定义OkHttpClient对象,非必须 + RxHttp.init(client, BuildConfig.DEBUG); //设置缓存策略,非必须 -// File cacheFile = new File(context.getExternalCacheDir(), "RxHttpCache"); - //RxHttp初始化,非必须 - RxHttpPlugins.init(client) //自定义OkHttpClient对象 - .setDebug(BuildConfig.DEBUG, false, 2) //调试模式/分段打印/json数据缩进空间 -// .setCache(cacheFile, 1000 * 100, CacheMode.REQUEST_NETWORK_FAILED_READ_CACHE) -// .setExcludeCacheKeys("time") //设置一些key,不参与cacheKey的组拼 -// .setResultDecoder(s -> s) //设置数据解密/解码器,非必须 -// .setConverter(FastJsonConverter.create()) //设置全局的转换器,非必须 - .setOnParamAssembly(p -> { //设置公共参数,非必须 - //1、可根据不同请求添加不同参数,每次发送请求前都会被回调 - //2、如果希望部分请求不回调这里,发请求前调用RxHttp#setAssemblyEnabled(false)即可 - Method method = p.getMethod(); - if (method.isGet()) { - p.add("method", "get"); - } else if (method.isPost()) { //Post请求 - p.add("method", "post"); - } - p.add("versionName", "1.0.0")//添加公共参数 - .add("time", System.currentTimeMillis()) - .addHeader("deviceType", "android"); //添加公共请求头 - }); +// File file = new File(context.getExternalCacheDir(), "RxHttpCache"); +// RxHttpPlugins.setCache(file, 1000 * 100, CacheMode.REQUEST_NETWORK_FAILED_READ_CACHE); +// RxHttpPlugins.setExcludeCacheKeys("time"); //设置一些key,不参与cacheKey的组拼 + + //设置数据解密/解码器,非必须 +// RxHttp.setResultDecoder(s -> s); + + //设置全局的转换器,非必须 +// RxHttp.setConverter(FastJsonConverter.create()); + + //设置公共参数,非必须 + RxHttp.setOnParamAssembly(p -> { + /*根据不同请求添加不同参数,子线程执行,每次发送请求前都会被回调 + 如果希望部分请求不回调这里,发请求前调用Param.setAssemblyEnabled(false)即可 + */ + Method method = p.getMethod(); + if (method.isGet()) { //Get请求 + + } else if (method.isPost()) { //Post请求 + + } + return p.add("versionName", "1.0.0")//添加公共参数 + .add("time", System.currentTimeMillis()) + .addHeader("deviceType", "android"); //添加公共请求头 + }); } } diff --git a/app/src/main/java/com/example/httpsender/ToolBarActivity.java b/app/src/main/java/com/example/httpsender/ToolBarActivity.java deleted file mode 100644 index 53acd96d..00000000 --- a/app/src/main/java/com/example/httpsender/ToolBarActivity.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.example.httpsender; - -import android.os.Build; -import android.view.MenuItem; -import android.view.ViewGroup; - -import androidx.annotation.LayoutRes; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; -import androidx.databinding.DataBindingUtil; -import androidx.databinding.ViewDataBinding; - - -/** - * 需要ToolBar的activity都必须继承本类 - */ -public abstract class ToolBarActivity extends AppCompatActivity { - - protected Toolbar toolbar; - protected ActionBar actionBar; - private ViewGroup mContainer; - - public T bindingInflate(@LayoutRes int layoutResID) { - initView(); - return DataBindingUtil.inflate(getLayoutInflater(), layoutResID, mContainer, true); - } - - @Override - public void setContentView(@LayoutRes int layoutResID) { - initView(); - getLayoutInflater().inflate(layoutResID, mContainer, true); - } - - private void initView() { - super.setContentView(R.layout.toolbar_activity); - mContainer = findViewById(R.id.container); - toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int itemId = item.getItemId(); - if (itemId == android.R.id.home) { - boolean popSuccess = getSupportFragmentManager().popBackStackImmediate(); - if (popSuccess) return true; - finish(); - } - return super.onOptionsItemSelected(item); - } - - public void setToolbarColor(int color) { - toolbar.setBackgroundColor(color); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - getWindow().setStatusBarColor(color); - } - } - - @Override - public void setTitle(CharSequence title) { - super.setTitle(title); - if (toolbar == null) return; - toolbar.setTitle(title); - } - - @Override - public void setTitle(int titleId) { - super.setTitle(titleId); - if (toolbar == null) return; - toolbar.setTitle(titleId); - } - - protected int dp2px(float dpValue) { - final float scale = getResources().getDisplayMetrics().density; - return (int) (dpValue * scale + 0.5f); - } -} diff --git a/app/src/main/java/com/example/httpsender/adapter/FragmentPageAdapter.java b/app/src/main/java/com/example/httpsender/adapter/FragmentPageAdapter.java deleted file mode 100644 index 71be75bf..00000000 --- a/app/src/main/java/com/example/httpsender/adapter/FragmentPageAdapter.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.example.httpsender.adapter; - - - -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; - -import java.util.Arrays; -import java.util.List; - - -/** - * User: ljx - * Date: 2018/6/9 - * Time: 13:53 - */ -public class FragmentPageAdapter extends FragmentPagerAdapter { - - private List mFragments; - private List mTitles; - - - public FragmentPageAdapter(FragmentManager fm, List fragments, String[] titles) { - this(fm, fragments, Arrays.asList(titles)); - } - - public FragmentPageAdapter(FragmentManager fm, List fragments, List titles) { - super(fm); - mFragments = fragments; - mTitles = titles; - } - - @Override - public int getCount() { - return mFragments.size(); - } - - @Override - public CharSequence getPageTitle(int position) { - return mTitles.get(position); - } - - @Override - public Fragment getItem(int position) { - return mFragments.get(position); - } -} diff --git a/app/src/main/java/com/example/httpsender/entity/Article.java b/app/src/main/java/com/example/httpsender/entity/Article.java index fe45f4eb..200cf6d6 100644 --- a/app/src/main/java/com/example/httpsender/entity/Article.java +++ b/app/src/main/java/com/example/httpsender/entity/Article.java @@ -284,20 +284,4 @@ public void setVisible(int visible) { public void setZan(int zan) { this.zan = zan; } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Article article = (Article) o; - - return id == article.id; - } - - @Override - public int hashCode() { - return id; - } } diff --git a/app/src/main/java/com/example/httpsender/entity/DownloadInfo.java b/app/src/main/java/com/example/httpsender/entity/DownloadInfo.java new file mode 100644 index 00000000..9125773b --- /dev/null +++ b/app/src/main/java/com/example/httpsender/entity/DownloadInfo.java @@ -0,0 +1,88 @@ +package com.example.httpsender.entity; + +import io.reactivex.disposables.Disposable; + +/** + * User: ljx + * Date: 2019-06-08 + * Time: 10:09 + */ +public class DownloadInfo { + + private int taskId; + + private String url; + private int progress; + + private long currentSize; + private long totalSize; + + private Disposable mDisposable; + + + private int state; //0=未开始 1=等待中 2=下载中 3=暂停中 4=已完成 5=下载失败 6=已取消 + + public int getTaskId() { + return taskId; + } + + public void setTaskId(int taskId) { + this.taskId = taskId; + } + + public int getState() { + return state; + } + + public void setState(int state) { + this.state = state; + } + + public Disposable getDisposable() { + return mDisposable; + } + + public void setDisposable(Disposable disposable) { + mDisposable = disposable; + } + + public boolean isDownloading() { + return mDisposable != null && !mDisposable.isDisposed(); + } + + public DownloadInfo(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public int getProgress() { + return progress; + } + + public void setProgress(int progress) { + this.progress = progress; + } + + public long getCurrentSize() { + return currentSize; + } + + public void setCurrentSize(long currentSize) { + this.currentSize = currentSize; + } + + public long getTotalSize() { + return totalSize; + } + + public void setTotalSize(long totalSize) { + this.totalSize = totalSize; + } +} diff --git a/app/src/main/java/com/example/httpsender/entity/DownloadTask.java b/app/src/main/java/com/example/httpsender/entity/DownloadTask.java deleted file mode 100644 index 3e8106bc..00000000 --- a/app/src/main/java/com/example/httpsender/entity/DownloadTask.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.example.httpsender.entity; - - - -/** - * User: ljx - * Date: 2019-06-08 - * Time: 10:09 - */ -public class DownloadTask { - - private String url; - private String localPath; - - private float progress; - private long currentSize; - private long totalSize; - - private long speed; - private long remainingTime; - - private int state; //0=未开始 1=等待中 2=下载中 3=暂停中 4=已完成 5=下载失败 6=已取消 - - - public DownloadTask(String url) { - this.url = url; - } - - public int getState() { - return state; - } - - public void setState(int state) { - this.state = state; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getLocalPath() { - return localPath; - } - - public void setLocalPath(String localPath) { - this.localPath = localPath; - } - - public long getSpeed() { - return speed; - } - - public void setSpeed(long speed) { - this.speed = speed; - } - - public long getRemainingTime() { - return remainingTime; - } - - public void setRemainingTime(long remainingTime) { - this.remainingTime = remainingTime; - } - - public float getProgress() { - return progress; - } - - public void setProgress(float progress) { - this.progress = progress; - } - - public long getCurrentSize() { - return currentSize; - } - - public void setCurrentSize(long currentSize) { - this.currentSize = currentSize; - } - - public long getTotalSize() { - return totalSize; - } - - public void setTotalSize(long totalSize) { - this.totalSize = totalSize; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - DownloadTask task = (DownloadTask) o; - - return url.equals(task.url); - } - - @Override - public int hashCode() { - return url.hashCode(); - } -} diff --git a/app/src/main/java/com/example/httpsender/entity/ErrorInfo.java b/app/src/main/java/com/example/httpsender/entity/ErrorInfo.java index 7b76d143..2979a9ea 100644 --- a/app/src/main/java/com/example/httpsender/entity/ErrorInfo.java +++ b/app/src/main/java/com/example/httpsender/entity/ErrorInfo.java @@ -4,15 +4,9 @@ import com.example.httpsender.AppHolder; import com.example.httpsender.ExceptionHelper; -import com.example.httpsender.R; import com.example.httpsender.Tip; import com.google.gson.JsonSyntaxException; -import java.net.ConnectException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; -import java.util.concurrent.TimeoutException; - import rxhttp.wrapper.exception.HttpStatusCodeException; import rxhttp.wrapper.exception.ParseException; @@ -30,34 +24,19 @@ public class ErrorInfo { public ErrorInfo(Throwable throwable) { this.throwable = throwable; - String errorMsg = null; - if (throwable instanceof UnknownHostException) { - if (!ExceptionHelper.isNetworkConnected(AppHolder.getInstance())) { - errorMsg = getString(R.string.network_error); - } else { - errorMsg = getString(R.string.notify_no_network); - } - } else if (throwable instanceof SocketTimeoutException || throwable instanceof TimeoutException) { - //前者是通过OkHttpClient设置的超时引发的异常,后者是对单个请求调用timeout方法引发的超时异常 - errorMsg = getString(R.string.time_out_please_try_again_later); - } else if (throwable instanceof ConnectException) { - errorMsg = getString(R.string.esky_service_exception); - } else if (throwable instanceof HttpStatusCodeException) { //请求失败异常 + String errorMsg = ExceptionHelper.handleNetworkException(throwable); //网络异常 + if (throwable instanceof HttpStatusCodeException) { //请求失败异常 String code = throwable.getLocalizedMessage(); if ("416".equals(code)) { errorMsg = "请求范围不符合要求"; - } else { - errorMsg = throwable.getMessage(); } } else if (throwable instanceof JsonSyntaxException) { //请求成功,但Json语法异常,导致解析失败 errorMsg = "数据解析失败,请稍后再试"; } else if (throwable instanceof ParseException) { // ParseException异常表明请求成功,但是数据不正确 String errorCode = throwable.getLocalizedMessage(); - this.errorCode = Integer.parseInt(errorCode); + this.errorCode = Integer.valueOf(errorCode); errorMsg = throwable.getMessage(); if (TextUtils.isEmpty(errorMsg)) errorMsg = errorCode;//errorMsg为空,显示errorCode - } else { - errorMsg = throwable.getMessage(); } this.errorMsg = errorMsg; } @@ -94,8 +73,4 @@ public boolean show(int standbyMsg) { Tip.show(TextUtils.isEmpty(errorMsg) ? AppHolder.getInstance().getString(standbyMsg) : errorMsg); return true; } - - public String getString(int resId) { - return AppHolder.getInstance().getString(resId); - } } diff --git a/app/src/main/java/com/example/httpsender/entity/Name.java b/app/src/main/java/com/example/httpsender/entity/Name.java index 2eadd925..555f090d 100644 --- a/app/src/main/java/com/example/httpsender/entity/Name.java +++ b/app/src/main/java/com/example/httpsender/entity/Name.java @@ -12,12 +12,4 @@ public class Name { public Name(String name) { this.name = name; } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } } diff --git a/app/src/main/java/com/example/httpsender/entity/PageList.java b/app/src/main/java/com/example/httpsender/entity/PageList.java index daac1ba6..2306cb71 100644 --- a/app/src/main/java/com/example/httpsender/entity/PageList.java +++ b/app/src/main/java/com/example/httpsender/entity/PageList.java @@ -1,7 +1,6 @@ package com.example.httpsender.entity; -import java.util.ArrayList; import java.util.List; /** @@ -44,7 +43,7 @@ public void setTotal(int total) { this.total = total; } - public void setDatas(ArrayList datas) { + public void setDatas(List datas) { this.datas = datas; } } diff --git a/app/src/main/java/com/example/httpsender/entity/Url.kt b/app/src/main/java/com/example/httpsender/entity/Url.kt index 73083ab6..71b297b1 100644 --- a/app/src/main/java/com/example/httpsender/entity/Url.kt +++ b/app/src/main/java/com/example/httpsender/entity/Url.kt @@ -1,20 +1,22 @@ package com.example.httpsender.entity + import rxhttp.wrapper.annotation.DefaultDomain import rxhttp.wrapper.annotation.Domain /** * User: ljx - * Date: 2020/2/27 - * Time: 23:55 + * Date: 2019/3/27 + * Time: 10:36 */ -object Url { +class Url { - @JvmField - @DefaultDomain //设置为默认域名 - var baseUrl = "https://www.wanandroid.com/" + companion object { + @Domain(name = "Update") + const val update = "http://update.9158.com" - const val UPLOAD_URL = "http://t.xinhuo.com/index.php/Api/Pic/uploadPic" + @DefaultDomain //设置为默认域名 + const val baseUrl = "https://www.wanandroid.com/" + } - const val DOWNLOAD_URL = "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk" -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/httpsender/fragment/AwaitFragment.kt b/app/src/main/java/com/example/httpsender/fragment/AwaitFragment.kt deleted file mode 100644 index 3ee62c4d..00000000 --- a/app/src/main/java/com/example/httpsender/fragment/AwaitFragment.kt +++ /dev/null @@ -1,320 +0,0 @@ -package com.example.httpsender.fragment - -import android.graphics.Color -import android.net.Uri -import android.os.Bundle -import android.view.View -import androidx.lifecycle.lifecycleScope -import com.example.httpsender.R -import com.example.httpsender.databinding.AwaitFragmentBinding -import com.example.httpsender.entity.* -import com.example.httpsender.kt.errorMsg -import com.example.httpsender.kt.show -import com.google.gson.Gson -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.launch -import rxhttp.* -import rxhttp.wrapper.param.RxHttp -import rxhttp.wrapper.param.toAwaitResponse -import java.io.File -import java.util.* - -/** - * 使用 协程(RxHttp + Await) 发请求 - * - * ``` - * val user = RxHttp.postXxx("/service/...") - * .add("key", "value") - * .toAwait() - * .awaitResult { - * val user = it - * }.onFailure { - * val throwable = it - * } - *``` - * - * User: ljx - * Date: 2020/4/24 - * Time: 18:16 - */ -class AwaitFragment : BaseFragment(), View.OnClickListener { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.await_fragment) - } - - override fun AwaitFragmentBinding.onViewCreated(savedInstanceState: Bundle?) { - click = this@AwaitFragment - } - - //发送Get请求,获取文章列表 - private suspend fun AwaitFragmentBinding.sendGet(view: View) { - RxHttp.get("/article/list/0/json") - .toAwaitResponse>() - .toAwaitOkResponse() - .awaitResult { - val list = it.body() - val response = it.raw() - val headers = it.headers() - tvResult.text = Gson().toJson(list) - }.onFailure { - tvResult.text = it.errorMsg - //失败回调 - it.show() - } - } - - //发送Post表单请求,根据关键字查询文章 - private suspend fun AwaitFragmentBinding.sendPostForm(view: View) { - RxHttp.postForm("/article/query/0/json") - .add("k", "性能优化") - .toAwaitResponse>() - .awaitResult { - tvResult.text = Gson().toJson(it) - }.onFailure { - tvResult.text = it.errorMsg - //失败回调 - it.show() - } - } - - //发送Post Json请求,此接口不通,通过日志可以看到,发送出去的json对象 - private suspend fun AwaitFragmentBinding.sendPostJson(view: View) { - /* - 发送以下User对象 - {"name":"张三","sex":1,"height":180,"weight":70, - "interest":["羽毛球","游泳"], - "location":{"latitude":30.7866,"longitude":120.6788}, - "address":{"street":"科技园路.","city":"江苏苏州","country":"中国"}} - */ - val interestList: MutableList = ArrayList() //爱好 - interestList.add("羽毛球") - interestList.add("游泳") - val address = """ - {"street":"科技园路.","city":"江苏苏州","country":"中国"} - """.trimIndent() - RxHttp.postJson("/article/list/0/json") - .add("name", "张三") - .add("sex", 1) - .addAll("""{"height":180,"weight":70}""") //通过addAll系列方法添加多个参数 - .add("interest", interestList) //添加数组对象 - .add("location", Location(120.6788, 30.7866)) //添加位置对象 - .addJsonElement("address", address) //通过字符串添加一个对象 - .toAwaitString() - .awaitResult { - tvResult.text = it - }.onFailure { - tvResult.text = it.errorMsg - //失败回调 - it.show() - } - } - - //发送Post JsonArray请求,通过日志可以看到,发送出去的json数组 - private suspend fun AwaitFragmentBinding.sendPostJsonArray(view: View) { - /* - 发送以下Json数组 - [{"name":"张三"},{"name":"李四"},{"name":"王五"},{"name":"赵六"},{"name":"杨七"}] - */ - val names: MutableList = ArrayList() - names.add(Name("赵六")) - names.add(Name("杨七")) - RxHttp.postJsonArray("/article/list/0/json") - .add("name", "张三") - .add(Name("李四")) - .addJsonElement("""{"name":"王五"}""") - .addAll(names) - .toAwaitString() - .awaitResult { - tvResult.text = it - }.onFailure { - tvResult.text = it.errorMsg - //失败回调 - it.show() - } - - } - - //此接口不同,但通过日志可以看到,发送出去的是xml数据,如果收到也是xml数据,则会自动解析为我们指定的对象 - private suspend fun AwaitFragmentBinding.xmlConverter(view: View) { - RxHttp.postBody("http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=sf-muni") - .setBody(Name("张三")) - .setXmlConverter() - .toAwait() - .awaitResult { - tvResult.text = Gson().toJson(it) - }.onFailure { - tvResult.text = it.errorMsg - //失败回调 - it.show() - } - } - - /** - * android 10之前 或 沙盒目录(Android/data/packageName/)下的文件上传 - * - * 注意:这里并非通过 [Await] 实现的, 而是通过 [Flow] 监听的进度,因为在监听上传进度这块,Flow性能更优,且更简单 - * - * 如不需要监听进度,toFlow 方法不要传进度回调即可 - */ - private suspend fun AwaitFragmentBinding.upload(v: View) { - RxHttp.postForm(Url.UPLOAD_URL) - .addFile("file", File("xxxx/1.png")) - .toFlow() - .onProgress { - //上传进度回调,0-100,仅在进度有更新时才会回调 - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已上传的字节大小 - val totalSize = it.totalSize //要上传的总字节大小 - tvResult.append("\n$it") - }.catch { - tvResult.append("\n${it.errorMsg}") - //失败回调 - it.show() - }.collect { - tvResult.append("\n上传成功 : $it") - } - } - - /** - * android 10 及以上文件上传 ,兼容Android 10以下 - * - * 注意:这里并非通过 [Await] 实现的, 而是通过 [Flow] 监听的进度,因为在监听上传进度这块,Flow性能更优,且更简单 - * - * 如不需要监听进度,toFlow 方法不要传进度回调即可 - */ - private suspend fun AwaitFragmentBinding.uploadAndroid10(v: View) { - //真实环境,需要调用文件选择器,拿到Uri对象 - val uri = Uri.parse("content://media/external/downloads/13417") - RxHttp.postForm(Url.UPLOAD_URL) - .addPart(requireContext(), "file", uri) - .toFlow() - .onProgress { - //上传进度回调,0-100,仅在进度有更新时才会回调 - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已上传的字节大小 - val totalSize = it.totalSize //要上传的总字节大小 - tvResult.append("\n$it") - }.catch { - tvResult.append("\n${it.errorMsg}") - //失败回调 - it.show() - }.collect { - tvResult.append("\n上传成功 : $it") - } - } - - /** - * Android 10以下 或 下载文件到沙盒目录下,下载可以直接传入file的绝对路径 - * - * 如不需要监听下载进度,toDownload 方法不要传进度回调即可 - */ - private suspend fun AwaitFragmentBinding.download(view: View) { -// val destPath = "${requireContext().externalCacheDir}/${System.currentTimeMillis()}.apk" -// RxHttp.get(Url.DOWNLOAD_URL) -// .toDownloadAwait(destPath) { -// val currentProgress = it.progress //当前进度 0-100 -// val currentSize = it.currentSize //当前已下载的字节大小 -// val totalSize = it.totalSize //要下载的总字节大小 -// tvResult.append(it.toString()) -// }.awaitResult { -// tvResult.append("\n下载完成, $it") -// }.onFailure { -// //异常回调 -// tvResult.append("\n${it.errorMsg}") -// it.show() -// } - } - - /** - * 断点下载 - * Android 10以下 或 下载文件到沙盒目录下,下载可以直接传入file的绝对路径 - * 如不需要监听下载进度,toDownload 方法不要传进度回调即可 - */ - private suspend fun AwaitFragmentBinding.appendDownload(view: View) { -// val destPath = "${requireContext().externalCacheDir}/Miaobo.apk" -// RxHttp.get(Url.DOWNLOAD_URL) -// .toDownloadAwait(destPath, true) { -// val currentProgress = it.progress //当前进度 0-100 -// val currentSize = it.currentSize //当前已下载的字节大小 -// val totalSize = it.totalSize //要下载的总字节大小 -// tvResult.append(it.toString()) -// }.awaitResult { -// tvResult.append("\n下载完成, $it") -// }.onFailure { -// //异常回调 -// tvResult.append("\n${it.errorMsg}") -// it.show() -// } - } - - /** - * Android 10 及以上下载,兼容Android 10以下 - * 如不需要监听下载进度,toDownload 方法不要传进度回调即可 - */ - private suspend fun AwaitFragmentBinding.downloadAndroid10(view: View) { -// val factory = Android10DownloadFactory(requireContext(), "miaobo.apk") -// RxHttp.get(Url.DOWNLOAD_URL) -// .toDownloadAwait(factory) { -// val currentProgress = it.progress //当前进度 0-100 -// val currentSize = it.currentSize //当前已下载的字节大小 -// val totalSize = it.totalSize //要下载的总字节大小 -// tvResult.append(it.toString()) -// }.awaitResult { -// tvResult.append("\n下载完成, $it") -// }.onFailure { -// //异常回调 -// tvResult.append("\n${it.errorMsg}") -// it.show() -// } - } - - /** - * Android 10 及以上断点下载,兼容Android 10以下 - * 如不需要监听下载进度,toDownload 方法不要传进度回调即可 - */ - private suspend fun AwaitFragmentBinding.appendDownloadAndroid10(view: View) { -// val factory = Android10DownloadFactory(requireContext(), "miaobo.apk") -// RxHttp.get(Url.DOWNLOAD_URL) -// .toDownloadAwait(factory, true) { -// val currentProgress = it.progress //当前进度 0-100 -// val currentSize = it.currentSize //当前已下载的字节大小 -// val totalSize = it.totalSize //要下载的总字节大小 -// tvResult.append(it.toString()) -// }.awaitResult { -// tvResult.append("\n下载完成, $it") -// }.onFailure { -// //异常回调 -// tvResult.append("\n${it.errorMsg}") -// it.show() -// } - } - - - private fun AwaitFragmentBinding.clearLog(view: View) { - tvResult.text = "" - tvResult.setBackgroundColor(Color.TRANSPARENT) - } - - override fun onClick(v: View) { - mBinding.run { - lifecycleScope.launch { - when (v.id) { - R.id.sendGet -> sendGet(v) - R.id.sendPostForm -> sendPostForm(v) - R.id.sendPostJson -> sendPostJson(v) - R.id.sendPostJsonArray -> sendPostJsonArray(v) - R.id.xmlConverter -> xmlConverter(v) - R.id.upload -> upload(v) - R.id.upload10 -> uploadAndroid10(v) - R.id.download -> download(v) - R.id.download_append -> appendDownload(v) - R.id.download10 -> downloadAndroid10(v) - R.id.download10_append -> appendDownloadAndroid10(v) - R.id.bt_clear -> clearLog(v) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/fragment/BaseFragment.kt b/app/src/main/java/com/example/httpsender/fragment/BaseFragment.kt deleted file mode 100644 index b5ef559c..00000000 --- a/app/src/main/java/com/example/httpsender/fragment/BaseFragment.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.httpsender.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.annotation.LayoutRes -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding -import androidx.fragment.app.Fragment - -/** - * User: ljx - * Date: 2020/6/2 - * Time: 11:45 - */ -abstract class BaseFragment : Fragment() { - - @LayoutRes - private var layoutId = 0 - protected lateinit var mBinding: T - - fun setContentView(@LayoutRes layoutId: Int) { - this.layoutId = layoutId - } - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - mBinding = DataBindingUtil.inflate(inflater, layoutId, container, false) - return mBinding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - mBinding.onViewCreated(savedInstanceState) - } - - open fun T.onViewCreated(savedInstanceState: Bundle?) { - - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/fragment/FlowFragment.kt b/app/src/main/java/com/example/httpsender/fragment/FlowFragment.kt deleted file mode 100644 index 020aabdf..00000000 --- a/app/src/main/java/com/example/httpsender/fragment/FlowFragment.kt +++ /dev/null @@ -1,316 +0,0 @@ -package com.example.httpsender.fragment - -import android.graphics.Color -import android.net.Uri -import android.os.Bundle -import android.view.View -import androidx.lifecycle.lifecycleScope -import com.example.httpsender.R -import com.example.httpsender.databinding.FlowFragmentBinding -import com.example.httpsender.entity.Article -import com.example.httpsender.entity.Location -import com.example.httpsender.entity.Name -import com.example.httpsender.entity.NewsDataXml -import com.example.httpsender.entity.PageList -import com.example.httpsender.entity.Url -import com.example.httpsender.kt.errorMsg -import com.example.httpsender.kt.show -import com.example.httpsender.parser.Android10DownloadFactory -import com.google.gson.Gson -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.launch -import rxhttp.toDownloadFlow -import rxhttp.toFlow -import rxhttp.wrapper.param.RxHttp -import rxhttp.wrapper.param.toFlowResponse -import java.io.File - -/** - * 使用 协程(RxHttp + Flow) 发请求 - * - * ``` - * RxHttp.postXxx("/service/...") - * .add("key", "value") - * .toFlow() - * .catch { - * val throwable = it - * }.collect { - * val user = it - * } - *``` - * User: ljx - * Date: 2021/9/18 - * Time: 20:16 - */ -class FlowFragment : BaseFragment(), View.OnClickListener { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.flow_fragment) - } - - override fun FlowFragmentBinding.onViewCreated(savedInstanceState: Bundle?) { - click = this@FlowFragment - } - - //发送Get请求,获取文章列表 - private suspend fun FlowFragmentBinding.sendGet(view: View) { - RxHttp.get("/article/list/0/json") - .toFlowResponse>() - .catch { - tvResult.text = it.errorMsg - it.show() - }.collect { - tvResult.text = Gson().toJson(it) - } - } - - //发送Post表单请求,根据关键字查询文章 - private suspend fun FlowFragmentBinding.sendPostForm(view: View) { - RxHttp.postForm("/article/query/0/json") - .add("k", "性能优化") - .toFlowResponse>() - .catch { - tvResult.text = it.errorMsg - it.show() - }.collect { - tvResult.text = Gson().toJson(it) - } - } - - //发送Post Json请求,此接口不通,通过日志可以看到,发送出去的json对象 - private suspend fun FlowFragmentBinding.sendPostJson(view: View) { - /* - 发送以下User对象 - {"name":"张三","sex":1,"height":180,"weight":70, - "interest":["羽毛球","游泳"], - "location":{"latitude":30.7866,"longitude":120.6788}, - "address":{"street":"科技园路.","city":"江苏苏州","country":"中国"}} - */ - val interestList: MutableList = ArrayList() //爱好 - interestList.add("羽毛球") - interestList.add("游泳") - val address = """ - {"street":"科技园路.","city":"江苏苏州","country":"中国"} - """.trimIndent() - RxHttp.postJson("/article/list/0/json") - .add("name", "张三") - .add("sex", 1) - .addAll("""{"height":180,"weight":70}""") //通过addAll系列方法添加多个参数 - .add("interest", interestList) //添加数组对象 - .add("location", Location(120.6788, 30.7866)) //添加位置对象 - .addJsonElement("address", address) //通过字符串添加一个对象 - .toFlow() - .catch { - tvResult.text = it.errorMsg - it.show() - }.collect { - tvResult.text = it - } - } - - //发送Post JsonArray请求,通过日志可以看到,发送出去的json数组 - private suspend fun FlowFragmentBinding.sendPostJsonArray(view: View) { - /* - 发送以下Json数组 - [{"name":"张三"},{"name":"李四"},{"name":"王五"},{"name":"赵六"},{"name":"杨七"}] - */ - val names: MutableList = ArrayList() - names.add(Name("赵六")) - names.add(Name("杨七")) - RxHttp.postJsonArray("/article/list/0/json") - .add("name", "张三") - .add(Name("李四")) - .addJsonElement("""{"name":"王五"}""") - .addAll(names) - .toFlow() - .catch { - tvResult.text = it.errorMsg - it.show() - }.collect { - tvResult.text = it - } - - } - - //此接口不同,但通过日志可以看到,发送出去的是xml数据,如果收到也是xml数据,则会自动解析为我们指定的对象 - private suspend fun FlowFragmentBinding.xmlConverter(view: View) { - RxHttp.postBody("http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=sf-muni") - .setBody(Name("张三")) - .setXmlConverter() - .toFlow() - .catch { - tvResult.text = it.errorMsg - it.show() - }.collect { - tvResult.text = Gson().toJson(it) - } - } - - /** - * android 10之前 或 沙盒目录(Android/data/packageName/)下的文件上传 - * - * 如不需要监听进度,toFlow 方法不要传进度回调即可 - */ - private suspend fun FlowFragmentBinding.upload(v: View) { - RxHttp.postForm(Url.UPLOAD_URL) - .addFile("file", File("xxxx/1.png")) - .toFlow() - .onProgress { - //上传进度回调,0-100,仅在进度有更新时才会回调 - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已上传的字节大小 - val totalSize = it.totalSize //要上传的总字节大小 - tvResult.append("\n$it") - }.catch { - tvResult.append("\n${it.errorMsg}") - //失败回调 - it.show() - }.collect { - tvResult.append("\n上传成功 : $it") - } - } - - /** - * android 10 及以上文件上传 ,兼容Android 10以下 - * - * 如不需要监听进度,toFlow 方法不要传进度回调即可 - */ - private suspend fun FlowFragmentBinding.uploadAndroid10(v: View) { - //真实环境,需要调用文件选择器,拿到Uri对象 - val uri = Uri.parse("content://media/external/downloads/13417") - RxHttp.postForm(Url.UPLOAD_URL) - .addPart(requireContext(), "file", uri) - .toFlow() - .onProgress { - //上传进度回调,0-100,仅在进度有更新时才会回调 - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已上传的字节大小 - val totalSize = it.totalSize //要上传的总字节大小 - tvResult.append("\n$it") - }.catch { - tvResult.append("\n${it.errorMsg}") - //失败回调 - it.show() - }.collect { - tvResult.append("\n上传成功 : $it") - } - } - - /** - * Android 10以下 或 下载文件到沙盒目录下,下载可以直接传入file的绝对路径 - * - * 如不需要监听下载进度,toFlow 方法不要传进度回调即可 - */ - private suspend fun FlowFragmentBinding.download(view: View) { - val destPath = "${requireContext().externalCacheDir}/${System.currentTimeMillis()}.apk" - RxHttp.get(Url.DOWNLOAD_URL) - .toDownloadFlow(destPath) - .onProgress { - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已下载的字节大小 - val totalSize = it.totalSize //要下载的总字节大小 - tvResult.append("\n$it") - }.catch { - tvResult.append("\n${it.errorMsg}") //异常回调 - it.show() - }.collect { - tvResult.append("\n下载完成, $it") - } - } - - /** - * 断点下载 - * Android 10以下 或 下载文件到沙盒目录下,下载可以直接传入file的绝对路径 - * - * 如不需要监听下载进度,toFlow 方法不要传进度回调即可 - */ - private suspend fun FlowFragmentBinding.appendDownload(view: View) { - val destPath = "${requireContext().externalCacheDir}/Miaobo.apk" - RxHttp.get(Url.DOWNLOAD_URL) - .toDownloadFlow(destPath, true) - .onProgress { - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已下载的字节大小 - val totalSize = it.totalSize //要下载的总字节大小 - tvResult.append("\n$it") - }.catch { - tvResult.append("\n${it.errorMsg}") - it.show() - }.collect { - tvResult.append("\n下载完成, $it") - } - } - - /** - * Android 10 及以上下载,兼容Android 10以下 - * - * 如不需要监听下载进度,toFlow 方法不要传进度回调即可 - */ - private suspend fun FlowFragmentBinding.downloadAndroid10(view: View) { - val factory = Android10DownloadFactory(requireContext(), "miaobo.apk") - RxHttp.get(Url.DOWNLOAD_URL) - .toDownloadFlow(factory) - .onProgress { - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已下载的字节大小 - val totalSize = it.totalSize //要下载的总字节大小 - tvResult.append("\n$it") - }.catch { - tvResult.append("\n${it.errorMsg}") - it.show() - }.collect { - tvResult.append("\n下载完成, $it") - } - } - - /** - * Android 10 及以上断点下载,兼容Android 10以下 - * - * 如不需要监听下载进度,toFlow 方法不要传进度回调即可 - */ - private suspend fun FlowFragmentBinding.appendDownloadAndroid10(view: View) { - val factory = Android10DownloadFactory(requireContext(), "miaobo.apk") - RxHttp.get(Url.DOWNLOAD_URL) - .toDownloadFlow(factory, true) - .onProgress { - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已下载的字节大小 - val totalSize = it.totalSize //要下载的总字节大小 - tvResult.append("\n$it") - }.catch { - //异常回调 - tvResult.append("\n${it.errorMsg}") - it.show() - }.collect { - tvResult.append("\n下载完成, $it") - } - } - - - private fun FlowFragmentBinding.clearLog(view: View) { - tvResult.text = "" - tvResult.setBackgroundColor(Color.TRANSPARENT) - } - - override fun onClick(v: View) { - mBinding.run { - lifecycleScope.launch { - when (v.id) { - R.id.sendGet -> sendGet(v) - R.id.sendPostForm -> sendPostForm(v) - R.id.sendPostJson -> sendPostJson(v) - R.id.sendPostJsonArray -> sendPostJsonArray(v) - R.id.xmlConverter -> xmlConverter(v) - R.id.upload -> upload(v) - R.id.upload10 -> uploadAndroid10(v) - R.id.download -> download(v) - R.id.download_append -> appendDownload(v) - R.id.download10 -> downloadAndroid10(v) - R.id.download10_append -> appendDownloadAndroid10(v) - R.id.bt_clear -> clearLog(v) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/fragment/MultiDownloadFragment.java b/app/src/main/java/com/example/httpsender/fragment/MultiDownloadFragment.java deleted file mode 100644 index 18ce36ed..00000000 --- a/app/src/main/java/com/example/httpsender/fragment/MultiDownloadFragment.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.example.httpsender.fragment; - -import android.os.Bundle; -import android.view.View; -import android.view.View.OnClickListener; - -import androidx.annotation.Nullable; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.SimpleItemAnimator; - -import com.example.httpsender.DownloadMultiAdapter; -import com.example.httpsender.DownloadMultiAdapter.OnItemClickListener; -import com.example.httpsender.R; -import com.example.httpsender.Tip; -import com.example.httpsender.databinding.MultiDownloadFragmentBinding; -import com.example.httpsender.entity.DownloadTask; -import com.example.httpsender.vm.MultiTaskDownloader; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -/** - * User: ljx - * Date: 2021/9/25 - * Time: 18:08 - */ -public class MultiDownloadFragment extends BaseFragment implements OnItemClickListener, OnClickListener { - - private final String[] downloadUrl = { -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/uS12nZLR.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/BYGanTMW.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/Iu9hZLL8.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/DdKLk5VX.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/Byww5X8k.ts", - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?111",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?222",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?333",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?444",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?555",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?666",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?777",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?888",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?999",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?101",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?102",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?103",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?104",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?105",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?106",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?107",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk?108",//探探 - "https://apk-ssl.tancdn.com/3.5.3_276/%E6%8E%A2%E6%8E%A2.apk",//探探 -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/OUkREagY.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/ZJUsgPSd.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/I5ivzoXR.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/PFPXapY7.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/tJj2JTVy.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/mj2fFYjH.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/MOXijkzw.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/uiwVyFej.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/HijAOXaK.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/h2mFS6ef.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/vf8fjmJd.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/0jsVXFSa.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/aZfnIVnP.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/l7cddTCQ.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/QaMi23d0.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/ljLywei6.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/FQpwzm4U.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/4oW2C2iZ.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/57OL3KeG.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/vYlV9nTw.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/XUmd5HWF.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/btVvEY5r.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/eJRKGoaP.ts", -// "https://hnzy4.jinhaiwzhs.com:65/20210625/mndoRF1O/2000kb/hls/iz5kx1X1.ts", - }; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.multi_download_fragment); - } - - @Override - public void onViewCreated(@NotNull MultiDownloadFragmentBinding binding, @Nullable Bundle savedInstanceState) { - super.onViewCreated(binding, savedInstanceState); - binding.setClick(this); - ArrayList allTask = new ArrayList<>(); //所有下载任务 - for (int i = 0; i < downloadUrl.length; i++) { - String url = downloadUrl[i]; - DownloadTask task = new DownloadTask(url); - String suffix = url.substring(url.lastIndexOf(".")); - task.setLocalPath(getContext().getExternalCacheDir() + "/" + i + suffix); - allTask.add(task); - } - RecyclerView recyclerView = binding.recyclerView; - - MultiTaskDownloader.addTasks(allTask); - DownloadMultiAdapter multiAdapter = new DownloadMultiAdapter(MultiTaskDownloader.getAllTask()); - multiAdapter.setOnItemClickListener(this); - recyclerView.setAdapter(multiAdapter); - ((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false); - - MultiTaskDownloader.getLiveTask().observe(getViewLifecycleOwner(), task -> { - int index = MultiTaskDownloader.getAllTask().indexOf(task); - if (index != -1) { - //任务有更新,刷新单个item - multiAdapter.notifyItemChanged(index); - } - }); - } - - @Override - public void onClick(View v) { - int id = v.getId(); - if (id == R.id.start_all) { - MultiTaskDownloader.startAllDownloadTask(); - } else if (id == R.id.cancel_all) { - MultiTaskDownloader.cancelAllTask(); - } - } - - @Override - public void onItemClick(View view, DownloadTask task, int position) { - if (view.getId() == R.id.bt_pause) { - int curState = task.getState(); //任务当前状态 - if (curState == MultiTaskDownloader.IDLE //未开始->开始下载 - || curState == MultiTaskDownloader.PAUSED //暂停下载->继续下载 - || curState == MultiTaskDownloader.CANCEL //已取消->重新开始下载 - || curState == MultiTaskDownloader.FAIL //下载失败->重新下载 - ) { - MultiTaskDownloader.download(task); - } else if (curState == MultiTaskDownloader.WAITING) { //等待中->取消下载 - MultiTaskDownloader.removeWaitTask(task); - } else if (curState == MultiTaskDownloader.DOWNLOADING) { //下载中->暂停下载 - MultiTaskDownloader.pauseTask(task); - } else if (curState == MultiTaskDownloader.COMPLETED) { //任务已完成 - Tip.show("该任务已完成"); - } - } - } -} diff --git a/app/src/main/java/com/example/httpsender/fragment/RxJavaFragment.kt b/app/src/main/java/com/example/httpsender/fragment/RxJavaFragment.kt deleted file mode 100644 index 7a7e79ee..00000000 --- a/app/src/main/java/com/example/httpsender/fragment/RxJavaFragment.kt +++ /dev/null @@ -1,355 +0,0 @@ -package com.example.httpsender.fragment - -import android.graphics.Color -import android.net.Uri -import android.os.Bundle -import android.view.View -import com.example.httpsender.R -import com.example.httpsender.databinding.RxjavaFragmentBinding -import com.example.httpsender.entity.Article -import com.example.httpsender.entity.Location -import com.example.httpsender.entity.Name -import com.example.httpsender.entity.NewsDataXml -import com.example.httpsender.entity.PageList -import com.example.httpsender.entity.Url -import com.example.httpsender.kt.errorMsg -import com.example.httpsender.kt.show -import com.example.httpsender.parser.Android10DownloadFactory -import com.google.gson.Gson -import com.rxjava.rxlife.life -import com.rxjava.rxlife.lifeOnMain -import rxhttp.wrapper.param.RxHttp -import rxhttp.wrapper.param.toObservable -import rxhttp.wrapper.param.toObservableResponse -import java.io.File - -/** - * 使用 RxHttp + RxJava 发请求 - * - * ``` - * RxHttp.postXxx("/service/...") - * .add("key", "value") - * .toObservable() - * .subscribe(user -> { - * - * }, throwable -> { - * - * }) - * ``` - * User: ljx - * Date: 2020/4/24 - * Time: 18:16 - */ -class RxJavaFragment : BaseFragment(), View.OnClickListener { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.rxjava_fragment) - } - - override fun RxjavaFragmentBinding.onViewCreated(savedInstanceState: Bundle?) { - click = this@RxJavaFragment - } - - //发送Get请求,获取文章列表 - fun RxjavaFragmentBinding.sendGet(view: View?) { - RxHttp.get("/article/list/0/json") - .addQuery("aa") - .addQuery("bb","") - .toObservableResponse>() - .lifeOnMain(this@RxJavaFragment) - .subscribe({ - tvResult.text = Gson().toJson(it) - }, { - tvResult.text = it.errorMsg - it.show() - }) - } - - //发送Post表单请求,根据关键字查询文章 - fun RxjavaFragmentBinding.sendPostForm(view: View?) { - RxHttp.postForm("/article/query/0/json") - .add("k", "性能优化") - .toObservableResponse>() - .lifeOnMain(this@RxJavaFragment) //感知生命周期,并在主线程回调 - .subscribe({ - tvResult.text = Gson().toJson(it) - }, { - tvResult.text = it.errorMsg - it.show() - }) - } - - //发送Post Json请求,此接口不通,通过日志可以看到,发送出去的json对象 - fun RxjavaFragmentBinding.sendPostJson(view: View?) { - //发送以下User对象 - /* - { - "name": "张三", - "sex": 1, - "height": 180, - "weight": 70, - "interest": [ - "羽毛球", - "游泳" - ], - "location": { - "latitude": 30.7866, - "longitude": 120.6788 - }, - "address": { - "street": "科技园路.", - "city": "江苏苏州", - "country": "中国" - } - } - */ - val interestList: MutableList = ArrayList() //爱好 - interestList.add("羽毛球") - interestList.add("游泳") - val address = "{\"street\":\"科技园路.\",\"city\":\"江苏苏州\",\"country\":\"中国\"}" - RxHttp.postJson("/article/list/0/json") - .add("name", "张三") - .add("sex", 1) - .addAll("{\"height\":180,\"weight\":70}") //通过addAll系列方法添加多个参数 - .add("interest", interestList) //添加数组对象 - .add("location", Location(120.6788, 30.7866)) //添加位置对象 - .addJsonElement("address", address) //通过字符串添加一个对象 - .toObservableString() - .lifeOnMain(this@RxJavaFragment)//感知生命周期,并在主线程回调 - .subscribe({ - tvResult.text = it - }, { - tvResult.text = it.errorMsg - it.show() - }) - } - - //发送Post JsonArray请求,通过日志可以看到,发送出去的json数组 - fun RxjavaFragmentBinding.sendPostJsonArray(view: View?) { - //发送以下Json数组 - /* - [ - { - "name": "张三" - }, - { - "name": "李四" - }, - { - "name": "王五" - }, - { - "name": "赵六" - }, - { - "name": "杨七" - } - ] - */ - val names: MutableList = ArrayList() - names.add(Name("赵六")) - names.add(Name("杨七")) - RxHttp.postJsonArray("/article/list/0/json") - .add("name", "张三") - .add(Name("李四")) - .addJsonElement("{\"name\":\"王五\"}") - .addAll(names) - .toObservableString() - .lifeOnMain(this@RxJavaFragment) - .subscribe({ - tvResult.text = it - }, { - tvResult.text = it.errorMsg - //失败回调 - it.show() - }) - } - - //此接口不同,但通过日志可以看到,发送出去的是xml数据,如果收到也是xml数据,则会自动解析为我们指定的对象 - fun RxjavaFragmentBinding.xmlConverter(view: View?) { - RxHttp.postBody("http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=sf-muni") - .setBody(Name("张三")) - .setXmlConverter() - .toObservable() - .lifeOnMain(this@RxJavaFragment) //感知生命周期,并在主线程回调 - .subscribe({ - tvResult.text = Gson().toJson(it) - }, { - tvResult.text = it.errorMsg - //失败回调 - it.show() - }) - } - - /** - * android 10之前 或 沙盒目录(Android/data/packageName/)下的文件上传,如不需要监听进度,注释掉 upload 方法即可 - */ - private fun RxjavaFragmentBinding.upload(v: View) { - RxHttp.postForm(Url.UPLOAD_URL) - .addFile("file", File("xxxx/1.png")) - .toObservableString() - .onMainProgress { - //上传进度回调,0-100,仅在进度有更新时才会回调 - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已上传的字节大小 - val totalSize = it.totalSize //要上传的总字节大小 - tvResult.append("\n$it") - } - .life(this@RxJavaFragment) //页面销毁,自动关闭请求 - .subscribe({ - tvResult.append("\n上传成功 : $it") - }, { - tvResult.append("\n${it.errorMsg}") - it.show() - }) - } - - /** - * android 10 及以上文件上传 ,兼容Android 10以下,如不需要监听进度,注释掉 upload 方法即可 - */ - private fun RxjavaFragmentBinding.uploadAndroid10(v: View) { - //真实环境,需要调用文件选择器,拿到Uri对象 - val uri = Uri.parse("content://media/external/downloads/13417") - RxHttp.postForm(Url.UPLOAD_URL) - .addPart(requireContext(), "file", uri) - .toObservableString() - .onMainProgress { - //上传进度回调,0-100,仅在进度有更新时才会回调 - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已上传的字节大小 - val totalSize = it.totalSize //要上传的总字节大小 - tvResult.append("\n$it") - } - .life(this@RxJavaFragment) //页面销毁,自动关闭请求 - .subscribe({ - tvResult.append("\n上传成功 : $it") - }, { - tvResult.append("\n${it.errorMsg}") //失败回调 - it.show() - }) - } - - - /** - * Android 10以下 或 下载文件到沙盒目录下,下载可以直接传入file的绝对路径 - * - * 如不需要监听下载进度,asDownload 方法不要传进度回调即可 - */ - private fun RxjavaFragmentBinding.download(view: View) { - val destPath = "${requireContext().externalCacheDir}/${System.currentTimeMillis()}.apk" - RxHttp.get(Url.DOWNLOAD_URL) - .toDownloadObservable(destPath) - .onMainProgress { - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已下载的字节大小 - val totalSize = it.totalSize //要下载的总字节大小 - tvResult.append("\n$it") - } - .life(this@RxJavaFragment) //感知生命周期,并在主线程回调 - .subscribe({ - tvResult.append("\n下载完成, $it") - }, { - //下载失败 - tvResult.append("\n${it.errorMsg}") - it.show() - }) - } - - /** - * 断点下载 - * Android 10以下 或 下载文件到沙盒目录下,下载可以直接传入file的绝对路径 - * 如不需要监听下载进度,asAppendDownload 方法不要传进度回调即可 - */ - private fun RxjavaFragmentBinding.appendDownload(view: View) { - val destPath = "${requireContext().externalCacheDir}/Miaobo.apk" - RxHttp.get(Url.DOWNLOAD_URL) - .toDownloadObservable(destPath, true) - .onMainProgress { - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已下载的字节大小 - val totalSize = it.totalSize //要下载的总字节大小 - tvResult.append("\n$it") - } - .life(this@RxJavaFragment) //感知生命周期,并在主线程回调 - .subscribe({ - tvResult.append("\n下载完成, $it") - }, { - //下载失败 - tvResult.append("\n${it.errorMsg}") - it.show() - }) - } - - - /** - * Android 10 及以上下载,兼容Android 10以下 - * 如不需要监听下载进度,asDownload 方法不要传进度回调即可 - */ - private fun RxjavaFragmentBinding.downloadAndroid10(view: View) { - val factory = Android10DownloadFactory(requireContext(), "miaobo.apk") - RxHttp.get(Url.DOWNLOAD_URL) - .toDownloadObservable(factory) - .onMainProgress { - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已下载的字节大小 - val totalSize = it.totalSize //要下载的总字节大小 - tvResult.append("\n$it") - } - .life(this@RxJavaFragment) //感知生命周期,并在主线程回调 - .subscribe({ - tvResult.append("\n下载完成, $it") - }, { - //下载失败 - tvResult.append("\n${it.errorMsg}") - it.show() - }) - } - - /** - * Android 10 及以上断点下载,兼容Android 10以下 - * 如不需要监听下载进度,asAppendDownload 方法不要传进度回调即可 - */ - private fun RxjavaFragmentBinding.appendDownloadAndroid10(view: View) { - val factory = Android10DownloadFactory(requireContext(), "miaobo.apk") - RxHttp.get(Url.DOWNLOAD_URL) - .toDownloadObservable(factory, true) - .onMainProgress { - val currentProgress = it.progress //当前进度 0-100 - val currentSize = it.currentSize //当前已下载的字节大小 - val totalSize = it.totalSize //要下载的总字节大小 - tvResult.append("\n$it") - } - .life(this@RxJavaFragment) //感知生命周期,并在主线程回调 - .subscribe({ - tvResult.append("\n下载完成, $it") - }, { - //下载失败 - tvResult.append("\n${it.errorMsg}") - it.show() - }) - } - - private fun RxjavaFragmentBinding.clearLog(view: View?) { - tvResult.text = "" - tvResult.setBackgroundColor(Color.TRANSPARENT) - } - - override fun onClick(v: View) { - mBinding.apply { - when (v.id) { - R.id.sendGet -> sendGet(v) - R.id.sendPostForm -> sendPostForm(v) - R.id.sendPostJson -> sendPostJson(v) - R.id.sendPostJsonArray -> sendPostJsonArray(v) - R.id.xmlConverter -> xmlConverter(v) - R.id.upload -> upload(v) - R.id.upload10 -> uploadAndroid10(v) - R.id.download -> download(v) - R.id.download_append -> appendDownload(v) - R.id.download10 -> downloadAndroid10(v) - R.id.download10_append -> appendDownloadAndroid10(v) - R.id.bt_clear -> clearLog(v) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/interceptor/TokenInterceptor.java b/app/src/main/java/com/example/httpsender/interceptor/TokenInterceptor.java index db22d291..f8de52f7 100644 --- a/app/src/main/java/com/example/httpsender/interceptor/TokenInterceptor.java +++ b/app/src/main/java/com/example/httpsender/interceptor/TokenInterceptor.java @@ -3,15 +3,16 @@ import com.example.httpsender.entity.User; -import org.jetbrains.annotations.NotNull; - import java.io.IOException; -import java.util.concurrent.atomic.AtomicReference; +import okhttp3.FormBody; import okhttp3.Interceptor; import okhttp3.Request; +import okhttp3.RequestBody; import okhttp3.Response; import rxhttp.wrapper.param.RxHttp; +import rxhttp.wrapper.param.RxHttp$FormParam; +import rxhttp.wrapper.parse.SimpleParser; /** * token 失效,自动刷新token,然后再次发送请求,用户无感知 @@ -21,49 +22,62 @@ */ public class TokenInterceptor implements Interceptor { - //保存刷新后的token - private final AtomicReference atomicToken = new AtomicReference<>(); - //token刷新时间 - @NotNull + private static volatile long SESSION_KEY_REFRESH_TIME = 0; + @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response originalResponse = chain.proceed(request); - String code = originalResponse.header("xxx"); //其中xxx,自己跟服务端协定 - if ("-1".equals(code)) { //token 失效 这里根据自己的业务需求写判断条件 - atomicToken.set(null); + String code = originalResponse.header("token_code"); + if ("-1".equals(code)) { //token 失效 1、这里根据自己的业务需求写判断条件 return handleTokenInvalid(chain, request); } return originalResponse; } - //处理token失效问题, 同步刷新 + + //处理token失效问题 private Response handleTokenInvalid(Chain chain, Request request) throws IOException { - boolean success = refreshToken(); + RxHttp$FormParam rxHttp = RxHttp.postForm(request.url().toString()); //2、根据自己的业务修改 + RequestBody body = request.body(); + if (body instanceof FormBody) { + FormBody formBody = (FormBody) body; + for (int i = 0; i < formBody.size(); i++) { + rxHttp.add(formBody.name(i), formBody.value(i)); + } + } + //同步刷新token + Object requestTime = rxHttp.queryValue("request_time"); //3、发请求前需要add("request_time",System.currentTimeMillis()) + boolean success = refreshToken(requestTime); Request newRequest; - if (success) { //刷新成功,重新添加token - newRequest = request.newBuilder() - .header("xxx", atomicToken.get()) //其中xxx,自己跟服务端协定 - .build(); + if (success) { //刷新成功,重新签名 + rxHttp.add("token", User.get().getToken()); //拿到最新的token,重新发起请求 4、根据自己的业务修改 + newRequest = rxHttp.buildRequest(); } else { newRequest = request; } return chain.proceed(newRequest); } - //刷新token, 考虑到有并发情况,故这里需要加锁 - private boolean refreshToken() { - //token不等于null,说明已经刷新 - if (atomicToken.get() != null) return true; + //刷新token + private boolean refreshToken(Object value) { + long requestTime = 0; + try { + requestTime = Integer.valueOf(value.toString()); + } catch (Exception ignore) { + } + //请求时间小于token刷新时间,说明token已经刷新,则无需再次刷新 + if (requestTime <= SESSION_KEY_REFRESH_TIME) return true; synchronized (this) { //再次判断是否已经刷新 - if (atomicToken.get() != null) return true; + if (requestTime <= SESSION_KEY_REFRESH_TIME) return true; try { - //根据自己业务,同步刷新token, 注意这里千万不能异步 + //获取到最新的token,这里需要同步请求token,千万不能异步 5、根据自己的业务修改 String token = RxHttp.postForm("/refreshToken/...") - .executeString(); - atomicToken.set(token); + .execute(SimpleParser.get(String.class)); + + SESSION_KEY_REFRESH_TIME = System.currentTimeMillis() / 1000; User.get().setToken(token); //保存最新的token return true; } catch (IOException e) { diff --git a/app/src/main/java/com/example/httpsender/kt/Activity.kt b/app/src/main/java/com/example/httpsender/kt/Activity.kt deleted file mode 100644 index 91ee01c8..00000000 --- a/app/src/main/java/com/example/httpsender/kt/Activity.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.example.httpsender.kt - -import android.app.Activity -import android.content.Intent -import androidx.fragment.app.Fragment -import kotlin.reflect.KClass - -/** - * User: ljx - * Date: 2020/5/15 - * Time: 16:33 - */ - -fun Activity.startActivity(clazz: KClass, block: (Intent.() -> Unit)? = null) { - val intent = Intent(this, clazz.java).apply { - block?.invoke(this) - } - startActivity(intent) -} - -fun Fragment.startActivity(clazz: KClass, block: (Intent.() -> Unit)? = null) { - val intent = Intent(activity, clazz.java).apply { - block?.invoke(this) - } - startActivity(intent) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/kt/KotlinExtensions.kt b/app/src/main/java/com/example/httpsender/kt/KotlinExtensions.kt deleted file mode 100644 index 1dcb704b..00000000 --- a/app/src/main/java/com/example/httpsender/kt/KotlinExtensions.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.example.httpsender.kt - -import android.content.Context -import android.net.ConnectivityManager -import com.example.httpsender.AppHolder -import com.example.httpsender.Tip -import com.google.gson.JsonSyntaxException -import kotlinx.coroutines.TimeoutCancellationException -import rxhttp.wrapper.exception.HttpStatusCodeException -import rxhttp.wrapper.exception.ParseException -import java.net.ConnectException -import java.net.SocketTimeoutException -import java.net.UnknownHostException -import java.util.concurrent.TimeoutException - -/** - * User: ljx - * Date: 2020-02-07 - * Time: 21:04 - */ -fun Throwable.show() { - errorMsg.show() -} - -fun String.show() { - Tip.show(this) -} - -val Throwable.errorCode: Int - get() = - when (this) { - is HttpStatusCodeException -> this.statusCode //Http状态码异常 - is ParseException -> this.errorCode.toIntOrNull() ?: -1 //业务code异常 - else -> -1 - } - -val Throwable.errorMsg: String - get() { - return if (this is UnknownHostException) { //网络异常 - if (!isNetworkConnected(AppHolder.getInstance())) - "当前无网络,请检查你的网络设置" - else - "网络连接不可用,请稍后重试!" - } else if ( - this is SocketTimeoutException //okhttp全局设置超时 - || this is TimeoutException //rxjava中的timeout方法超时 - || this is TimeoutCancellationException //协程超时 - ) { - "连接超时,请稍后再试" - } else if (this is ConnectException) { - "网络不给力,请稍候重试!" - } else if (this is HttpStatusCodeException) { //请求失败异常 - "Http状态码异常 $message" - } else if (this is JsonSyntaxException) { //请求成功,但Json语法异常,导致解析失败 - "数据解析失败,请检查数据是否正确" - } else if (this is ParseException) { // ParseException异常表明请求成功,但是数据不正确 - this.message ?: errorCode //msg为空,显示code - } else { - message ?: this.toString() - } - } - -private fun isNetworkConnected(context: Context): Boolean { - val mConnectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - val mNetworkInfo = mConnectivityManager.activeNetworkInfo - if (mNetworkInfo != null) { - return mNetworkInfo.isAvailable - } - return false -} diff --git a/app/src/main/java/com/example/httpsender/kt/Uri.kt b/app/src/main/java/com/example/httpsender/kt/Uri.kt deleted file mode 100644 index cfbb2721..00000000 --- a/app/src/main/java/com/example/httpsender/kt/Uri.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.example.httpsender.kt - -import android.content.Context -import android.net.Uri -import android.provider.MediaStore -import android.util.Log - -/** - * User: ljx - * Date: 2020/9/24 - * Time: 15:33 - */ - -fun Uri.dimQuery(context: Context, displayName: String) { - context.contentResolver.query(this, null, - "_display_name LIKE '%$displayName%'",null, null)?.use { - while (it.moveToNext()) { - val id = it.getString(it.getColumnIndex(MediaStore.MediaColumns._ID)) - val name = it.getString(it.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)) - val data = it.getString(it.getColumnIndex(MediaStore.MediaColumns.DATA)) - //注意: 通过这种方式获取的文件size,在文件被手动删除后,读取到的是不准确的 - val size = it.getString(it.getColumnIndex(MediaStore.MediaColumns.SIZE)) - val dateAdded = it.getString(it.getColumnIndex(MediaStore.MediaColumns.DATE_ADDED)) - val dateModified = it.getString(it.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED)) - Log.e("LJX", "id=$id size=$size name=$name data=$data dateAdded=$dateAdded dateModified=$dateModified") - } - } -} - -fun Uri.dimDelete(context: Context, displayName: String) { - val delete = context.contentResolver.delete(this, "_display_name LIKE '%$displayName%'", null) - Log.e("LJX", "delete=$delete") -} - -fun Uri.delete(context: Context, displayName: String) { - val delete = context.contentResolver.delete(this, "_display_name=?", arrayOf(displayName)) - Log.e("LJX", "delete=$delete") -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/param/GetEncryptParam.java b/app/src/main/java/com/example/httpsender/param/GetEncryptParam.java index dd493ad9..6f93b9cb 100644 --- a/app/src/main/java/com/example/httpsender/param/GetEncryptParam.java +++ b/app/src/main/java/com/example/httpsender/param/GetEncryptParam.java @@ -1,19 +1,10 @@ package com.example.httpsender.param; -import android.graphics.Point; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import okhttp3.HttpUrl; import rxhttp.wrapper.annotation.Param; -import rxhttp.wrapper.entity.KeyValuePair; import rxhttp.wrapper.param.Method; import rxhttp.wrapper.param.NoBodyParam; /** - * 加密get请求 * User: ljx * Date: 2019-09-12 * Time: 17:25 @@ -25,25 +16,7 @@ public GetEncryptParam(String url) { super(url, Method.GET); } - @SafeVarargs - public final GetEncryptParam test(List a, Map map, T[]... b) throws IOException, IllegalArgumentException { - return this; - } + public void test(){ - @Override - public HttpUrl getHttpUrl() { - StringBuilder paramsBuilder = new StringBuilder(); //存储加密后的参数 - List queryParam = getQueryParam(); - if (queryParam != null) { - for (KeyValuePair pair : getQueryParam()) { - //这里遍历所有添加的参数,可对参数进行加密操作 - String key = pair.getKey(); - Object value = pair.getValue(); - //加密逻辑自己写 - } - } - String simpleUrl = getSimpleUrl(); //拿到请求Url - if (paramsBuilder.length() == 0) return HttpUrl.get(simpleUrl); - return HttpUrl.get(simpleUrl + "?" + paramsBuilder); //将加密后的参数和url组拼成HttpUrl对象并返回 } } diff --git a/app/src/main/java/com/example/httpsender/param/PostEncryptFormParam.java b/app/src/main/java/com/example/httpsender/param/PostEncryptFormParam.java index eb6cf71b..e7addab1 100644 --- a/app/src/main/java/com/example/httpsender/param/PostEncryptFormParam.java +++ b/app/src/main/java/com/example/httpsender/param/PostEncryptFormParam.java @@ -18,8 +18,8 @@ public PostEncryptFormParam(String url) { super(url, Method.POST); } - public PostEncryptFormParam(String url, Method method) { - super(url, method); + public PostEncryptFormParam test() { + return this; } public PostEncryptFormParam test1(String s) { diff --git a/app/src/main/java/com/example/httpsender/param/PostEncryptJsonParam.kt b/app/src/main/java/com/example/httpsender/param/PostEncryptJsonParam.kt index 0e47913d..8e172589 100644 --- a/app/src/main/java/com/example/httpsender/param/PostEncryptJsonParam.kt +++ b/app/src/main/java/com/example/httpsender/param/PostEncryptJsonParam.kt @@ -1,10 +1,7 @@ package com.example.httpsender.param -import okhttp3.MediaType -import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody import rxhttp.wrapper.annotation.Param import rxhttp.wrapper.param.JsonParam import rxhttp.wrapper.param.Method @@ -18,18 +15,17 @@ import rxhttp.wrapper.utils.GsonUtil @Param(methodName = "postEncryptJson") class PostEncryptJsonParam(url: String) : JsonParam(url, Method.POST) { - private var MEDIA_TYPE_JSON: MediaType = "application/json; charset=utf-8".toMediaType() /** * @return 根据自己的业务需求返回对应的RequestBody */ override fun getRequestBody(): RequestBody { //我们要发送Post请求,参数以加密后的json形式发出 //第一步,将参数转换为Json字符串 - val json = if (bodyParam == null) "" else GsonUtil.toJson(bodyParam) + val json = if (params == null) "" else GsonUtil.toJson(params) //第二步,加密 val encryptByte = encrypt(json, "RxHttp") //第三部,创建RequestBody并返回 - return encryptByte!!.toRequestBody(MEDIA_TYPE_JSON) + return RequestBody.create(MEDIA_TYPE_JSON, encryptByte!!) } /** diff --git a/app/src/main/java/com/example/httpsender/param/PostEncryptJsonParam1.java b/app/src/main/java/com/example/httpsender/param/PostEncryptJsonParam1.java index cc9c0066..7777239f 100644 --- a/app/src/main/java/com/example/httpsender/param/PostEncryptJsonParam1.java +++ b/app/src/main/java/com/example/httpsender/param/PostEncryptJsonParam1.java @@ -1,8 +1,9 @@ package com.example.httpsender.param; +import okhttp3.Request; import okhttp3.RequestBody; import rxhttp.wrapper.annotation.Param; -import rxhttp.wrapper.param.AbstractBodyParam; +import rxhttp.wrapper.param.AbstractParam; import rxhttp.wrapper.param.Method; /** @@ -11,18 +12,23 @@ * Time: 11:52 */ @Param(methodName = "postEncryptJson1") -public class PostEncryptJsonParam1 extends AbstractBodyParam { +public class PostEncryptJsonParam1 extends AbstractParam { public PostEncryptJsonParam1(String url) { super(url, Method.POST); } + @Override + public Request buildRequest() { + return null; + } + @Override public RequestBody getRequestBody() { return null; } - public void test() { + public void test(){ } diff --git a/app/src/main/java/com/example/httpsender/parser/Android10DownloadFactory.kt b/app/src/main/java/com/example/httpsender/parser/Android10DownloadFactory.kt deleted file mode 100644 index 45143ad6..00000000 --- a/app/src/main/java/com/example/httpsender/parser/Android10DownloadFactory.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.example.httpsender.parser - -import android.content.ContentValues -import android.content.Context -import android.net.Uri -import android.os.Build -import android.os.Environment -import android.provider.MediaStore -import androidx.annotation.RequiresApi -import okhttp3.Response -import rxhttp.wrapper.callback.UriFactory -import rxhttp.wrapper.utils.query -import java.io.File - -/** - * User: ljx - * Date: 2020/9/11 - * Time: 17:43 - * - * @param context Context - * @param filename 文件名 - * @param relativePath 文件相对路径,可取值: - * [Environment.DIRECTORY_DOWNLOADS] - * [Environment.DIRECTORY_DCIM] - * [Environment.DIRECTORY_PICTURES] - * [Environment.DIRECTORY_MUSIC] - * [Environment.DIRECTORY_MOVIES] - * [Environment.DIRECTORY_DOCUMENTS] - * ... - */ -class Android10DownloadFactory @JvmOverloads constructor( - context: Context, - private val filename: String, - private val relativePath: String = Environment.DIRECTORY_DOWNLOADS -) : UriFactory(context) { - - /** - * [MediaStore.Files.getContentUri] - * [MediaStore.Downloads.EXTERNAL_CONTENT_URI] - * [MediaStore.Audio.Media.EXTERNAL_CONTENT_URI] - * [MediaStore.Video.Media.EXTERNAL_CONTENT_URI] - * [MediaStore.Images.Media.EXTERNAL_CONTENT_URI] - */ - @RequiresApi(Build.VERSION_CODES.Q) - fun getInsertUri() = MediaStore.Downloads.EXTERNAL_CONTENT_URI - - override fun query(): Uri? { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - getInsertUri().query(context, filename, relativePath) - } else { - val file = File("${Environment.getExternalStorageDirectory()}/$relativePath/$filename") - Uri.fromFile(file) - } - } - - override fun insert(response: Response): Uri { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - val uri = getInsertUri().query(context, filename, relativePath) - /* - * 通过查找,要插入的Uri已经存在,就无需再次插入 - * 否则会出现新插入的文件,文件名被系统更改的现象,因为insert不会执行覆盖操作 - */ - if (uri != null) return uri - ContentValues().run { - put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath) //下载到指定目录 - put(MediaStore.MediaColumns.DISPLAY_NAME, filename) //文件名 - //取contentType响应头作为文件类型 - put(MediaStore.MediaColumns.MIME_TYPE, response.body?.contentType().toString()) - context.contentResolver.insert(getInsertUri(), this) - //当相同路径下的文件,在文件管理器中被手动删除时,就会插入失败 - } ?: throw NullPointerException("Uri insert failed. Try changing filename") - } else { - val file = File("${Environment.getExternalStorageDirectory()}/$relativePath/$filename") - Uri.fromFile(file) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/parser/ResponseListParser.java b/app/src/main/java/com/example/httpsender/parser/ResponseListParser.java new file mode 100644 index 00000000..e92e78bc --- /dev/null +++ b/app/src/main/java/com/example/httpsender/parser/ResponseListParser.java @@ -0,0 +1,42 @@ +package com.example.httpsender.parser; + + +import com.example.httpsender.entity.Response; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.List; + +import rxhttp.wrapper.annotation.Parser; +import rxhttp.wrapper.entity.ParameterizedTypeImpl; +import rxhttp.wrapper.exception.ParseException; +import rxhttp.wrapper.parse.AbstractParser; + +/** + * Response> 数据解析器,解析完成对Response对象做判断,如果ok,返回数据 List + * User: ljx + * Date: 2018/10/23 + * Time: 13:49 + */ +@Parser(name = "ResponseList") +public class ResponseListParser extends AbstractParser> { + + protected ResponseListParser() { + super(); + } + + public ResponseListParser(Class type) { + super(type); + } + + @Override + public List onParse(okhttp3.Response response) throws IOException { + final Type type = ParameterizedTypeImpl.get(Response.class, List.class, mType); //获取泛型类型 + Response> data = convert(response, type); + List list = data.getData(); //获取data字段 + if (data.getCode() != 0 || list == null) { //code不等于0,说明数据不正确,抛出异常 + throw new ParseException(String.valueOf(data.getCode()), data.getMsg(), response); + } + return list; + } +} diff --git a/app/src/main/java/com/example/httpsender/parser/ResponsePageListParser.java b/app/src/main/java/com/example/httpsender/parser/ResponsePageListParser.java new file mode 100644 index 00000000..5b52016a --- /dev/null +++ b/app/src/main/java/com/example/httpsender/parser/ResponsePageListParser.java @@ -0,0 +1,42 @@ +package com.example.httpsender.parser; + + +import com.example.httpsender.entity.PageList; +import com.example.httpsender.entity.Response; + +import java.io.IOException; +import java.lang.reflect.Type; + +import rxhttp.wrapper.annotation.Parser; +import rxhttp.wrapper.entity.ParameterizedTypeImpl; +import rxhttp.wrapper.exception.ParseException; +import rxhttp.wrapper.parse.AbstractParser; + +/** + * Response> 数据解析器,解析完成对Response对象做判断,如果ok,返回数据 PageList + * User: ljx + * Date: 2018/10/23 + * Time: 13:49 + */ +@Parser(name = "ResponsePageList") +public class ResponsePageListParser extends AbstractParser> { + + protected ResponsePageListParser() { + super(); + } + + public ResponsePageListParser(Class type) { + super(type); + } + + @Override + public PageList onParse(okhttp3.Response response) throws IOException { + final Type type = ParameterizedTypeImpl.get(Response.class, PageList.class, mType); //获取泛型类型 + Response> data = convert(response, type); + PageList pageList = data.getData(); //获取data字段 + if (data.getCode() != 0 || pageList == null) { //code不等于0,说明数据不正确,抛出异常 + throw new ParseException(String.valueOf(data.getCode()), data.getMsg(), response); + } + return pageList; + } +} diff --git a/app/src/main/java/com/example/httpsender/parser/ResponseParser.java b/app/src/main/java/com/example/httpsender/parser/ResponseParser.java new file mode 100644 index 00000000..e3ed913f --- /dev/null +++ b/app/src/main/java/com/example/httpsender/parser/ResponseParser.java @@ -0,0 +1,50 @@ +package com.example.httpsender.parser; + + +import com.example.httpsender.entity.Response; + +import java.io.IOException; +import java.lang.reflect.Type; + +import rxhttp.wrapper.annotation.Parser; +import rxhttp.wrapper.entity.ParameterizedTypeImpl; +import rxhttp.wrapper.exception.ParseException; +import rxhttp.wrapper.parse.AbstractParser; + +/** + * Response 数据解析器,解析完成对Response对象做判断,如果ok,返回数据 T + * User: ljx + * Date: 2018/10/23 + * Time: 13:49 + */ +@Parser(name = "Response") +public class ResponseParser extends AbstractParser { + + protected ResponseParser() { + super(); + } + + public ResponseParser(Class type) { + super(type); + } + + @SuppressWarnings("unchecked") + @Override + public T onParse(okhttp3.Response response) throws IOException { + final Type type = ParameterizedTypeImpl.get(Response.class, mType); //获取泛型类型 + Response data = convert(response, type); + T t = data.getData(); //获取data字段 + if (t == null && mType == String.class) { + /* + * 考虑到有些时候服务端会返回:{"errorCode":0,"errorMsg":"关注成功"} 类似没有data的数据 + * 此时code正确,但是data字段为空,直接返回data的话,会报空指针错误, + * 所以,判断泛型为String类型时,重新赋值,并确保赋值不为null + */ + t = (T) data.getMsg(); + } + if (data.getCode() != 0 || t == null) {//code不等于0,说明数据不正确,抛出异常 + throw new ParseException(String.valueOf(data.getCode()), data.getMsg(), response); + } + return t; + } +} diff --git a/app/src/main/java/com/example/httpsender/parser/ResponseParser.kt b/app/src/main/java/com/example/httpsender/parser/ResponseParser.kt deleted file mode 100644 index fc8664b4..00000000 --- a/app/src/main/java/com/example/httpsender/parser/ResponseParser.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.example.httpsender.parser - -import com.example.httpsender.entity.PageList -import com.example.httpsender.entity.Response -import rxhttp.wrapper.annotation.Parser -import rxhttp.wrapper.exception.ParseException -import rxhttp.wrapper.parse.TypeParser -import rxhttp.wrapper.utils.convertTo -import java.io.IOException -import java.lang.reflect.Type - -/** - * 输入T,输出T,并对code统一判断 - * User: ljx - * Date: 2018/10/23 - * Time: 13:49 - * - * 如果使用协程发送请求,wrappers属性可不设置,设置了也无效 - */ -@Parser(name = "Response", wrappers = [PageList::class]) -open class ResponseParser : TypeParser { - /** - * 此构造方法可适用任意Class对象,但更多用于带泛型的Class对象,如:List>> - * - * 如Java环境中调用 - * toObservable(new ResponseParser>>(){}) - * 等价于kotlin环境下的 - * toObservableResponse>>() - * - * 注:此构造方法一定要用protected关键字修饰,否则调用此构造方法将拿不到泛型类型 - */ - protected constructor() : super() - - /** - * 该解析器会生成以下系列方法,前3个kotlin环境调用,后4个Java环境调用,所有方法内部均会调用本构造方法 - * toFlowResponse() - * toAwaitResponse() - * toObservableResponse() - * toObservableResponse(Type) - * toObservableResponse(Class) - * toObservableResponseList(Class) - * toObservableResponsePageList(Class) - * - * Flow/Await下 toXxxResponse> 等同于 toObservableResponsePageList(Class) - */ - constructor(type: Type) : super(type) - - @Throws(IOException::class) - override fun onParse(response: okhttp3.Response): T { - val data: Response = response.convertTo(Response::class, *types) - var t = data.data //获取data字段 - if (t == null && types[0] === String::class.java) { - /* - * 考虑到有些时候服务端会返回:{"errorCode":0,"errorMsg":"关注成功"} 类似没有data的数据 - * 此时code正确,但是data字段为空,直接返回data的话,会报空指针错误, - * 所以,判断泛型为String类型时,重新赋值,并确保赋值不为null - */ - @Suppress("UNCHECKED_CAST") - t = data.msg as T - } - if (data.code != 0 || t == null) { //code不等于0,说明数据不正确,抛出异常 - throw ParseException(data.code.toString(), data.msg, response) - } - return t - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/parser/java/DoubleTypeParser.java b/app/src/main/java/com/example/httpsender/parser/java/DoubleTypeParser.java deleted file mode 100644 index 90b69c72..00000000 --- a/app/src/main/java/com/example/httpsender/parser/java/DoubleTypeParser.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.example.httpsender.parser.java; - -import android.util.Pair; - -import com.example.httpsender.entity.Response; - -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.lang.reflect.Type; - -import rxhttp.wrapper.entity.ParameterizedTypeImpl; -import rxhttp.wrapper.exception.ParseException; -import rxhttp.wrapper.parse.TypeParser; -import rxhttp.wrapper.utils.Converter; - -/** - * 大于等于2个泛型的解析器,可以参考此类 - * User: ljx - * Date: 2018/10/23 - * Time: 13:49 - */ -//@Parser(name = "DoubleType") -public class DoubleTypeParser extends TypeParser> { - - protected DoubleTypeParser() { - super(); - } - - public DoubleTypeParser(Type fType, Type sType) { - super(fType, sType); - } - - @Override - public Pair onParse(@NotNull okhttp3.Response response) throws IOException { - Type pairType = ParameterizedTypeImpl.getParameterized(Pair.class, types); - Response> data = Converter.convertTo(response, Response.class, pairType); - Pair t = data.getData(); //获取data字段 - if (data.getCode() != 0 || t == null) {//code不等于0,说明数据不正确,抛出异常 - throw new ParseException(String.valueOf(data.getCode()), data.getMsg(), response); - } - return t; - } -} diff --git a/app/src/main/java/com/example/httpsender/parser/java/ResponseParser.java b/app/src/main/java/com/example/httpsender/parser/java/ResponseParser.java deleted file mode 100644 index 9b467f76..00000000 --- a/app/src/main/java/com/example/httpsender/parser/java/ResponseParser.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.example.httpsender.parser.java; - -import com.example.httpsender.entity.Response; - -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.lang.reflect.Type; - -import rxhttp.wrapper.exception.ParseException; -import rxhttp.wrapper.parse.TypeParser; -import rxhttp.wrapper.utils.Converter; - -/** - * 输入T,输出T,并对code统一判断 - * User: ljx - * Date: 2018/10/23 - * Time: 13:49 - */ -//@Parser(name = "Response", wrappers = {PageList.class}) -public class ResponseParser extends TypeParser { - - /** - * 此构造方法可适用任意Class对象,但更多用于带泛型的Class对象,如:List>> - *

- * 如Java环境中调用 - * toObservable(new ResponseParser>>(){}) - * 等价与kotlin环境下的 - * toObservableResponse>>() - *

- * 注:此构造方法一定要用protected关键字修饰,否则调用此构造方法将拿不到泛型类型 - */ - protected ResponseParser() { - super(); - } - - /** - * 该解析器会生成以下系列方法,前3个kotlin环境调用,后4个Java环境调用,所有方法内部均会调用本构造方法 - * toFlowResponse() - * toAwaitResponse() - * toObservableResponse() - * toObservableResponse(Type) - * toObservableResponse(Class) - * toObservableResponseList(Class) - * toObservableResponsePageList(Class) - *

- * Flow/Await下 toXxxResponse> 等价与 toObservableResponsePageList(Class) - */ - public ResponseParser(Type type) { - super(type); - } - - @SuppressWarnings("unchecked") - @Override - public T onParse(@NotNull okhttp3.Response response) throws IOException { - Response data = Converter.convertTo(response, Response.class, types); - T t = data.getData(); //获取data字段 - if (t == null && types[0] == String.class) { - /* - * 考虑到有些时候服务端会返回:{"errorCode":0,"errorMsg":"关注成功"} 类似没有data的数据 - * 此时code正确,但是data字段为空,直接返回data的话,会报空指针错误, - * 所以,判断泛型为String类型时,重新赋值,并确保赋值不为null - */ - t = (T) data.getMsg(); - } - if (data.getCode() != 0 || t == null) {//code不等于0,说明数据不正确,抛出异常 - throw new ParseException(String.valueOf(data.getCode()), data.getMsg(), response); - } - return t; - } -} diff --git a/app/src/main/java/com/example/httpsender/parser/kotlin/ResponseListParser.kt b/app/src/main/java/com/example/httpsender/parser/kotlin/ResponseListParser.kt new file mode 100644 index 00000000..7ad54321 --- /dev/null +++ b/app/src/main/java/com/example/httpsender/parser/kotlin/ResponseListParser.kt @@ -0,0 +1,33 @@ +package com.example.httpsender.parser.kotlin + + +import com.example.httpsender.entity.Response +import rxhttp.wrapper.entity.ParameterizedTypeImpl +import rxhttp.wrapper.exception.ParseException +import rxhttp.wrapper.parse.AbstractParser +import java.io.IOException + +/** + * Response> 数据解析器,解析完成对Response对象做判断,如果ok,返回数据 List + * User: ljx + * Date: 2018/10/23 + * Time: 13:49 + */ +//@Parser(name = "ResponseList") +class ResponseListParser : AbstractParser> { + + protected constructor() : super() + + constructor(type: Class) : super(type) + + @Throws(IOException::class) + override fun onParse(response: okhttp3.Response): List { + val type = ParameterizedTypeImpl.get(Response::class.java, List::class.java, mType) //获取泛型类型 + val data = convert>>(response, type) + val list = data.data //获取data字段 + if (data.code != 0 || list == null) { //code不等于0,说明数据不正确,抛出异常 + throw ParseException(data.code.toString(), data.msg, response) + } + return list + } +} diff --git a/app/src/main/java/com/example/httpsender/parser/kotlin/ResponsePageListParser.kt b/app/src/main/java/com/example/httpsender/parser/kotlin/ResponsePageListParser.kt new file mode 100644 index 00000000..43d15914 --- /dev/null +++ b/app/src/main/java/com/example/httpsender/parser/kotlin/ResponsePageListParser.kt @@ -0,0 +1,34 @@ +package com.example.httpsender.parser.kotlin + + +import com.example.httpsender.entity.PageList +import com.example.httpsender.entity.Response +import rxhttp.wrapper.entity.ParameterizedTypeImpl +import rxhttp.wrapper.exception.ParseException +import rxhttp.wrapper.parse.AbstractParser +import java.io.IOException + +/** + * Response> 数据解析器,解析完成对Response对象做判断,如果ok,返回数据 PageList + * User: ljx + * Date: 2018/10/23 + * Time: 13:49 + */ +//@Parser(name = "ResponsePageList") +class ResponsePageListParser : AbstractParser> { + + protected constructor() : super() + + constructor(type: Class) : super(type) + + @Throws(IOException::class) + override fun onParse(response: okhttp3.Response): PageList { + val type = ParameterizedTypeImpl.get(Response::class.java, PageList::class.java, mType) //获取泛型类型 + val data = convert>>(response, type) + val pageList = data.data //获取data字段 + if (data.code != 0 || pageList == null) { //code不等于0,说明数据不正确,抛出异常 + throw ParseException(data.code.toString(), data.msg, response) + } + return pageList + } +} diff --git a/app/src/main/java/com/example/httpsender/parser/kotlin/ResponseParser.kt b/app/src/main/java/com/example/httpsender/parser/kotlin/ResponseParser.kt new file mode 100644 index 00000000..de6330b8 --- /dev/null +++ b/app/src/main/java/com/example/httpsender/parser/kotlin/ResponseParser.kt @@ -0,0 +1,41 @@ +package com.example.httpsender.parser.kotlin + + +import com.example.httpsender.entity.Response +import rxhttp.wrapper.entity.ParameterizedTypeImpl +import rxhttp.wrapper.exception.ParseException +import rxhttp.wrapper.parse.AbstractParser +import java.io.IOException + +/** + * Response 数据解析器,解析完成对Response对象做判断,如果ok,返回数据 T + * User: ljx + * Date: 2018/10/23 + * Time: 13:49 + */ +//@Parser(name = "Response") +class ResponseParser : AbstractParser { + + protected constructor() : super() + + constructor(type: Class) : super(type) + + @Throws(IOException::class) + override fun onParse(response: okhttp3.Response): T { + val type = ParameterizedTypeImpl.get(Response::class.java, mType) //获取泛型类型 + val data = convert>(response, type) + var t: T? = data.data //获取data字段 + if (t == null && mType === String::class.java) { + /* + * 考虑到有些时候服务端会返回:{"errorCode":0,"errorMsg":"关注成功"} 类似没有data的数据 + * 此时code正确,但是data字段为空,直接返回data的话,会报空指针错误, + * 所以,判断泛型为String类型时,重新赋值,并确保赋值不为null + */ + t = data.msg as T + } + if (data.code != 0 || t == null) {//code不等于0,说明数据不正确,抛出异常 + throw ParseException(data.code.toString(), data.msg, response) + } + return t + } +} diff --git a/app/src/main/java/com/example/httpsender/utils/Preferences.java b/app/src/main/java/com/example/httpsender/utils/Preferences.java deleted file mode 100644 index ec607c38..00000000 --- a/app/src/main/java/com/example/httpsender/utils/Preferences.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.example.httpsender.utils; - -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -import com.example.httpsender.AppHolder; - - -/** - * User: hqs - * Date: 2016/5/10 - * Time: 19:07 - */ -public class Preferences { - - private static SharedPreferences sharedPreferences; - private static SharedPreferences.Editor editor; - - private Preferences() { - } - - private static SharedPreferences getInstance() { - if (sharedPreferences == null) { - synchronized (Preferences.class) { - if (sharedPreferences == null) { - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(AppHolder.getInstance()); - } - } - } - return sharedPreferences; - } - - public static SharedPreferences.Editor getEditor() { - if (editor == null) { - editor = getInstance().edit(); - } - return editor; - } - - public static String getValue(String key, String defaultValue) { - return getInstance().getString(key, defaultValue); - } - - public static void setValue(String key, String value) { - getEditor().putString(key, value).commit(); - } - - public static int getValue(String key, int defaultValue) { - return getInstance().getInt(key, defaultValue); - } - - public static void setValue(String key, int value) { - getEditor().putInt(key, value).commit(); - } - - public static void setFloat(String key, float value) { - getEditor().putFloat(key, value).commit(); - } - - public static float getFloat(String key, float defaultValue) { - return getInstance().getFloat(key, defaultValue); - } - - public static boolean getValue(String key, boolean defaultValue) { - return getInstance().getBoolean(key, defaultValue); - } - - public static void setValue(String key, boolean value) { - getEditor().putBoolean(key, value).commit(); - } - - public static long getValue(String key, long defaultValue) { - return getInstance().getLong(key, defaultValue); - } - - public static void setValue(String key, long value) { - getEditor().putLong(key, value).commit(); - } -} diff --git a/app/src/main/java/com/example/httpsender/view/ScaleTransitionPagerTitleView.java b/app/src/main/java/com/example/httpsender/view/ScaleTransitionPagerTitleView.java deleted file mode 100644 index f449bd98..00000000 --- a/app/src/main/java/com/example/httpsender/view/ScaleTransitionPagerTitleView.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.httpsender.view; - -import android.content.Context; - -import net.lucode.hackware.magicindicator.buildins.commonnavigator.titles.ColorTransitionPagerTitleView; - -/** - * 带颜色渐变和缩放的指示器标题 - * 博客: http://hackware.lucode.net - * Created by hackware on 2016/6/26. - */ -public class ScaleTransitionPagerTitleView extends ColorTransitionPagerTitleView { - private float mMinScale = 0.75f; - - public ScaleTransitionPagerTitleView(Context context) { - super(context); - } - - @Override - public void onEnter(int index, int totalCount, float enterPercent, boolean leftToRight) { - super.onEnter(index, totalCount, enterPercent, leftToRight); // 实现颜色渐变 - setScaleX(mMinScale + (1.0f - mMinScale) * enterPercent); - setScaleY(mMinScale + (1.0f - mMinScale) * enterPercent); - } - - @Override - public void onLeave(int index, int totalCount, float leavePercent, boolean leftToRight) { - super.onLeave(index, totalCount, leavePercent, leftToRight); // 实现颜色渐变 - setScaleX(1.0f + (mMinScale - 1.0f) * leavePercent); - setScaleY(1.0f + (mMinScale - 1.0f) * leavePercent); - } - - public float getMinScale() { - return mMinScale; - } - - public void setMinScale(float minScale) { - mMinScale = minScale; - } -} diff --git a/app/src/main/java/com/example/httpsender/vm/MultiTaskAwaitDownloader.kt b/app/src/main/java/com/example/httpsender/vm/MultiTaskAwaitDownloader.kt deleted file mode 100644 index 1171941b..00000000 --- a/app/src/main/java/com/example/httpsender/vm/MultiTaskAwaitDownloader.kt +++ /dev/null @@ -1,188 +0,0 @@ -package com.example.httpsender.vm - -import androidx.lifecycle.MutableLiveData -import com.example.httpsender.Tip -import com.example.httpsender.entity.DownloadTask -import com.example.httpsender.utils.Preferences -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import okhttp3.internal.http2.StreamResetException -import okio.ByteString.Companion.encodeUtf8 -import rxhttp.RxHttpPlugins -import rxhttp.awaitResult -import rxhttp.wrapper.param.RxHttp -import java.io.File -import java.util.* - -/** - * User: ljx - * Date: 2020/7/12 - * Time: 18:00 - */ -object MultiTaskAwaitDownloader { - - const val IDLE = 0 //未开始,闲置状态 - const val WAITING = 1 //等待中状态 - const val DOWNLOADING = 2 //下载中 - const val PAUSED = 3 //已暂停 - const val COMPLETED = 4 //已完成 - const val FAIL = 5 //下载失败 - const val CANCEL = 6 //取消状态,等待时被取消 - - private const val MAX_TASK_COUNT = 3 //最大并发数 - - @JvmStatic - val liveTask = MutableLiveData() //用于刷新UI - - @JvmStatic - val allTask = ArrayList() //所有下载任务 - private val waitTask = LinkedList() //等待下载的任务 - private val downloadingTask = LinkedList() //下载中的任务 - - //记录每个文件的总大小,key为文件url - private val lengthMap = HashMap() - - @JvmStatic - fun addTasks(tasks: ArrayList) { - val allTaskList = allTask - tasks.forEach { - if (!allTaskList.contains(it)) { - val md5Key = it.url.encodeUtf8().md5().hex() - val length = Preferences.getValue(md5Key, -1L) - if (length != -1L) { - it.totalSize = length - it.currentSize = File(it.localPath).length() - it.progress = it.currentSize * 1.0f / it.totalSize - lengthMap[it.url] = length - - if (it.currentSize > 0) { - it.state = PAUSED - } - if (it.totalSize == it.currentSize) { - //如果当前size等于总size,则任务文件下载完成,注意: 这个判断不是100%准确,最好能对文件做md5校验 - it.state = COMPLETED - } - } - allTaskList.add(it) - } - } - } - - //开始下载所有任务 - @JvmStatic - fun startAllDownloadTask() { - val allTaskList = allTask - allTaskList.forEach { - if (it.state != COMPLETED && it.state != DOWNLOADING) { - download(it) - } - } - } - - @JvmStatic - fun download(task: DownloadTask) { - if (downloadingTask.size >= MAX_TASK_COUNT) { - //超过最大下载数,添加进等待队列 - task.state = WAITING - updateTask(task) - waitTask.offer(task) - return - } - task.state = DOWNLOADING - updateTask(task) - downloadingTask.add(task) - - //如果想使用RxJava或Await下载,更改以下代码即可 - CoroutineScope(Dispatchers.Main).launch { -// RxHttp.get(task.url) -// .tag(task.url) //记录tag,手动取消下载时用到 -// .toDownloadAwait(task.localPath, true) { -// //下载进度回调,0-100,仅在进度有更新时才会回调 -// task.progress = it.progress //当前进度 0-100 -// task.currentSize = it.currentSize //当前已下载的字节大小 -// task.totalSize = it.totalSize //要下载的总字节大小 -// updateTask(task) -// val key = task.url -// val length = lengthMap[key] -// if (length != task.totalSize) { -// //服务器返回的文件总大小与本地的不一致,则更新 -// lengthMap[key] = task.totalSize -// saveTotalSize(lengthMap) -// } -// }.awaitResult { -// Tip.show("下载成功") -// task.state = COMPLETED -// }.onFailure { -// //手动取消下载时,会收到StreamResetException异常,不做任何处理 -// if (it !is StreamResetException) { -// Tip.show("下载失败") -// task.state = FAIL -// } -// } - //下载结束,不管任务成功还是失败,如果还有在等待的任务,都开启下一个任务 - updateTask(task) - downloadingTask.remove(task) - waitTask.poll()?.let { download(it) } - } - } - - - private fun saveTotalSize(map: HashMap) { - val editor = Preferences.getEditor() - for ((key, value) in map) { - val md5Key = key.encodeUtf8().md5().hex() - editor.putLong(md5Key, value) - } - editor.commit() - } - - - //关闭所有任务 - @JvmStatic - fun cancelAllTask() { - var iterator = waitTask.iterator() - while (iterator.hasNext()) { - val task = iterator.next() - task.state = CANCEL - iterator.remove() - updateTask(task) - } - - iterator = downloadingTask.iterator() - while (iterator.hasNext()) { - val task = iterator.next() - iterator.remove() - RxHttpPlugins.cancelAll(task.url) - task.state = CANCEL - updateTask(task) - } - } - - //等待中->取消下载 - @JvmStatic - fun removeWaitTask(task: DownloadTask) { - waitTask.remove(task) - task.state = CANCEL - updateTask(task) - } - - //暂停下载 - @JvmStatic - fun pauseTask(task: DownloadTask) { - //根据tag取消下载 - RxHttpPlugins.cancelAll(task.url) - task.state = PAUSED - updateTask(task) - } - - @JvmStatic - fun haveTaskExecuting(): Boolean { - return waitTask.size > 0 || downloadingTask.size > 0 - } - - //发送通知,更新UI - private fun updateTask(task: DownloadTask) { - liveTask.value = task - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/vm/MultiTaskDownloader.kt b/app/src/main/java/com/example/httpsender/vm/MultiTaskDownloader.kt deleted file mode 100644 index be755de1..00000000 --- a/app/src/main/java/com/example/httpsender/vm/MultiTaskDownloader.kt +++ /dev/null @@ -1,192 +0,0 @@ -package com.example.httpsender.vm - -import android.annotation.SuppressLint -import androidx.lifecycle.MutableLiveData -import com.example.httpsender.Tip -import com.example.httpsender.entity.DownloadTask -import com.example.httpsender.utils.Preferences -import okhttp3.internal.http2.StreamResetException -import okio.ByteString.Companion.encodeUtf8 -import rxhttp.RxHttpPlugins -import rxhttp.wrapper.param.RxHttp -import java.io.File -import java.util.* - -/** - * User: ljx - * Date: 2020/7/12 - * Time: 18:00 - */ -object MultiTaskDownloader { - - const val IDLE = 0 //未开始,闲置状态 - const val WAITING = 1 //等待中状态 - const val DOWNLOADING = 2 //下载中 - const val PAUSED = 3 //已暂停 - const val COMPLETED = 4 //已完成 - const val FAIL = 5 //下载失败 - const val CANCEL = 6 //取消状态,等待时被取消 - - private const val MAX_TASK_COUNT = 3 //最大并发数 - - @JvmStatic - val liveTask = MutableLiveData() //用于刷新UI - @JvmStatic - val allTask = ArrayList() //所有下载任务 - private val waitTask = LinkedList() //等待下载的任务 - private val downloadingTask = LinkedList() //下载中的任务 - - //记录每个文件的总大小,key为文件url - private val lengthMap = HashMap() - - @JvmStatic - fun addTasks(tasks: ArrayList) { - val allTaskList = allTask - tasks.forEach { - if (!allTaskList.contains(it)) { - val md5Key = it.url.encodeUtf8().md5().hex() - val length = Preferences.getValue(md5Key, -1L) - if (length != -1L) { - it.totalSize = length - it.currentSize = File(it.localPath).length() - it.progress = it.currentSize * 1.0f / it.totalSize - lengthMap[it.url] = length - - if (it.currentSize > 0) { - it.state = PAUSED - } - if (it.totalSize == it.currentSize) { - //如果当前size等于总size,则任务文件下载完成,注意: 这个判断不是100%准确,最好能对文件做md5校验 - it.state = COMPLETED - } - } - allTaskList.add(it) - } - } - } - - //开始下载所有任务 - @JvmStatic - fun startAllDownloadTask() { - val allTaskList = allTask - allTaskList.forEach { - if (it.state != COMPLETED && it.state != DOWNLOADING) { - download(it) - } - } - } - - @SuppressLint("CheckResult") - @JvmStatic - fun download(task: DownloadTask) { - if (downloadingTask.size >= MAX_TASK_COUNT) { - //超过最大下载数,添加进等待队列 - task.state = WAITING - updateTask(task) - waitTask.offer(task) - return - } - task.state = DOWNLOADING - updateTask(task) - downloadingTask.add(task) - - //如果想使用Await或Flow下载,更改以下代码即可 - RxHttp.get(task.url) - .tag(task.url) //记录tag,手动取消下载时用到 - .toDownloadObservable(task.localPath, true) - .onMainProgress { - //下载进度回调,0-100,仅在进度有更新时才会回调 - task.speed = it.speed - task.remainingTime = it.calculateRemainingTime() - task.progress = it.fraction //当前进度 [0.0, 1.0] - task.currentSize = it.currentSize //当前已下载的字节大小 - task.totalSize = it.totalSize //要下载的总字节大小 - updateTask(task) - val key = task.url - val length = lengthMap[key] - if (length != task.totalSize) { - //服务器返回的文件总大小与本地的不一致,则更新 - lengthMap[key] = task.totalSize - saveTotalSize(lengthMap) - } - } - .doFinally { - updateTask(task) - //不管任务成功还是失败,如果还有在等待的任务,都开启下一个任务 - downloadingTask.remove(task) - waitTask.poll()?.let { download(it) } - } - .subscribe({ - Tip.show("下载完成") - task.state = COMPLETED - }, { - //手动取消下载时,会收到StreamResetException异常,不做任何处理 - if (it !is StreamResetException){ - Tip.show("下载失败") - task.state = FAIL - } - }) - - } - - - private fun saveTotalSize(map: HashMap) { - val editor = Preferences.getEditor() - for ((key, value) in map) { - val md5Key = key.encodeUtf8().md5().hex() - editor.putLong(md5Key, value) - } - editor.commit() - } - - - //关闭所有任务 - @JvmStatic - fun cancelAllTask() { - var iterator = waitTask.iterator() - while (iterator.hasNext()) { - val task = iterator.next() - task.state = CANCEL - iterator.remove() - updateTask(task) - } - - iterator = downloadingTask.iterator() - while (iterator.hasNext()) { - val task = iterator.next() - iterator.remove() - RxHttpPlugins.cancelAll(task.url) - task.state = CANCEL - updateTask(task) - } - } - - //等待中->取消下载 - @JvmStatic - fun removeWaitTask(task: DownloadTask) { - waitTask.remove(task) - task.state = CANCEL - updateTask(task) - } - - //暂停下载 - @JvmStatic - fun pauseTask(task: DownloadTask) { - //根据tag取消下载 - RxHttpPlugins.cancelAll(task.url) - task.state = PAUSED - task.speed = 0 - task.remainingTime = -1 - updateTask(task) - } - - @JvmStatic - fun haveTaskExecuting(): Boolean { - return waitTask.size > 0 || downloadingTask.size > 0 - } - - //发送通知,更新UI - private fun updateTask(task: DownloadTask) { - liveTask.value = task - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/httpsender/vm/MultiTaskFlowDownloader.kt b/app/src/main/java/com/example/httpsender/vm/MultiTaskFlowDownloader.kt deleted file mode 100644 index 9a25fbf8..00000000 --- a/app/src/main/java/com/example/httpsender/vm/MultiTaskFlowDownloader.kt +++ /dev/null @@ -1,192 +0,0 @@ -package com.example.httpsender.vm - -import androidx.lifecycle.MutableLiveData -import com.example.httpsender.Tip -import com.example.httpsender.entity.DownloadTask -import com.example.httpsender.utils.Preferences -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.launch -import okhttp3.internal.http2.StreamResetException -import okio.ByteString.Companion.encodeUtf8 -import rxhttp.RxHttpPlugins -import rxhttp.toDownloadFlow -import rxhttp.wrapper.param.RxHttp -import java.io.File -import java.util.* - -/** - * User: ljx - * Date: 2020/7/12 - * Time: 18:00 - */ -object MultiTaskFlowDownloader { - - const val IDLE = 0 //未开始,闲置状态 - const val WAITING = 1 //等待中状态 - const val DOWNLOADING = 2 //下载中 - const val PAUSED = 3 //已暂停 - const val COMPLETED = 4 //已完成 - const val FAIL = 5 //下载失败 - const val CANCEL = 6 //取消状态,等待时被取消 - - private const val MAX_TASK_COUNT = 3 //最大并发数 - - @JvmStatic - val liveTask = MutableLiveData() //用于刷新UI - - @JvmStatic - val allTask = ArrayList() //所有下载任务 - private val waitTask = LinkedList() //等待下载的任务 - private val downloadingTask = LinkedList() //下载中的任务 - - //记录每个文件的总大小,key为文件url - private val lengthMap = HashMap() - - @JvmStatic - fun addTasks(tasks: ArrayList) { - val allTaskList = allTask - tasks.forEach { - if (!allTaskList.contains(it)) { - val md5Key = it.url.encodeUtf8().md5().hex() - val length = Preferences.getValue(md5Key, -1L) - if (length != -1L) { - it.totalSize = length - it.currentSize = File(it.localPath).length() - it.progress = it.currentSize * 1.0f / it.totalSize - lengthMap[it.url] = length - - if (it.currentSize > 0) { - it.state = PAUSED - } - if (it.totalSize == it.currentSize) { - //如果当前size等于总size,则任务文件下载完成,注意: 这个判断不是100%准确,最好能对文件做md5校验 - it.state = COMPLETED - } - } - allTaskList.add(it) - } - } - } - - //开始下载所有任务 - @JvmStatic - fun startAllDownloadTask() { - val allTaskList = allTask - allTaskList.forEach { - if (it.state != COMPLETED && it.state != DOWNLOADING) { - download(it) - } - } - } - - @JvmStatic - fun download(task: DownloadTask) { - if (downloadingTask.size >= MAX_TASK_COUNT) { - //超过最大下载数,添加进等待队列 - task.state = WAITING - updateTask(task) - waitTask.offer(task) - return - } - - task.state = DOWNLOADING - updateTask(task) - downloadingTask.add(task) - - //如果想使用RxJava或Flow下载,更改以下代码即可 - CoroutineScope(Dispatchers.Main).launch { - RxHttp.get(task.url) - .tag(task.url) //记录tag,手动取消下载时用到 - .toDownloadFlow(task.localPath, true) - .onProgress{ - //下载进度回调,0-100,仅在进度有更新时才会回调 - task.progress = it.fraction //当前进度 [0.0, 1.0] - task.currentSize = it.currentSize //当前已下载的字节大小 - task.totalSize = it.totalSize //要下载的总字节大小 - updateTask(task) - val key = task.url - val length = lengthMap[key] - if (length != task.totalSize) { - //服务器返回的文件总大小与本地的不一致,则更新 - lengthMap[key] = task.totalSize - saveTotalSize(lengthMap) - } - }.catch { - //手动取消下载时,会收到StreamResetException异常,不做任何处理 - if (it !is StreamResetException) { - Tip.show("下载失败") - task.state = FAIL - } - }.collect { - Tip.show("下载成功") - task.state = COMPLETED - } - - //下载结束,不管任务成功还是失败,如果还有在等待的任务,都开启下一个任务 - updateTask(task) - downloadingTask.remove(task) - waitTask.poll()?.let { download(it) } - } - } - - - private fun saveTotalSize(map: HashMap) { - val editor = Preferences.getEditor() - for ((key, value) in map) { - val md5Key = key.encodeUtf8().md5().hex() - editor.putLong(md5Key, value) - } - editor.commit() - } - - - //关闭所有任务 - @JvmStatic - fun cancelAllTask() { - var iterator = waitTask.iterator() - while (iterator.hasNext()) { - val task = iterator.next() - task.state = CANCEL - iterator.remove() - updateTask(task) - } - - iterator = downloadingTask.iterator() - while (iterator.hasNext()) { - val task = iterator.next() - iterator.remove() - RxHttpPlugins.cancelAll(task.url) - task.state = CANCEL - updateTask(task) - } - } - - //等待中->取消下载 - @JvmStatic - fun removeWaitTask(task: DownloadTask) { - waitTask.remove(task) - task.state = CANCEL - updateTask(task) - } - - //暂停下载 - @JvmStatic - fun pauseTask(task: DownloadTask) { - //根据tag取消下载 - RxHttpPlugins.cancelAll(task.url) - task.state = PAUSED - updateTask(task) - } - - @JvmStatic - fun haveTaskExecuting(): Boolean { - return waitTask.size > 0 || downloadingTask.size > 0 - } - - //发送通知,更新UI - private fun updateTask(task: DownloadTask) { - liveTask.value = task - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/await_fragment.xml b/app/src/main/res/layout/await_fragment.xml deleted file mode 100644 index 042ec19b..00000000 --- a/app/src/main/res/layout/await_fragment.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - -