├── .gitignore ├── .travis.yml ├── LICENSE ├── README-zh.md ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── me │ │ └── jessyan │ │ └── retrofiturlmanager │ │ └── demo │ │ ├── BaseApplication.java │ │ ├── MainActivity.java │ │ ├── NetWorkManager.java │ │ └── api │ │ ├── Api.java │ │ ├── OneApiService.java │ │ ├── ThreeApiService.java │ │ └── TwoApiService.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── art └── overview.gif ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── manager ├── .gitignore ├── bintray.gradle ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── me │ │ └── jessyan │ │ └── retrofiturlmanager │ │ ├── InvalidUrlException.java │ │ ├── RetrofitUrlManager.java │ │ ├── Utils.java │ │ ├── cache │ │ ├── Cache.java │ │ └── LruCache.java │ │ ├── onUrlChangeListener.java │ │ └── parser │ │ ├── AdvancedUrlParser.java │ │ ├── DefaultUrlParser.java │ │ ├── DomainUrlParser.java │ │ ├── SuperUrlParser.java │ │ └── UrlParser.java │ └── res │ └── values │ └── strings.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea 38 | 39 | # Keystore files 40 | *.jks 41 | 42 | # MacOS 43 | .DS_Store 44 | 45 | # External native build folder generated in Android Studio 2.2 and later 46 | .externalNativeBuild 47 | 48 | # Google Services (e.g. APIs or Firebase) 49 | google-services.json 50 | 51 | # Freeline 52 | freeline.py 53 | freeline/ 54 | freeline_project_description.json 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | before_install: 4 | - yes | sdkmanager "platforms;android-27" 5 | 6 | env: 7 | global: 8 | - ANDROID_API_LEVEL=27 9 | - ANDROID_BUILD_TOOLS_VERSION=27.0.3 10 | - TRAVIS_SECURE_ENV_VARS=true 11 | 12 | android: 13 | components: 14 | # The BuildTools version used by your project 15 | - tools 16 | - platform-tools 17 | - build-tools-$ANDROID_BUILD_TOOLS_VERSION 18 | - extra-android-m2repository 19 | - extra-google-android-support 20 | 21 | # The SDK version used to compile your project 22 | - android-$ANDROID_API_LEVEL 23 | licenses: 24 | - '.+' 25 | 26 | script: 27 | - ./gradlew clean 28 | - ./gradlew assembleDebug 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # RetrofitUrlManager 2 | [ ![Jcenter](https://img.shields.io/badge/Jcenter-v1.4.0-brightgreen.svg?style=flat-square) ](https://bintray.com/jessyancoding/maven/retrofit-url-manager/1.4.0/link) 3 | [ ![Build Status](https://travis-ci.org/JessYanCoding/RetrofitUrlManager.svg?branch=master) ](https://travis-ci.org/JessYanCoding/RetrofitUrlManager) 4 | [ ![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-RetrofitUrlManager-brightgreen.svg?style=flat-square) ](https://android-arsenal.com/details/1/6007) 5 | [ ![API](https://img.shields.io/badge/API-9%2B-blue.svg?style=flat-square) ](https://developer.android.com/about/versions/android-2.3.html) 6 | [ ![License](http://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square) ](http://www.apache.org/licenses/LICENSE-2.0) 7 | [ ![Author](https://img.shields.io/badge/Author-JessYan-orange.svg?style=flat-square) ](https://www.jianshu.com/u/1d0c0bc634db) 8 | [ ![QQ-Group](https://img.shields.io/badge/QQ%E7%BE%A4-455850365%20%7C%20301733278-orange.svg?style=flat-square) ](https://shang.qq.com/wpa/qunwpa?idkey=7e59e59145e6c7c68932ace10f52790636451f01d1ecadb6a652b1df234df753) 9 | 10 | ## Let Retrofit support multiple baseUrl and can be change the baseUrl at runtime. 11 | 12 | ## Overview 13 | ![overview](art/overview.gif) 14 | 15 | ## Introduction 16 | 以最简洁的 **Api** 让 **Retrofit** 同时支持多个 **BaseUrl** 以及动态改变 **BaseUrl**. 17 | 18 | ## Notice 19 | [**框架的分析和思路 (一)**](http://www.jianshu.com/p/2919bdb8d09a) 20 | 21 | [**框架的分析和思路 (二)**](https://www.jianshu.com/p/35a8959c2f86) 22 | 23 | [**更完整的 Sample**](https://github.com/JessYanCoding/ArmsComponent) 24 | 25 | ## Download 26 | ``` gradle 27 | implementation 'me.jessyan:retrofit-url-manager:1.4.0' 28 | ``` 29 | 30 | ## Usage 31 | ### Initialize 32 | ``` java 33 | // 构建 OkHttpClient 时,将 OkHttpClient.Builder() 传入 with() 方法,进行初始化配置 34 | OkHttpClient = RetrofitUrlManager.getInstance().with(new OkHttpClient.Builder()) 35 | .build(); 36 | ``` 37 | 38 | ### Step 1 39 | ``` java 40 | public interface ApiService { 41 | @Headers({"Domain-Name: douban"}) // 加上 Domain-Name header 42 | @GET("/v2/book/{id}") 43 | Observable getBook(@Path("id") int id); 44 | } 45 | 46 | ``` 47 | 48 | ### Step 2 49 | ``` java 50 | // 可在 App 运行时,随时切换 BaseUrl (指定了 Domain-Name header 的接口) 51 | RetrofitUrlManager.getInstance().putDomain("douban", "https://api.douban.com"); 52 | ``` 53 | 54 | ### If you want to change the global BaseUrl 55 | ```java 56 | // 全局 BaseUrl 的优先级低于 Domain-Name header 中单独配置的,其他未配置的接口将受全局 BaseUrl 的影响 57 | RetrofitUrlManager.getInstance().setGlobalDomain("your BaseUrl"); 58 | 59 | ``` 60 | 61 | ## About Me 62 | * **Email**: 63 | * **Home**: 64 | * **掘金**: 65 | * **简书**: 66 | 67 | ## License 68 | ``` 69 | Copyright 2017, jessyan 70 | 71 | Licensed under the Apache License, Version 2.0 (the "License"); 72 | you may not use this file except in compliance with the License. 73 | You may obtain a copy of the License at 74 | 75 | http://www.apache.org/licenses/LICENSE-2.0 76 | 77 | Unless required by applicable law or agreed to in writing, software 78 | distributed under the License is distributed on an "AS IS" BASIS, 79 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 80 | See the License for the specific language governing permissions and 81 | limitations under the License. 82 | ``` 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RetrofitUrlManager 2 | [ ![Jcenter](https://img.shields.io/badge/Jcenter-v1.4.0-brightgreen.svg?style=flat-square) ](https://bintray.com/jessyancoding/maven/retrofit-url-manager/1.4.0/link) 3 | [ ![Build Status](https://travis-ci.org/JessYanCoding/RetrofitUrlManager.svg?branch=master) ](https://travis-ci.org/JessYanCoding/RetrofitUrlManager) 4 | [ ![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-RetrofitUrlManager-brightgreen.svg?style=flat-square) ](https://android-arsenal.com/details/1/6007) 5 | [ ![API](https://img.shields.io/badge/API-9%2B-blue.svg?style=flat-square) ](https://developer.android.com/about/versions/android-2.3.html) 6 | [ ![License](http://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square) ](http://www.apache.org/licenses/LICENSE-2.0) 7 | [ ![Author](https://img.shields.io/badge/Author-JessYan-orange.svg?style=flat-square) ](https://www.jianshu.com/u/1d0c0bc634db) 8 | [ ![QQ-Group](https://img.shields.io/badge/QQ%E7%BE%A4-455850365%20%7C%20301733278-orange.svg?style=flat-square) ](https://shang.qq.com/wpa/qunwpa?idkey=7e59e59145e6c7c68932ace10f52790636451f01d1ecadb6a652b1df234df753) 9 | 10 | ## Let Retrofit support multiple baseUrl and can be change the baseUrl at runtime. 11 | 12 | [中文说明](README-zh.md) 13 | 14 | ## Overview 15 | ![overview](art/overview.gif) 16 | 17 | ## Notice 18 | [**Framework analysis 1**](http://www.jianshu.com/p/2919bdb8d09a) 19 | 20 | [**Framework analysis 2**](https://www.jianshu.com/p/35a8959c2f86) 21 | 22 | [**More complete sample**](https://github.com/JessYanCoding/ArmsComponent) 23 | 24 | ## Download 25 | ``` gradle 26 | implementation 'me.jessyan:retrofit-url-manager:1.4.0' 27 | ``` 28 | 29 | ## Usage 30 | ### Initialize 31 | ``` java 32 | // When building OkHttpClient, the OkHttpClient.Builder() is passed to the with() method to initialize the configuration 33 | OkHttpClient = RetrofitUrlManager.getInstance().with(new OkHttpClient.Builder()) 34 | .build(); 35 | ``` 36 | 37 | ### Step 1 38 | ``` java 39 | public interface ApiService { 40 | @Headers({"Domain-Name: douban"}) // Add the Domain-Name header 41 | @GET("/v2/book/{id}") 42 | Observable getBook(@Path("id") int id); 43 | } 44 | 45 | ``` 46 | 47 | ### Step 2 48 | ``` java 49 | // You can change BaseUrl at any time while App is running (The interface that declared the Domain-Name header) 50 | RetrofitUrlManager.getInstance().putDomain("douban", "https://api.douban.com"); 51 | ``` 52 | 53 | ### If you want to change the global BaseUrl: 54 | ```java 55 | // BaseUrl configured in the Domain-Name header will override BaseUrl in the global setting 56 | RetrofitUrlManager.getInstance().setGlobalDomain("your BaseUrl"); 57 | 58 | ``` 59 | 60 | ## About Me 61 | * **Email**: 62 | * **Home**: 63 | * **掘金**: 64 | * **简书**: 65 | 66 | ## License 67 | ``` 68 | Copyright 2017, jessyan 69 | 70 | Licensed under the Apache License, Version 2.0 (the "License"); 71 | you may not use this file except in compliance with the License. 72 | You may obtain a copy of the License at 73 | 74 | http://www.apache.org/licenses/LICENSE-2.0 75 | 76 | Unless required by applicable law or agreed to in writing, software 77 | distributed under the License is distributed on an "AS IS" BASIS, 78 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 79 | See the License for the specific language governing permissions and 80 | limitations under the License. 81 | ``` 82 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.compileSdkVersion 5 | buildToolsVersion rootProject.buildToolsVersion 6 | defaultConfig { 7 | applicationId "me.jessyan.retrofiturlmanager.demo" 8 | minSdkVersion 14 9 | targetSdkVersion rootProject.targetSdkVersion 10 | versionCode rootProject.versionCode 11 | versionName rootProject.versionName 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | lintOptions { 21 | abortOnError false 22 | warning 'InvalidPackage' 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation 'com.android.support:appcompat-v7:27.1.1' 28 | implementation 'com.squareup.okhttp3:okhttp:3.10.0' 29 | implementation 'com.squareup.retrofit2:retrofit:2.4.0' 30 | implementation 'com.squareup.retrofit2:converter-gson:2.4.0' 31 | implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' 32 | implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' 33 | implementation project(':manager') 34 | // implementation 'me.jessyan:retrofit-url-manager:1.4.0' 35 | } 36 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/jess/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/java/me/jessyan/retrofiturlmanager/demo/BaseApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 JessYan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.jessyan.retrofiturlmanager.demo; 17 | 18 | import android.app.Application; 19 | 20 | import me.jessyan.retrofiturlmanager.RetrofitUrlManager; 21 | import me.jessyan.retrofiturlmanager.parser.AdvancedUrlParser; 22 | import me.jessyan.retrofiturlmanager.parser.DomainUrlParser; 23 | import me.jessyan.retrofiturlmanager.parser.SuperUrlParser; 24 | 25 | import static me.jessyan.retrofiturlmanager.demo.api.Api.APP_DOUBAN_DOMAIN; 26 | import static me.jessyan.retrofiturlmanager.demo.api.Api.APP_GANK_DOMAIN; 27 | import static me.jessyan.retrofiturlmanager.demo.api.Api.APP_GITHUB_DOMAIN; 28 | import static me.jessyan.retrofiturlmanager.demo.api.Api.DOUBAN_DOMAIN_NAME; 29 | import static me.jessyan.retrofiturlmanager.demo.api.Api.GANK_DOMAIN_NAME; 30 | import static me.jessyan.retrofiturlmanager.demo.api.Api.GITHUB_DOMAIN_NAME; 31 | 32 | /** 33 | * ================================================ 34 | * RetrofitUrlManager 以简洁的 Api, 让 Retrofit 不仅支持多 BaseUrl 35 | * 还可以在 App 运行时动态切换任意 BaseUrl, 在多 BaseUrl 场景下也不会影响到其他不需要切换的 BaseUrl 36 | *

37 | * 想要更深入的使用本框架必须要了解2个术语 pathSegments 和 PathSize 38 | * "https://www.github.com/wiki/part?name=jess" 其中的 "/wiki" 和 "/part" 就是 pathSegment, PathSize 就是 pathSegment 的 Size 39 | * 这个 Url 的 PathSize 就是 2, 可以粗略理解为域名后面跟了几个 "/" PathSize 就是几 40 | *

41 | * 本框架分为三种模式, 普通模式 (默认)、高级模式 (需要手动开启)、超级模式 (需要手动开启) 42 | *

43 | * 普通模式: 44 | * 普通模式只能替换域名, 比如使用 "https:www.google.com" 作为 Retrofit 的 BaseUrl 可以被替换 45 | * 但是以 "https:www.google.com/api" 作为 BaseUrl 还是只能替换其中的域名 "https:www.google.com" 46 | * 详细替换规则可以查看 {@link DomainUrlParser} 47 | *

48 | * 高级模式: 49 | * 高级模式只能替换 {@link RetrofitUrlManager#startAdvancedModel(String)} 中传入的 BaseUrl, 但可以替换拥有多个 pathSegments 的 BaseUrl 50 | * 如 "https:www.google.com/api", 需要手动开启高级模式 {@link RetrofitUrlManager#startAdvancedModel(String)} 51 | * 详细替换规则可以查看 {@link AdvancedUrlParser} 52 | *

53 | * 超级模式: 54 | * 详细替换规则可以查看 {@link SuperUrlParser} 55 | * 超级模式属于高级模式的加强版, 优先级高于高级模式, 在高级模式中, 需要传入一个 BaseUrl (您传入 Retrofit 的 BaseUrl) 作为被替换的基准 56 | * 如这个传入的 BaseUrl 为 "https://www.github.com/wiki/part" (PathSize = 2), 那框架会将所有需要被替换的 Url 中的 域名 以及 域名 后面的前两个 pathSegments 57 | * 使用您传入 {@link RetrofitUrlManager#putDomain(String, String)} 方法的 Url 替换掉 58 | * 但如果突然有一小部分的 Url 只想将 "https://www.github.com/wiki" (PathSize = 1) 替换掉, 后面的 pathSegment '/part' 想被保留下来 59 | * 这时项目中就出现了多个 PathSize 不同的需要被替换的 BaseUrl 60 | *

61 | * 使用高级模式实现这种需求略显麻烦, 所以我创建了超级模式, 让每一个 Url 都可以随意指定不同的 BaseUrl (PathSize 自己定) 作为被替换的基准 62 | * 使 RetrofitUrlManager 可以从容应对各种复杂的需求 63 | *

64 | * 超级模式也需要手动开启, 但与高级模式不同的是, 开启超级模式并不需要调用 API, 只需要在 Url 中加入 {@link RetrofitUrlManager#IDENTIFICATION_PATH_SIZE} + PathSize 65 | *

66 | * 至此三种模式替换 BaseUrl 的自由程度 (可扩展性) 排名, 从小到大依次是: 67 | * 普通模式 (只能替换域名) < 高级模式 (只能替换 {@link RetrofitUrlManager#startAdvancedModel(String)} 中传入的 BaseUrl) < 超级模式 (每个 Url 都可以随意指定可被替换的 BaseUrl, pathSize 随意变换) 68 | *

69 | * 三种模式在使用上的复杂程度排名, 从小到大依次是: 70 | * 普通模式 (无需做过多配置) < 高级模式 (App 初始化时调用一次 {@link RetrofitUrlManager#startAdvancedModel(String)} 即可) < 超级模式 (每个需要被替换 BaseUrl 的 Url 中都需要加入 {@link RetrofitUrlManager#IDENTIFICATION_PATH_SIZE} + PathSize) 71 | *

72 | * 由此可见,自由度越强, 操作也越复杂, 所以可以根据自己的需求选择不同的模式, 并且也可以在需求变化时随意升级或降级这三种模式 73 | *

74 | * Created by JessYan on 19/07/2017 18:45 75 | * Contact me 76 | * Follow me 77 | * ================================================ 78 | */ 79 | public class BaseApplication extends Application { 80 | 81 | @Override 82 | public void onCreate() { 83 | super.onCreate(); 84 | RetrofitUrlManager.getInstance().setDebug(true); 85 | //将每个 BaseUrl 进行初始化,运行时可以随时改变 DOMAIN_NAME 对应的值,从而达到切换 BaseUrl 的效果 86 | RetrofitUrlManager.getInstance().putDomain(GITHUB_DOMAIN_NAME, APP_GITHUB_DOMAIN); 87 | RetrofitUrlManager.getInstance().putDomain(GANK_DOMAIN_NAME, APP_GANK_DOMAIN); 88 | RetrofitUrlManager.getInstance().putDomain(DOUBAN_DOMAIN_NAME, APP_DOUBAN_DOMAIN); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/me/jessyan/retrofiturlmanager/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 JessYan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.jessyan.retrofiturlmanager.demo; 17 | 18 | import android.app.ProgressDialog; 19 | import android.content.DialogInterface; 20 | import android.os.Bundle; 21 | import android.support.v7.app.AlertDialog; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.util.Log; 24 | import android.view.View; 25 | import android.widget.EditText; 26 | import android.widget.Toast; 27 | 28 | import java.io.IOException; 29 | 30 | import io.reactivex.Observable; 31 | import io.reactivex.ObservableSource; 32 | import io.reactivex.ObservableTransformer; 33 | import io.reactivex.Observer; 34 | import io.reactivex.android.schedulers.AndroidSchedulers; 35 | import io.reactivex.disposables.Disposable; 36 | import io.reactivex.functions.Action; 37 | import io.reactivex.functions.Consumer; 38 | import io.reactivex.schedulers.Schedulers; 39 | import me.jessyan.retrofiturlmanager.RetrofitUrlManager; 40 | import me.jessyan.retrofiturlmanager.onUrlChangeListener; 41 | import me.jessyan.retrofiturlmanager.parser.AdvancedUrlParser; 42 | import me.jessyan.retrofiturlmanager.parser.DomainUrlParser; 43 | import me.jessyan.retrofiturlmanager.parser.SuperUrlParser; 44 | import okhttp3.HttpUrl; 45 | import okhttp3.ResponseBody; 46 | 47 | import static me.jessyan.retrofiturlmanager.demo.api.Api.DOUBAN_DOMAIN_NAME; 48 | import static me.jessyan.retrofiturlmanager.demo.api.Api.GANK_DOMAIN_NAME; 49 | import static me.jessyan.retrofiturlmanager.demo.api.Api.GITHUB_DOMAIN_NAME; 50 | 51 | /** 52 | * ================================================ 53 | * RetrofitUrlManager 以简洁的 Api, 让 Retrofit 不仅支持多 BaseUrl 54 | * 还可以在 App 运行时动态切换任意 BaseUrl, 在多 BaseUrl 场景下也不会影响到其他不需要切换的 BaseUrl 55 | *

56 | * 想要更深入的使用本框架必须要了解2个术语 pathSegments 和 PathSize 57 | * "https://www.github.com/wiki/part?name=jess" 其中的 "/wiki" 和 "/part" 就是 pathSegment, PathSize 就是 pathSegment 的 Size 58 | * 这个 Url 的 PathSize 就是 2, 可以粗略理解为域名后面跟了几个 "/" PathSize 就是几 59 | *

60 | * 本框架分为三种模式, 普通模式 (默认)、高级模式 (需要手动开启)、超级模式 (需要手动开启) 61 | *

62 | * 普通模式: 63 | * 普通模式只能替换域名, 比如使用 "https:www.google.com" 作为 Retrofit 的 BaseUrl 可以被替换 64 | * 但是以 "https:www.google.com/api" 作为 BaseUrl 还是只能替换其中的域名 "https:www.google.com" 65 | * 详细替换规则可以查看 {@link DomainUrlParser} 66 | *

67 | * 高级模式: 68 | * 高级模式只能替换 {@link RetrofitUrlManager#startAdvancedModel(String)} 中传入的 BaseUrl, 但可以替换拥有多个 pathSegments 的 BaseUrl 69 | * 如 "https:www.google.com/api", 需要手动开启高级模式 {@link RetrofitUrlManager#startAdvancedModel(String)} 70 | * 详细替换规则可以查看 {@link AdvancedUrlParser} 71 | *

72 | * 超级模式: 73 | * 详细替换规则可以查看 {@link SuperUrlParser} 74 | * 超级模式属于高级模式的加强版, 优先级高于高级模式, 在高级模式中, 需要传入一个 BaseUrl (您传入 Retrofit 的 BaseUrl) 作为被替换的基准 75 | * 如这个传入的 BaseUrl 为 "https://www.github.com/wiki/part" (PathSize = 2), 那框架会将所有需要被替换的 Url 中的 域名 以及 域名 后面的前两个 pathSegments 76 | * 使用您传入 {@link RetrofitUrlManager#putDomain(String, String)} 方法的 Url 替换掉 77 | * 但如果突然有一小部分的 Url 只想将 "https://www.github.com/wiki" (PathSize = 1) 替换掉, 后面的 pathSegment '/part' 想被保留下来 78 | * 这时项目中就出现了多个 PathSize 不同的需要被替换的 BaseUrl 79 | *

80 | * 使用高级模式实现这种需求略显麻烦, 所以我创建了超级模式, 让每一个 Url 都可以随意指定不同的 BaseUrl (PathSize 自己定) 作为被替换的基准 81 | * 使 RetrofitUrlManager 可以从容应对各种复杂的需求 82 | *

83 | * 超级模式也需要手动开启, 但与高级模式不同的是, 开启超级模式并不需要调用 API, 只需要在 Url 中加入 {@link RetrofitUrlManager#IDENTIFICATION_PATH_SIZE} + PathSize 84 | *

85 | * 至此三种模式替换 BaseUrl 的自由程度 (可扩展性) 排名, 从小到大依次是: 86 | * 普通模式 (只能替换域名) < 高级模式 (只能替换 {@link RetrofitUrlManager#startAdvancedModel(String)} 中传入的 BaseUrl) < 超级模式 (每个 Url 都可以随意指定可被替换的 BaseUrl, pathSize 随意变换) 87 | *

88 | * 三种模式在使用上的复杂程度排名, 从小到大依次是: 89 | * 普通模式 (无需做过多配置) < 高级模式 (App 初始化时调用一次 {@link RetrofitUrlManager#startAdvancedModel(String)} 即可) < 超级模式 (每个需要被替换 BaseUrl 的 Url 中都需要加入 {@link RetrofitUrlManager#IDENTIFICATION_PATH_SIZE} + PathSize) 90 | *

91 | * 由此可见,自由度越强, 操作也越复杂, 所以可以根据自己的需求选择不同的模式, 并且也可以在需求变化时随意升级或降级这三种模式 92 | *

93 | * Created by JessYan on 18/07/2017 17:03 94 | * Contact me 95 | * Follow me 96 | * ================================================ 97 | */ 98 | public class MainActivity extends AppCompatActivity { 99 | 100 | private EditText mUrl1; 101 | private EditText mUrl2; 102 | private EditText mUrl3; 103 | private EditText mGlobalUrl; 104 | private ProgressDialog mProgressDialog; 105 | private ChangeListener mListener; 106 | 107 | @Override 108 | protected void onCreate(Bundle savedInstanceState) { 109 | super.onCreate(savedInstanceState); 110 | initView(); 111 | initListener(); 112 | } 113 | 114 | private void initView() { 115 | setContentView(R.layout.activity_main); 116 | mUrl1 = (EditText) findViewById(R.id.et_url1); 117 | mUrl2 = (EditText) findViewById(R.id.et_url2); 118 | mUrl3 = (EditText) findViewById(R.id.et_url3); 119 | mGlobalUrl = (EditText) findViewById(R.id.et_global_url); 120 | mProgressDialog = new ProgressDialog(this); 121 | mUrl1.setSelection(mUrl1.getText().toString().length()); 122 | } 123 | 124 | 125 | private void initListener() { 126 | this.mListener = new ChangeListener(); 127 | //如果有需要可以注册监听器,当一个 Url 的 BaseUrl 被新的 Url 替代,则会回调这个监听器,调用时间是在接口请求服务器之前 128 | RetrofitUrlManager.getInstance().registerUrlChangeListener(mListener); 129 | //如果您已经确定了最终的 BaseUrl ,不需要再动态切换 BaseUrl, 请 RetrofitUrlManager.getInstance().setRun(false); 130 | 131 | findViewById(R.id.bt_request1).setOnClickListener(new View.OnClickListener() { 132 | @Override 133 | public void onClick(View v) { 134 | HttpUrl httpUrl = RetrofitUrlManager.getInstance().fetchDomain(GITHUB_DOMAIN_NAME); 135 | if (httpUrl == null || !httpUrl.toString().equals(mUrl1.getText().toString())) { //可以在 App 运行时随意切换某个接口的 BaseUrl 136 | RetrofitUrlManager.getInstance().putDomain(GITHUB_DOMAIN_NAME, mUrl1.getText().toString()); 137 | } 138 | NetWorkManager 139 | .getInstance() 140 | .getOneApiService() 141 | .getUsers(1, 10) 142 | .compose(MainActivity.this.getDefaultTransformer()) 143 | .subscribe(getDefaultObserver()); 144 | } 145 | }); 146 | 147 | findViewById(R.id.bt_request2).setOnClickListener(new View.OnClickListener() { 148 | @Override 149 | public void onClick(View v) { 150 | HttpUrl httpUrl = RetrofitUrlManager.getInstance().fetchDomain(GANK_DOMAIN_NAME); 151 | if (httpUrl == null || !httpUrl.toString().equals(mUrl2.getText().toString())) { //可以在 App 运行时随意切换某个接口的 BaseUrl 152 | RetrofitUrlManager.getInstance().putDomain(GANK_DOMAIN_NAME, mUrl2.getText().toString()); 153 | } 154 | /** 155 | * 使用超级模式请求, 详细注释请查看 {@link me.jessyan.retrofiturlmanager.demo.api.TwoApiService#getData(int, int)} 156 | */ 157 | NetWorkManager 158 | .getInstance() 159 | .getTwoApiService() 160 | .getData(10, 1) 161 | .compose(MainActivity.this.getDefaultTransformer()) 162 | .subscribe(getDefaultObserver()); 163 | } 164 | }); 165 | 166 | findViewById(R.id.bt_request3).setOnClickListener(new View.OnClickListener() { 167 | @Override 168 | public void onClick(View v) { 169 | HttpUrl httpUrl = RetrofitUrlManager.getInstance().fetchDomain(DOUBAN_DOMAIN_NAME); 170 | if (httpUrl == null || !httpUrl.toString().equals(mUrl3.getText().toString())) { //可以在 App 运行时随意切换某个接口的 BaseUrl 171 | RetrofitUrlManager.getInstance().putDomain(DOUBAN_DOMAIN_NAME, mUrl3.getText().toString()); 172 | } 173 | NetWorkManager 174 | .getInstance() 175 | .getThreeApiService() 176 | .getBook(1220562) 177 | .compose(MainActivity.this.getDefaultTransformer()) 178 | .subscribe(getDefaultObserver()); 179 | } 180 | }); 181 | 182 | } 183 | 184 | private void showResult(String result) { 185 | new AlertDialog.Builder(this) 186 | .setMessage(result) 187 | .setCancelable(true) 188 | .setPositiveButton("ok", new DialogInterface.OnClickListener() { 189 | @Override 190 | public void onClick(DialogInterface dialog, int which) { 191 | dialog.dismiss(); 192 | } 193 | }) 194 | .setNegativeButton("cancel", new DialogInterface.OnClickListener() { 195 | @Override 196 | public void onClick(DialogInterface dialog, int which) { 197 | dialog.dismiss(); 198 | } 199 | }) 200 | .create() 201 | .show(); 202 | } 203 | 204 | /** 205 | * 什么是高级模式? 什么是 pathSegment? 建议先看看最上面的类注释! {@link MainActivity} 206 | */ 207 | public void startAdvancedModel(View view) { 208 | final EditText editText = new EditText(MainActivity.this); 209 | editText.setBackgroundDrawable(null); 210 | editText.setText("http://jessyan.me/1"); 211 | editText.setPadding(80, 30, 0, 0); 212 | new AlertDialog.Builder(this) 213 | .setTitle("增加或减少下面的 pathSegment, 看看替换后的 Url 有什么不同?") 214 | .setView(editText) 215 | .setCancelable(true) 216 | .setPositiveButton("ok", new DialogInterface.OnClickListener() { 217 | @Override 218 | public void onClick(DialogInterface dialog, int which) { 219 | dialog.dismiss(); 220 | RetrofitUrlManager.getInstance().startAdvancedModel(editText.getText().toString()); 221 | } 222 | }) 223 | .setNegativeButton("cancel", new DialogInterface.OnClickListener() { 224 | @Override 225 | public void onClick(DialogInterface dialog, int which) { 226 | dialog.dismiss(); 227 | } 228 | }) 229 | .create() 230 | .show(); 231 | } 232 | 233 | public void urlNotChange(View view) { 234 | showResult("使用本框架的全局 BaseUrl 后, 默认整个项目的所有 Url 都会被全局 BaseUrl 替换, 但是在实际开发中又需要某些 Url 保持原样不被全局 BaseUrl 替换掉, 比如请求某些固定的图片下载地址时, 这时在这个 Url 地址尾部加上 RetrofitUrlManager.IDENTIFICATION_IGNORE 即可避免被全局 BaseUrl 替换, 可以使用 RetrofitUrlManager.getInstance().setUrlNotChange(url) 方法, 该方法返回的 Url 已帮您自动在 Url 尾部加上该标志!"); 235 | } 236 | 237 | // 请求默认 BaseUrl,请求的接口没有配置 DomainHeader,所以只受全局 BaseUrl的影响 238 | public void btnRequestDefault(View view) { 239 | NetWorkManager 240 | .getInstance() 241 | .getOneApiService() 242 | .requestDefault() 243 | .compose(this.getDefaultTransformer()) 244 | .subscribe(getDefaultObserver()); 245 | } 246 | 247 | // 设置全局替换的 BaseUrl 248 | public void btnSetGlobalUrl(View view) { 249 | //当您项目中只有一个 BaseUrl, 但需要动态切换 BaseUrl 时, 全局 BaseUrl 显得非常方便 250 | //使用 RetrofitUrlManager.getInstance().setUrlNotChange(url); 方法处理过的 url 地址进行网络请求 251 | //则可以使此 url 地址忽略掉本框架的所有更改效果 252 | HttpUrl httpUrl = RetrofitUrlManager.getInstance().getGlobalDomain(); 253 | if (null == httpUrl || !httpUrl.toString().equals(mGlobalUrl.getText().toString().trim())) 254 | RetrofitUrlManager.getInstance().setGlobalDomain(mGlobalUrl.getText().toString().trim()); 255 | 256 | Toast.makeText(getApplicationContext(), "全局替换baseUrl成功", Toast.LENGTH_SHORT).show(); 257 | } 258 | 259 | // 移除全局的 BaseUrl 260 | public void btnRmoveGlobalUrl(View view) { 261 | //不想再使用全局 BaseUrl ,想用之前传入 Retrofit 的默认 BaseUrl ,就Remove 262 | RetrofitUrlManager.getInstance().removeGlobalDomain(); 263 | Toast.makeText(getApplicationContext(), "移除了全局baseUrl", Toast.LENGTH_SHORT).show(); 264 | } 265 | 266 | private ObservableTransformer getDefaultTransformer() { 267 | return new ObservableTransformer() { 268 | @Override 269 | public ObservableSource apply(Observable upstream) { 270 | return upstream.subscribeOn(Schedulers.io()) 271 | .doOnSubscribe(new Consumer() { 272 | @Override 273 | public void accept(Disposable disposable) throws Exception { 274 | mProgressDialog.show(); 275 | } 276 | }) 277 | .subscribeOn(AndroidSchedulers.mainThread()) 278 | .observeOn(AndroidSchedulers.mainThread()) 279 | .doAfterTerminate(new Action() { 280 | @Override 281 | public void run() throws Exception { 282 | mProgressDialog.dismiss(); 283 | } 284 | }); 285 | } 286 | }; 287 | } 288 | 289 | private Observer getDefaultObserver() { 290 | return new Observer() { 291 | @Override 292 | public void onSubscribe(Disposable d) { 293 | 294 | } 295 | 296 | @Override 297 | public void onNext(ResponseBody response) { 298 | try { 299 | String string = response.string(); 300 | Log.d("test", string); 301 | showResult(string); 302 | } catch (IOException e) { 303 | e.printStackTrace(); 304 | } 305 | } 306 | 307 | @Override 308 | public void onError(Throwable throwable) { 309 | throwable.printStackTrace(); 310 | showResult(throwable.getMessage()); 311 | } 312 | 313 | @Override 314 | public void onComplete() { 315 | 316 | } 317 | }; 318 | } 319 | 320 | @Override 321 | protected void onDestroy() { 322 | super.onDestroy(); 323 | RetrofitUrlManager.getInstance().unregisterUrlChangeListener(mListener); //记住注销监听器 324 | } 325 | 326 | private class ChangeListener implements onUrlChangeListener { 327 | 328 | @Override 329 | public void onUrlChangeBefore(HttpUrl oldUrl, String domainName) { 330 | Log.d("MainActivity", String.format("The oldUrl is <%s>, ready fetch <%s> from DomainNameHub", 331 | oldUrl.toString(), 332 | domainName)); 333 | } 334 | 335 | @Override 336 | public void onUrlChanged(final HttpUrl newUrl, HttpUrl oldUrl) { 337 | Observable.just(1) 338 | .observeOn(AndroidSchedulers.mainThread()) 339 | .subscribe(new Consumer() { 340 | @Override 341 | public void accept(Object o) throws Exception { 342 | Toast.makeText(getApplicationContext(), "The newUrl is { " + newUrl.toString() + " }", Toast.LENGTH_LONG).show(); 343 | } 344 | }, new Consumer() { 345 | @Override 346 | public void accept(Throwable throwable) throws Exception { 347 | throwable.printStackTrace(); 348 | } 349 | }); 350 | } 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /app/src/main/java/me/jessyan/retrofiturlmanager/demo/NetWorkManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 JessYan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.jessyan.retrofiturlmanager.demo; 17 | 18 | import java.util.concurrent.TimeUnit; 19 | 20 | import me.jessyan.retrofiturlmanager.RetrofitUrlManager; 21 | import me.jessyan.retrofiturlmanager.demo.api.OneApiService; 22 | import me.jessyan.retrofiturlmanager.demo.api.ThreeApiService; 23 | import me.jessyan.retrofiturlmanager.demo.api.TwoApiService; 24 | import okhttp3.OkHttpClient; 25 | import retrofit2.Retrofit; 26 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 27 | import retrofit2.converter.gson.GsonConverterFactory; 28 | 29 | import static me.jessyan.retrofiturlmanager.demo.api.Api.APP_DEFAULT_DOMAIN; 30 | 31 | /** 32 | * ================================================ 33 | * Created by JessYan on 18/07/2017 17:03 34 | * Contact me 35 | * Follow me 36 | * ================================================ 37 | */ 38 | public class NetWorkManager { 39 | private OkHttpClient mOkHttpClient; 40 | private Retrofit mRetrofit; 41 | private OneApiService mOneApiService; 42 | private TwoApiService mTwoApiService; 43 | private ThreeApiService mThreeApiService; 44 | 45 | private static class NetWorkManagerHolder { 46 | private static final NetWorkManager INSTANCE = new NetWorkManager(); 47 | } 48 | 49 | public static final NetWorkManager getInstance() { 50 | return NetWorkManagerHolder.INSTANCE; 51 | } 52 | 53 | private NetWorkManager() { 54 | this.mOkHttpClient = RetrofitUrlManager.getInstance().with(new OkHttpClient.Builder()) //RetrofitUrlManager 初始化 55 | .readTimeout(5, TimeUnit.SECONDS) 56 | .connectTimeout(5, TimeUnit.SECONDS) 57 | .build(); 58 | 59 | this.mRetrofit = new Retrofit.Builder() 60 | .baseUrl(APP_DEFAULT_DOMAIN) 61 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//使用rxjava 62 | .addConverterFactory(GsonConverterFactory.create())//使用Gson 63 | .client(mOkHttpClient) 64 | .build(); 65 | 66 | this.mOneApiService = mRetrofit.create(OneApiService.class); 67 | this.mTwoApiService = mRetrofit.create(TwoApiService.class); 68 | this.mThreeApiService = mRetrofit.create(ThreeApiService.class); 69 | } 70 | 71 | public OkHttpClient getOkHttpClient() { 72 | return mOkHttpClient; 73 | } 74 | 75 | public Retrofit getRetrofit() { 76 | return mRetrofit; 77 | } 78 | 79 | public OneApiService getOneApiService() { 80 | return mOneApiService; 81 | } 82 | 83 | public TwoApiService getTwoApiService() { 84 | return mTwoApiService; 85 | } 86 | 87 | public ThreeApiService getThreeApiService() { 88 | return mThreeApiService; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/me/jessyan/retrofiturlmanager/demo/api/Api.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 JessYan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.jessyan.retrofiturlmanager.demo.api; 17 | 18 | /** 19 | * ================================================ 20 | * Created by JessYan on 18/07/2017 17:01 21 | * Contact me 22 | * Follow me 23 | * ================================================ 24 | */ 25 | public interface Api { 26 | String APP_DEFAULT_DOMAIN = "http://jessyan.me"; 27 | 28 | String APP_GITHUB_DOMAIN = "https://api.github.com"; 29 | String APP_GANK_DOMAIN = "http://gank.io"; 30 | String APP_DOUBAN_DOMAIN = "https://api.douban.com"; 31 | 32 | String GITHUB_DOMAIN_NAME = "github"; 33 | String GANK_DOMAIN_NAME = "gank"; 34 | String DOUBAN_DOMAIN_NAME = "douban"; 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/me/jessyan/retrofiturlmanager/demo/api/OneApiService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 JessYan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.jessyan.retrofiturlmanager.demo.api; 17 | 18 | import io.reactivex.Observable; 19 | import me.jessyan.retrofiturlmanager.RetrofitUrlManager; 20 | import okhttp3.ResponseBody; 21 | import retrofit2.http.GET; 22 | import retrofit2.http.Headers; 23 | import retrofit2.http.Query; 24 | 25 | import static me.jessyan.retrofiturlmanager.RetrofitUrlManager.DOMAIN_NAME_HEADER; 26 | import static me.jessyan.retrofiturlmanager.demo.api.Api.GITHUB_DOMAIN_NAME; 27 | 28 | /** 29 | * ================================================ 30 | * Created by JessYan on 19/07/2017 11:49 31 | * Contact me 32 | * Follow me 33 | * ================================================ 34 | */ 35 | public interface OneApiService { 36 | /** 37 | * 如果不需要多个 BaseUrl, 继续使用初始化时传入 Retrofit 中的默认 BaseUrl, 就不要加上 DOMAIN_NAME_HEADER 这个 Header 38 | */ 39 | @Headers({DOMAIN_NAME_HEADER + GITHUB_DOMAIN_NAME}) 40 | /** 41 | * 可以通过在注解里给全路径达到使用不同的 BaseUrl, 但是这样无法在 App 运行时动态切换 BaseUrl 42 | */ 43 | @GET("/users") 44 | Observable getUsers(@Query("since") int lastIdQueried, @Query("per_page") int perPage); 45 | 46 | /** 47 | * 切换 Url 的优先级: DomainHeader 中对应 BaseUrl 的将覆盖全局的 BaseUrl 48 | * 这里不配置 DomainHeader, 将只受到设置的全局 BaseUrl 的影响, 没有全局 BaseUrl 将请求原始的 BaseUrl 49 | * 当你项目中只有一个 BaseUrl, 但需要动态切换 BaseUrl 时, 全局 BaseUrl 显得非常方便 50 | * 当你设置了全局 BaseUrl, 整个项目中没有加入 DomainHeader 的网络请求地址都将被替换成这个全局 BaseUrl 51 | * 如果在设置了全局 BaseUrl 的情况下你有某些网络请求地址不需要被替换成这个全局 BaseUrl, 请在 Url 地址尾部加入 {@link RetrofitUrlManager#IDENTIFICATION_IGNORE} 这个标识符 52 | * 当某些下载接口需要请求固定的全路径地址或者 Glide 使用 Okhttp 请求某一个固定图片地址, {@link RetrofitUrlManager#IDENTIFICATION_IGNORE} 就可以派上用场 53 | */ 54 | @GET("/BaseUrl-Solution") 55 | Observable requestDefault(); 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/me/jessyan/retrofiturlmanager/demo/api/ThreeApiService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 JessYan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.jessyan.retrofiturlmanager.demo.api; 17 | 18 | import io.reactivex.Observable; 19 | import okhttp3.ResponseBody; 20 | import retrofit2.http.GET; 21 | import retrofit2.http.Headers; 22 | import retrofit2.http.Path; 23 | 24 | import static me.jessyan.retrofiturlmanager.RetrofitUrlManager.DOMAIN_NAME_HEADER; 25 | import static me.jessyan.retrofiturlmanager.demo.api.Api.DOUBAN_DOMAIN_NAME; 26 | 27 | /** 28 | * ================================================ 29 | * Created by JessYan on 19/07/2017 11:50 30 | * Contact me 31 | * Follow me 32 | * ================================================ 33 | */ 34 | public interface ThreeApiService { 35 | /** 36 | * 如果不需要多个 BaseUrl, 继续使用初始化时传入 Retrofit 中的默认 BaseUrl, 就不要加上 DOMAIN_NAME_HEADER 这个 Header 37 | */ 38 | @Headers({DOMAIN_NAME_HEADER + DOUBAN_DOMAIN_NAME}) 39 | /** 40 | * 可以通过在注解里给全路径达到使用不同的 BaseUrl, 但是这样无法在 App 运行时动态切换 BaseUrl 41 | */ 42 | @GET("/v2/book/{id}") 43 | Observable getBook(@Path("id") int id); 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/me/jessyan/retrofiturlmanager/demo/api/TwoApiService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 JessYan 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.jessyan.retrofiturlmanager.demo.api; 17 | 18 | import io.reactivex.Observable; 19 | import me.jessyan.retrofiturlmanager.RetrofitUrlManager; 20 | import okhttp3.ResponseBody; 21 | import retrofit2.http.GET; 22 | import retrofit2.http.Headers; 23 | import retrofit2.http.Path; 24 | 25 | import static me.jessyan.retrofiturlmanager.RetrofitUrlManager.DOMAIN_NAME_HEADER; 26 | import static me.jessyan.retrofiturlmanager.demo.api.Api.GANK_DOMAIN_NAME; 27 | 28 | /** 29 | * ================================================ 30 | * Created by JessYan on 19/07/2017 11:50 31 | * Contact me 32 | * Follow me 33 | * ================================================ 34 | */ 35 | public interface TwoApiService { 36 | /** 37 | * 如果不需要多个 BaseUrl, 继续使用初始化时传入 Retrofit 中的默认 BaseUrl, 就不要加上 DOMAIN_NAME_HEADER 这个 Header 38 | */ 39 | @Headers({DOMAIN_NAME_HEADER + GANK_DOMAIN_NAME}) 40 | /** 41 | * 在 Url 的尾部加上 {@link RetrofitUrlManager#IDENTIFICATION_PATH_SIZE} + PathSize, 表示此 Url 使用超级模式 42 | * 超级模式是什么? 请看 {@link RetrofitUrlManager} 的类注释 43 | * {@link RetrofitUrlManager#IDENTIFICATION_PATH_SIZE} + 2 表示此 Url 中需要被替换的 BaseUrl 为 '域名/api/data', 它的 PathSize 等于 2 44 | */ 45 | @GET("/api/data/Android/{size}/{page}" + RetrofitUrlManager.IDENTIFICATION_PATH_SIZE + 2) 46 | Observable getData(@Path("size") int size, @Path("page") int page); 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 25 | 26 |