├── .circleci └── config.yml ├── .github └── workflows │ ├── build.yml │ ├── docs.yml │ └── issues-stale.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── GIF.gif ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ ├── app-release.apk │ └── output-metadata.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── king │ │ └── retrofit │ │ └── retrofithelper │ │ └── app │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── king │ │ │ └── retrofit │ │ │ └── retrofithelper │ │ │ └── app │ │ │ ├── App.kt │ │ │ ├── Constants.kt │ │ │ ├── LoadingFragment.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MainViewModel.kt │ │ │ ├── Repository.kt │ │ │ ├── api │ │ │ └── ApiService.kt │ │ │ ├── factory │ │ │ ├── StringConverterFactory.kt │ │ │ ├── StringRequestBodyConverter.kt │ │ │ └── StringResponseBodyConverter.kt │ │ │ └── interceptor │ │ │ └── LogInterceptor.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── loading_fragment.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── network_security_config.xml │ └── test │ └── java │ └── com │ └── king │ └── retrofit │ └── retrofithelper │ └── app │ └── ExampleUnitTest.kt ├── build.gradle ├── build_docs.sh ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mkdocs.yml ├── retrofit-helper ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── king │ │ └── retrofit │ │ └── retrofithelper │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── king │ │ └── retrofit │ │ └── retrofithelper │ │ ├── RetrofitHelper.java │ │ ├── annotation │ │ ├── BaseUrl.java │ │ ├── DomainName.java │ │ ├── RequestProgress.java │ │ ├── ResponseProgress.java │ │ └── Timeout.java │ │ ├── body │ │ ├── ProgressRequestBody.java │ │ └── ProgressResponseBody.java │ │ ├── interceptor │ │ ├── DomainInterceptor.java │ │ ├── HeaderInterceptor.java │ │ ├── ProgressInterceptor.java │ │ └── TimeoutInterceptor.java │ │ ├── listener │ │ └── ProgressListener.java │ │ └── parser │ │ ├── DomainParser.java │ │ └── HttpUrlParser.java │ └── test │ └── java │ └── com │ └── king │ └── retrofit │ └── retrofithelper │ └── ExampleUnitTest.java ├── settings.gradle └── versions.gradle /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | android: circleci/android@0.2.0 5 | 6 | jobs: 7 | build: 8 | executor: android/android 9 | 10 | steps: 11 | - checkout 12 | - run: 13 | command: ./gradlew build -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up JDK 11 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: 'zulu' 19 | java-version: 11 20 | - name: Build with Gradle 21 | run: ./gradlew build 22 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | env: 9 | JAVA_VERSION: 11 10 | PYTHON_VERSION: 3.x 11 | 12 | permissions: 13 | contents: write 14 | id-token: write 15 | pages: write 16 | 17 | jobs: 18 | docs: 19 | environment: 20 | name: github-pages 21 | url: ${{ steps.deployment.outputs.page_url }} 22 | runs-on: ubuntu-latest 23 | if: github.ref == 'refs/heads/master' 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | with: 29 | fetch-depth: 0 30 | 31 | - name: Configure JDK 32 | uses: actions/setup-java@v4 33 | with: 34 | distribution: 'zulu' 35 | java-version: ${{ env.JAVA_VERSION }} 36 | 37 | - name: Install Python 38 | uses: actions/setup-python@v5 39 | with: 40 | python-version: ${{ env.PYTHON_VERSION }} 41 | 42 | - name: Install MkDocs Material 43 | run: pip install mkdocs-material 44 | 45 | - name: Generate Docs 46 | run: ./build_docs.sh 47 | 48 | - name: Upload to GitHub Pages 49 | uses: actions/upload-pages-artifact@v3 50 | with: 51 | path: site 52 | 53 | - name: Deploy to GitHub Pages 54 | id: deployment 55 | uses: actions/deploy-pages@v4 56 | -------------------------------------------------------------------------------- /.github/workflows/issues-stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '0 16 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v5 14 | with: 15 | stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' 16 | days-before-stale: 30 17 | days-before-close: 5 18 | exempt-all-pr-milestones: true 19 | exempt-issue-labels: 'help wanted' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | dist: trusty 3 | jdk: oraclejdk8 4 | sudo: false 5 | 6 | env: 7 | global: 8 | - ANDROID_API_LEVEL=29 9 | - ANDROID_BUILD_TOOLS_VERSION=29.0.3 10 | - TRAVIS_SECURE_ENV_VARS=true 11 | 12 | before_install: 13 | - chmod +x gradlew 14 | - mkdir "$ANDROID_HOME/licenses" || true 15 | # Hack to accept Android licenses 16 | - yes | sdkmanager "platforms;android-$ANDROID_API_LEVEL" 17 | 18 | 19 | android: 20 | components: 21 | # The BuildTools version used by your project 22 | - tools 23 | - platform-tools 24 | - build-tools-$ANDROID_BUILD_TOOLS_VERSION 25 | # The SDK version used to compile your project 26 | - android-$ANDROID_API_LEVEL 27 | - extra-android-m2repository 28 | - extra-google-android-support 29 | 30 | script: 31 | - ./gradlew clean 32 | - ./gradlew assembleRelease -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 版本日志 2 | 3 | #### v1.1.0:2023-1-4 4 | * 新增支持请求响应进度监听 5 | 6 | #### v1.0.1:2021-4-23 7 | * 新增支持添加公共请求头 8 | * 新增 **@BaseUrl** 注解 9 | 10 | #### v1.0.0:2020-5-30 11 | * RetrofitHelper初始版本 -------------------------------------------------------------------------------- /GIF.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenly1314/RetrofitHelper/671181a7d7b790733fe2419468819e4659a01b8b/GIF.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Jenly Yu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RetrofitHelper 2 | 3 | ![Image](app/src/main/ic_launcher-playstore.png) 4 | 5 | [![MavenCentral](https://img.shields.io/maven-central/v/com.github.jenly1314/retrofit-helper?logo=sonatype)](https://repo1.maven.org/maven2/com/github/jenly1314/RetrofitHelper) 6 | [![JitPack](https://img.shields.io/jitpack/v/github/jenly1314/RetrofitHelper?logo=jitpack)](https://jitpack.io/#jenly1314/RetrofitHelper) 7 | [![CI](https://img.shields.io/github/actions/workflow/status/jenly1314/RetrofitHelper/build.yml?logo=github)](https://github.com/jenly1314/RetrofitHelper/actions/workflows/build.yml) 8 | [![Download](https://img.shields.io/badge/download-APK-brightgreen?logo=github)](https://raw.githubusercontent.com/jenly1314/RetrofitHelper/master/app/release/app-release.apk) 9 | [![API](https://img.shields.io/badge/API-16%2B-brightgreen?logo=android)](https://developer.android.com/guide/topics/manifest/uses-sdk-element#ApiLevels) 10 | [![License](https://img.shields.io/github/license/jenly1314/RetrofitHelper?logo=open-source-initiative)](https://opensource.org/licenses/mit) 11 | 12 | 13 | RetrofitHelper for Android 是一个为 Retrofit 提供便捷配置多个BaseUrl相关的扩展帮助类。 14 | 15 | ## 主要功能介绍 16 | - ✅ 支持配置多个BaseUrl 17 | - ✅ 支持动态改变BaseUrl 18 | - ✅ 支持动态配置超时时长 19 | - ✅ 支持添加公共请求头 20 | - ✅ 支持请求响应进度监听 21 | 22 | ## 效果展示 23 | ![Image](GIF.gif) 24 | 25 | > 你也可以直接下载 [演示App](https://raw.githubusercontent.com/jenly1314/RetrofitHelper/master/app/release/app-release.apk) 体验效果 26 | 27 | ## 引入 28 | 29 | ### Gradle: 30 | 31 | 1. 在Project的 **build.gradle** 或 **setting.gradle** 中添加远程仓库 32 | 33 | ```gradle 34 | repositories { 35 | //... 36 | mavenCentral() 37 | } 38 | ``` 39 | 40 | 2. 在Module的 **build.gradle** 中添加依赖项 41 | 42 | ```gradle 43 | // AndroidX 版本 44 | implementation 'com.github.jenly1314:retrofit-helper:1.1.0' 45 | ``` 46 | 47 | > 因为 **RetrofitHelper** 依赖的 **retrofit** 只在编译时有效,所以在使用时,您的项目还需依赖 **retrofit** 才能正常使用。 48 | 49 | ## 使用 50 | 51 | ### 示例 52 | 53 | #### 主要集成步骤代码示例 54 | 55 | Step.1 需使用JDK8编译,在你项目中的build.gradle的android{}中添加配置: 56 | ```gradle 57 | compileOptions { 58 | targetCompatibility JavaVersion.VERSION_1_8 59 | sourceCompatibility JavaVersion.VERSION_1_8 60 | } 61 | 62 | ``` 63 | 64 | Step.2 通过RetrofitHelper初始化OkHttpClient,进行初始化配置 65 | ```kotlin 66 | // 通过RetrofitHelper创建一个支持多个BaseUrl的 OkHttpClient 67 | // 方式一 68 | val clientBuilder = RetrofitHelper.getInstance() 69 | .createClientBuilder() 70 | //...你自己的其他配置 71 | 72 | // 方式二 73 | val okHttpClient = RetrofitHelper.getInstance() 74 | .with(clientBuilder) 75 | //...你自己的其他配置 76 | .build() 77 | 78 | ``` 79 | 80 | ```kotlin 81 | // 完整示例 82 | val okHttpClient = RetrofitHelper.getInstance() 83 | .createClientBuilder() 84 | .addInterceptor(LogInterceptor()) 85 | .build() 86 | val retrofit = Retrofit.Builder() 87 | .baseUrl(Constants.BASE_URL) 88 | .client(okHttpClient) 89 | .addConverterFactory(GsonConverterFactory.create(Gson())) 90 | .build() 91 | ``` 92 | 93 | Step.3 定义接口时,通过注解标记对应接口,支持动态改变 BaseUrl相关功能 94 | ```kotlin 95 | interface ApiService { 96 | 97 | /** 98 | * 接口示例,没添加任何标识,和常规使用一致 99 | * @return 100 | */ 101 | @GET("api/user") 102 | fun getUser(): Call 103 | 104 | /** 105 | * Retrofit默认返回接口定义示例,添加 DomainName 标示 和 Timeout 标示 106 | * @return 107 | */ 108 | @DomainName(domainName) // domainName 域名别名标识,用于支持切换对应的 BaseUrl 109 | @Timeout(connectTimeout = 15,readTimeout = 15,writeTimeout = 15,timeUnit = TimeUnit.SECONDS) //超时标识,用于自定义超时时长 110 | @GET("api/user") 111 | fun getUser(): Call 112 | 113 | /** 114 | * 动态改变 BaseUrl 115 | * @return 116 | */ 117 | @BaseUrl(baseUrl) // baseUrl 标识,用于支持指定 BaseUrl 118 | @GET("api/user") 119 | fun getUser(): Call 120 | 121 | //-------------------------------------- 122 | 123 | /** 124 | * 使用 RxJava 返回接口定义示例,添加 DomainName 标示 和 Timeout 标示 125 | * @return 126 | */ 127 | @DomainName(domainName) // domainName 域名别名标识,用于支持切换对应的 BaseUrl 128 | @Timeout(connectTimeout = 20,readTimeout = 10) // 超时标识,用于自定义超时时长 129 | @GET("api/user") 130 | fun getUser(): Observable 131 | 132 | /** 133 | * 下载;可通过 RetrofitHelper.getInstance().addResponseListener(key, listener) 监听下载进度 134 | */ 135 | @ResponseProgress(key) // 支持响应进度监听,自定义配置监听的key 136 | @Streaming 137 | @GET("api/download") 138 | fun download(): Call 139 | } 140 | 141 | ``` 142 | 143 | Step.4 添加多个 BaseUrl 支持 144 | ```kotlin 145 | // 添加多个 BaseUrl 支持 ,domainName为域名别名标识,domainUrl为域名对应的 BaseUrl,与上面的接口定义表示一致即可生效 146 | RetrofitHelper.getInstance().putDomain(domainName,domainUrl) 147 | ``` 148 | ```kotlin 149 | // 添加多个 BaseUrl 支持 示例 150 | RetrofitHelper.getInstance().apply { 151 | //GitHub baseUrl 152 | putDomain(Constants.DOMAIN_GITHUB,Constants.GITHUB_BASE_URL) 153 | //Google baseUrl 154 | putDomain(Constants.DOMAIN_GOOGLE,Constants.GOOGLE_BASE_URL) 155 | } 156 | ``` 157 | 158 | Step.5 添加进度监听 159 | 160 | ```kotlin 161 | // 添加请求进度监听:key默认为请求的url,也可以通过 {@link RequestProgress} 来自定义配置接口对应的key;自定义配置的key优先级高于默认的url 162 | RetrofitHelper.getInstance().addRequestListener(key, object: ProgressListener{ 163 | override fun onProgress(currentBytes: Long, contentLength: Long, completed: Boolean) { 164 | 165 | } 166 | 167 | override fun onException(e: Exception) { 168 | 169 | } 170 | }) 171 | ``` 172 | 173 | ```kotlin 174 | // 添加响应进度监听:key默认为请求的url,也可以通过 {@link ResponseProgress} 来自定义配置接口对应的key;自定义配置的key优先级高于默认的url 175 | RetrofitHelper.getInstance().addResponseListener(key, object: ProgressListener{ 176 | override fun onProgress(currentBytes: Long, contentLength: Long, completed: Boolean) { 177 | 178 | } 179 | 180 | override fun onException(e: Exception) { 181 | 182 | } 183 | }) 184 | ``` 185 | 186 | #### RetrofitHelper 说明 187 | ```java 188 | /** 189 | * Retrofit帮助类 190 | * 191 | * 主要功能介绍: 192 | * 1.支持管理多个 BaseUrl,且支持运行时动态改变 193 | * 2.支持接口自定义超时时长,满足每个接口动态定义超时时长 194 | * 3.支持添加公共请求头 195 | * 196 | * RetrofitHelper中的核心方法 197 | * 198 | * {@link #createClientBuilder()} 创建 {@link OkHttpClient.Builder}初始化一些配置参数,用于支持多个 BaseUrl 199 | * 200 | * {@link #with(OkHttpClient.Builder)} 传入 {@link OkHttpClient.Builder} 配置一些参数,用于支持多个 BaseUrl 201 | * 202 | * {@link #setBaseUrl(String)} 和 {@link #setBaseUrl(HttpUrl)} 主要用于设置默认的 BaseUrl。 203 | * 204 | * {@link #putDomain(String, String)} 和 {@link #putDomain(String, HttpUrl)} 主要用于支持多个 BaseUrl,且支持 BaseUrl 动态改变。 205 | * 206 | * {@link #setDynamicDomain(boolean)} 设置是否支持 配置多个BaseUrl,且支持动态改变,一般会通过其他途径自动开启,此方法一般不会主动用到,只有在特殊场景下可能会有此需求,所以提供此方法主要用于提供更多种可能。 207 | * 208 | * {@link #setHttpUrlParser(HttpUrlParser)} 设置 HttpUrl解析器 , 当前默认采用的 {@link DomainParser} 实现类,你也可以自定义实现 {@link HttpUrlParser} 209 | * 210 | * {@link #setAddHeader(boolean)} 设置是否添加头,一般会通过{@link #addHeader(String, String)}相关方法自动开启,此方法一般不会主动用到,只有特殊场景下会有此需求,主要用于提供统一控制。 211 | * 212 | * {@link #addHeader(String, String)} 设置头,主要用于添加公共头消息。 213 | * 214 | * {@link #addHeaders(Map)} 设置头,主要用于设置公共头消息。 215 | * 216 | * {@link #addRequestListener(String, ProgressListener)} 添加请求监听。 217 | * 218 | * {@link #addResponseListener(String, ProgressListener)} 添加响应监听。 219 | * 220 | * 这里只是列出一些对外使用的核心方法,和相关的简单说明。如果想了解更多,可以查看对应的方法和详情。 221 | * 222 | * @author Jenly 223 | */ 224 | public final class RetrofitHelper{ 225 | //... 226 | } 227 | 228 | ``` 229 | 230 | #### 特别说明 231 | ```kotlin 232 | //通过setBaseUrl可以动态改变全局的 BaseUrl,优先级比putDomain(domainName,domainUrl)低,谨慎使用 233 | RetrofitHelper.getInstance().setBaseUrl(dynamicUrl) 234 | ``` 235 | 236 | 更多使用详情,请查看[app](app)中的源码使用示例或直接查看[API帮助文档](https://jenly1314.github.io/RetrofitHelper/api/) 237 | 238 | ## 相关推荐 239 | 240 | - [BaseUrlManager](https://github.com/jenly1314/BaseUrlManager) BaseUrl管理器,主要打测试包时,一个App可动态切换到不同的开发环境或测试环境。 241 | - [MVVMFrame](https://github.com/jenly1314/MVVMFrame) 一个基于Google官方推出的JetPack构建的MVVM快速开发框架。 242 | - [AppUpdater](https://github.com/jenly1314/AppUpdater) 一个专注于App更新,一键傻瓜式集成App版本升级的轻量开源库。 243 | - [ImageViewer](http://github.com/AndroidKTX/ImageViewer) 一个图片查看器,一般用来查看图片详情或查看大图时使用。 244 | - [LogX](http://github.com/jenly1314/LogX) 一个轻量而强大的日志框架;好用不解释。 245 | - [KVCache](http://github.com/jenly1314/KVCache) 一个便于统一管理的键值缓存库;支持无缝切换缓存实现。 246 | - [AndroidKTX](http://github.com/AndroidKTX/AndroidKTX) 一个简化 Android 开发的 Kotlin 工具类集合。 247 | - [AndroidUtil](http://github.com/AndroidUtil/AndroidUtil) 一个整理了Android常用工具类集合,平时在开发的过程中可能会经常用到。 248 | 249 | 250 | 251 | ## 版本日志 252 | 253 | #### v1.1.0:2023-1-4 254 | * 新增支持请求响应进度监听 255 | 256 | #### [查看更多版本日志](CHANGELOG.md) 257 | 258 | --- 259 | 260 | ![footer](https://jenly1314.github.io/page/footer.svg) 261 | 262 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk build_versions.compileSdk 8 | 9 | defaultConfig { 10 | applicationId "com.king.retrofit.retrofithelper.app" 11 | minSdk build_versions.minSdk 12 | targetSdk build_versions.targetSdk 13 | versionCode app_version.versionCode 14 | versionName app_version.versionName 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | multiDexEnabled true 17 | 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | compileOptions { 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | } 31 | kotlinOptions { 32 | jvmTarget = JavaVersion.VERSION_1_8.toString() 33 | } 34 | 35 | buildFeatures { 36 | viewBinding true 37 | } 38 | namespace 'com.king.retrofit.retrofithelper.app' 39 | 40 | } 41 | 42 | dependencies { 43 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin" 44 | implementation "androidx.appcompat:appcompat:$versions.appcompat" 45 | implementation "androidx.core:core-ktx:$versions.core_ktx" 46 | 47 | testImplementation "junit:junit:$versions.junit" 48 | androidTestImplementation "androidx.test.ext:junit:$versions.androidExtJunit" 49 | androidTestImplementation "androidx.test:runner:$versions.runner" 50 | androidTestImplementation "androidx.test.espresso:espresso-core:$versions.espresso_core" 51 | 52 | implementation "androidx.constraintlayout:constraintlayout:$versions.constraintlayout" 53 | 54 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.kotlinxCoroutines" 55 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.kotlinxCoroutines" 56 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$versions.viewmodelKtx" 57 | 58 | implementation "com.squareup.retrofit2:retrofit:$versions.retrofit" 59 | implementation "com.squareup.retrofit2:converter-gson:$versions.retrofit" 60 | 61 | implementation project(':retrofit-helper') 62 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/release/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenly1314/RetrofitHelper/671181a7d7b790733fe2419468819e4659a01b8b/app/release/app-release.apk -------------------------------------------------------------------------------- /app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "com.king.retrofit.retrofithelper.app", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "versionCode": 3, 14 | "versionName": "1.1.0", 15 | "outputFile": "app-release.apk" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/king/retrofit/retrofithelper/app/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.king.http.retrofithelper.app", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenly1314/RetrofitHelper/671181a7d7b790733fe2419468819e4659a01b8b/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/App.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app 2 | 3 | import android.app.Application 4 | import com.king.retrofit.retrofithelper.RetrofitHelper 5 | 6 | /** 7 | * @author Jenly 8 | */ 9 | class App : Application() { 10 | 11 | 12 | override fun onCreate() { 13 | super.onCreate() 14 | 15 | //添加多个 BaseUrl 支持 16 | RetrofitHelper.getInstance().apply { 17 | //GitHub baseUrl 18 | putDomain(Constants.DOMAIN_GITHUB,Constants.GITHUB_BASE_URL) 19 | //Google baseUrl 20 | putDomain(Constants.DOMAIN_GOOGLE,Constants.GOOGLE_BASE_URL) 21 | //Add header 22 | // addHeader("seq",System.currentTimeMillis().toString()) 23 | } 24 | } 25 | 26 | 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app 2 | 3 | 4 | /** 5 | * @author Jenly 6 | */ 7 | object Constants { 8 | 9 | const val BASE_URL = "https://jenly1314.github.io" 10 | 11 | const val DOMAIN_GITHUB = "github" 12 | 13 | const val DOMAIN_GOOGLE = "google" 14 | 15 | const val DOMAIN_DYNAMIC = "dynamic" 16 | 17 | const val GITHUB_BASE_URL = "https://github.com" 18 | 19 | const val GOOGLE_BASE_URL = "https://google.com" 20 | 21 | const val BAIDU_BASE_URL = "https://baidu.com" 22 | 23 | const val DOWNLOAD_URL = "https://repo1.maven.org/maven2/com/github/jenly1314/retrofit-helper/1.1.0/retrofit-helper-1.1.0-javadoc.jar" 24 | 25 | const val RESPONSE_PROGRESS_1 = "response_progress_1" 26 | const val RESPONSE_PROGRESS_2 = "response_progress_2" 27 | 28 | const val TAG = "RetrofitHelper" 29 | 30 | const val URL_REGEX = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]" 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/LoadingFragment.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app 2 | 3 | import android.graphics.Color 4 | import android.graphics.drawable.ColorDrawable 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.view.Window 10 | import androidx.fragment.app.DialogFragment 11 | 12 | /** 13 | * @author Jenly 14 | */ 15 | class LoadingFragment : DialogFragment(){ 16 | 17 | override fun onCreateView( 18 | inflater: LayoutInflater, 19 | container: ViewGroup?, 20 | savedInstanceState: Bundle? 21 | ): View? { 22 | dialog?.requestWindowFeature(Window.FEATURE_NO_TITLE) 23 | return inflater.inflate(R.layout.loading_fragment,container,false) 24 | } 25 | 26 | override fun onActivityCreated(savedInstanceState: Bundle?) { 27 | super.onActivityCreated(savedInstanceState) 28 | dialog?.window?.run { 29 | setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) 30 | attributes?.dimAmount = 0f 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app 2 | 3 | import androidx.appcompat.app.AppCompatActivity 4 | import android.os.Bundle 5 | import android.text.TextUtils 6 | import android.text.method.ScrollingMovementMethod 7 | import android.util.Log 8 | import android.view.View 9 | import androidx.lifecycle.ViewModelProvider 10 | import com.king.retrofit.retrofithelper.RetrofitHelper 11 | import com.king.retrofit.retrofithelper.app.databinding.ActivityMainBinding 12 | import com.king.retrofit.retrofithelper.listener.ProgressListener 13 | import java.lang.Exception 14 | 15 | class MainActivity : AppCompatActivity() { 16 | 17 | private val loadingFragment by lazy { LoadingFragment() } 18 | 19 | private val viewModel by lazy { ViewModelProvider.AndroidViewModelFactory.getInstance(application).create(MainViewModel::class.java) } 20 | 21 | private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) } 22 | 23 | private var isDownload = false 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | setContentView(binding.root) 28 | initData() 29 | } 30 | 31 | private fun initData(){ 32 | viewModel.liveDataStatus.observe(this) { 33 | loading(it) 34 | } 35 | 36 | viewModel.liveData.observe(this) { 37 | binding.tvResponse.text = it 38 | if(isDownload){ 39 | isDownload = false 40 | } 41 | } 42 | 43 | binding.tvResponse.movementMethod = ScrollingMovementMethod.getInstance() 44 | 45 | binding.etUrl.setText(Constants.BAIDU_BASE_URL) 46 | binding.etUrl.setSelection(binding.etUrl.length()) 47 | // 添加响应进度监听 48 | RetrofitHelper.getInstance().addResponseListener( "${Constants.BAIDU_BASE_URL}/index.html", object: ProgressListener{ 49 | override fun onProgress(currentBytes: Long, contentLength: Long, completed: Boolean) { 50 | Log.d(Constants.TAG, "onProgress: ${currentBytes}/${contentLength} - ${currentBytes.toFloat().div(contentLength).times(100F)}% - $completed") 51 | } 52 | 53 | override fun onException(e: Exception) { 54 | e.message?.let { 55 | Log.e(Constants.TAG, it) 56 | } 57 | } 58 | }) 59 | 60 | // 添加响应进度监听 61 | RetrofitHelper.getInstance().addResponseListener(Constants.RESPONSE_PROGRESS_1, object: ProgressListener{ 62 | override fun onProgress(currentBytes: Long, contentLength: Long, completed: Boolean) { 63 | Log.d(Constants.TAG, "${Constants.RESPONSE_PROGRESS_1}: ${currentBytes}/${contentLength} - ${currentBytes.toFloat().div(contentLength).times(100F)}% - $completed") 64 | } 65 | 66 | override fun onException(e: Exception) { 67 | e.message?.let { 68 | Log.e(Constants.TAG, it) 69 | } 70 | } 71 | }) 72 | 73 | // 添加响应进度监听 74 | RetrofitHelper.getInstance().addResponseListener(Constants.RESPONSE_PROGRESS_2, object: ProgressListener{ 75 | override fun onProgress(currentBytes: Long, contentLength: Long, completed: Boolean) { 76 | val progress = if(contentLength > 0){ 77 | currentBytes.toFloat().div(contentLength).times(100F).toInt() 78 | }else { 79 | 0 80 | } 81 | Log.d(Constants.TAG, "${Constants.RESPONSE_PROGRESS_2}: ${currentBytes}/${contentLength} - ${currentBytes.toFloat().div(contentLength).times(100F)}% - $completed") 82 | binding.progressBar.progress = progress 83 | if(completed){ 84 | isDownload = false 85 | binding.tvResponse.text = "下载完成" 86 | }else{ 87 | binding.tvResponse.text = "进度:${currentBytes}/${contentLength} - 进度百分比: ${progress}%" 88 | } 89 | } 90 | 91 | override fun onException(e: Exception) { 92 | e.message?.let { 93 | Log.e(Constants.TAG, it) 94 | } 95 | isDownload = false 96 | binding.tvResponse.text = e.message 97 | } 98 | }) 99 | 100 | } 101 | 102 | private fun loading(isLoading: Boolean){ 103 | if(isLoading){ 104 | loadingFragment.show(supportFragmentManager,"loading") 105 | }else{ 106 | loadingFragment.dismiss() 107 | } 108 | } 109 | 110 | override fun onStop() { 111 | super.onStop() 112 | RetrofitHelper.getInstance().clearListener() 113 | } 114 | 115 | 116 | fun onClick(v: View){ 117 | when(v.id){ 118 | //原始BaseUrl请求 119 | R.id.btn1 -> viewModel.getRequest1() 120 | //切换到GitHub Url 121 | R.id.btn2 -> viewModel.getRequest2() 122 | //切换到Google Url 123 | R.id.btn3 -> viewModel.getRequest3() 124 | //切换到BaiDu Url 125 | R.id.btn4 -> viewModel.getRequest4() 126 | //动态设置BaseUrl 127 | R.id.btn5 -> { 128 | 129 | if(TextUtils.isEmpty(binding.etUrl.text)){ 130 | return 131 | } 132 | val dynamicUrl = binding.etUrl.text.toString() 133 | //动态改变 BaseUrl,这里只改变标记为Constants.DOMAIN_DYNAMIC的接口 134 | RetrofitHelper.getInstance().putDomain(Constants.DOMAIN_DYNAMIC,dynamicUrl) 135 | //通过setBaseUrl可以动态改变全局的 BaseUrl,优先级比domainName低,谨慎使用 136 | // RetrofitHelper.getInstance().setBaseUrl(dynamicUrl) 137 | viewModel.getRequest5() 138 | } 139 | R.id.btn6 -> { 140 | if(!isDownload){ 141 | isDownload = true 142 | Log.d(Constants.TAG, "download...") 143 | viewModel.download() 144 | } 145 | } 146 | } 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app 2 | 3 | import android.util.Log 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.launch 9 | import kotlinx.coroutines.withContext 10 | import retrofit2.await 11 | import java.lang.Exception 12 | import kotlin.math.log 13 | 14 | /** 15 | * @author Jenly 16 | */ 17 | class MainViewModel : ViewModel() { 18 | 19 | val liveDataStatus by lazy { MutableLiveData() } 20 | 21 | val liveData by lazy { MutableLiveData() } 22 | 23 | fun getRequest1(){ 24 | launch{ 25 | val response = Repository.getRequest1().await() 26 | liveData.value = response 27 | } 28 | } 29 | 30 | fun getRequest2(){ 31 | launch{ 32 | val response = Repository.getRequest2().await() 33 | liveData.value = response 34 | } 35 | } 36 | 37 | fun getRequest3(){ 38 | launch{ 39 | val response = Repository.getRequest3().await() 40 | liveData.value = response 41 | } 42 | } 43 | 44 | fun getRequest4(){ 45 | launch{ 46 | val response = Repository.getRequest4().await() 47 | liveData.value = response 48 | } 49 | } 50 | 51 | fun getRequest5(){ 52 | launch{ 53 | val response = Repository.getRequest5().await() 54 | liveData.value = response 55 | } 56 | } 57 | 58 | fun download(){ 59 | launch { 60 | val result = withContext(Dispatchers.IO){ 61 | val response = Repository.download() 62 | val inputStream = response.execute().body()?.byteStream() 63 | inputStream?.let { 64 | var buffer = ByteArray(1024) 65 | while (it.read(buffer) != -1){ 66 | 67 | } 68 | true 69 | } ?: false 70 | } 71 | Log.d(Constants.TAG, "result: $result") 72 | } 73 | } 74 | 75 | private fun launch(block: suspend () -> Unit) = viewModelScope.launch { 76 | try { 77 | liveDataStatus.value = true 78 | block() 79 | }catch (e: Exception){ 80 | e.printStackTrace() 81 | liveData.value = e.message 82 | } 83 | liveDataStatus.value = false 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/Repository.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app 2 | 3 | import com.king.retrofit.retrofithelper.RetrofitHelper 4 | import com.king.retrofit.retrofithelper.app.api.ApiService 5 | import com.king.retrofit.retrofithelper.app.factory.StringConverterFactory 6 | import com.king.retrofit.retrofithelper.app.interceptor.LogInterceptor 7 | import okhttp3.OkHttpClient 8 | import retrofit2.Retrofit 9 | 10 | /** 11 | * @author Jenly 12 | */ 13 | object Repository { 14 | 15 | private val mRetrofit by lazy { 16 | val okHttpClient = RetrofitHelper.getInstance() 17 | .createClientBuilder() 18 | .addInterceptor(LogInterceptor()) 19 | .build() 20 | Retrofit.Builder() 21 | .baseUrl(Constants.BASE_URL) 22 | .client(okHttpClient) 23 | .addConverterFactory(StringConverterFactory()) //这里主要是方便用来演示多个BaseUrl 24 | .build() 25 | } 26 | 27 | private val mApiService by lazy { 28 | mRetrofit.create(ApiService::class.java) 29 | } 30 | 31 | fun getRequest1() = mApiService.getRequest1() 32 | 33 | fun getRequest2() = mApiService.getRequest2() 34 | 35 | fun getRequest3() = mApiService.getRequest3() 36 | 37 | fun getRequest4() = mApiService.getRequest4() 38 | 39 | fun getRequest5() = mApiService.getRequest5() 40 | 41 | fun download() = mApiService.download(Constants.DOWNLOAD_URL) 42 | 43 | 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/api/ApiService.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app.api 2 | 3 | import com.king.retrofit.retrofithelper.annotation.* 4 | import com.king.retrofit.retrofithelper.app.Constants 5 | import okhttp3.ResponseBody 6 | import retrofit2.Call 7 | import retrofit2.http.GET 8 | import retrofit2.http.Streaming 9 | import retrofit2.http.Url 10 | 11 | /** 12 | * @author Jenly 13 | */ 14 | interface ApiService { 15 | 16 | /** 17 | * 原始请求 18 | * @return 19 | */ 20 | @GET("index.html") 21 | fun getRequest1(): Call 22 | 23 | /** 24 | * BaseUrl切换成 GitHub Url 25 | * @return 26 | */ 27 | @DomainName(Constants.DOMAIN_GITHUB) // 域名别名标识,用于支持切换对应的 BaseUrl 28 | @GET("index.html") 29 | fun getRequest2(): Call 30 | 31 | /** 32 | * 自定义超时 33 | * BaseUrl切换成 Google Url 34 | * @return 35 | */ 36 | @DomainName(Constants.DOMAIN_GOOGLE) // 域名别名标识,用于支持切换对应的 BaseUrl 37 | @Timeout(connectTimeout = 15,readTimeout = 15,writeTimeout = 15) // 超时标识,用于自定义超时时长 38 | @GET("index.html") 39 | fun getRequest3(): Call 40 | 41 | /** 42 | * 动态改变 BaseUrl 43 | * @return 44 | */ 45 | @BaseUrl(Constants.BAIDU_BASE_URL) // BaseUrl标识,用于支持指定 BaseUrl 46 | @GET("index.html") 47 | fun getRequest4(): Call 48 | 49 | 50 | /** 51 | * 动态改变 BaseUrl 52 | * @return 53 | */ 54 | @ResponseProgress(Constants.RESPONSE_PROGRESS_1) // 支持响应进度监听,自定义配置监听的key 55 | @DomainName(Constants.DOMAIN_DYNAMIC) // 域名别名标识,用于支持切换对应的 BaseUrl 56 | @GET("index.html") 57 | fun getRequest5(): Call 58 | 59 | /** 60 | * 下载;可通过 RetrofitHelper.getInstance().addResponseListener(key, listener) 监听下载进度 61 | */ 62 | @ResponseProgress(Constants.RESPONSE_PROGRESS_2) // 支持响应进度监听,自定义配置监听的key 63 | @Streaming 64 | @GET 65 | fun download(@Url url: String): Call 66 | 67 | } -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/factory/StringConverterFactory.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app.factory 2 | 3 | import okhttp3.RequestBody 4 | import okhttp3.ResponseBody 5 | import retrofit2.Converter 6 | import retrofit2.Retrofit 7 | import java.lang.reflect.Type 8 | 9 | /** 10 | * @author Jenly 11 | */ 12 | class StringConverterFactory internal constructor() : Converter.Factory() { 13 | 14 | 15 | 16 | override fun requestBodyConverter( 17 | type: Type, 18 | parameterAnnotations: Array, 19 | methodAnnotations: Array, 20 | retrofit: Retrofit 21 | ): Converter { 22 | return StringRequestBodyConverter() 23 | } 24 | 25 | override fun responseBodyConverter( 26 | type: Type, 27 | annotations: Array, 28 | retrofit: Retrofit 29 | ): Converter { 30 | return StringResponseBodyConverter() 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/factory/StringRequestBodyConverter.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app.factory 2 | 3 | import okhttp3.MediaType 4 | import okhttp3.RequestBody 5 | import okio.Buffer 6 | import retrofit2.Converter 7 | import java.io.OutputStreamWriter 8 | import java.nio.charset.Charset 9 | 10 | /** 11 | * @author Jenly 12 | */ 13 | class StringRequestBodyConverter : Converter{ 14 | 15 | companion object{ 16 | 17 | private val MEDIA_TYPE = MediaType.parse("text/plain; charset=UTF-8") 18 | private val UTF_8 = Charset.forName("UTF-8") 19 | } 20 | 21 | override fun convert(value: String): RequestBody? { 22 | val buffer = Buffer() 23 | val writer = OutputStreamWriter(buffer.outputStream(), UTF_8); 24 | writer.write(value) 25 | writer.close() 26 | return RequestBody.create(MEDIA_TYPE, buffer.readByteString()) 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/factory/StringResponseBodyConverter.kt: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app.factory 2 | 3 | import okhttp3.ResponseBody 4 | import retrofit2.Converter 5 | 6 | /** 7 | * @author Jenly 8 | */ 9 | class StringResponseBodyConverter() : Converter{ 10 | 11 | override fun convert(value: ResponseBody): String? { 12 | value.use { 13 | return it.string() 14 | } 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/king/retrofit/retrofithelper/app/interceptor/LogInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.king.retrofit.retrofithelper.app.interceptor; 2 | 3 | import android.util.Log; 4 | 5 | import com.king.retrofit.retrofithelper.app.Constants; 6 | 7 | import java.io.EOFException; 8 | import java.io.IOException; 9 | 10 | import okhttp3.Interceptor; 11 | import okhttp3.MediaType; 12 | import okhttp3.Request; 13 | import okhttp3.RequestBody; 14 | import okhttp3.Response; 15 | import okhttp3.ResponseBody; 16 | import okio.Buffer; 17 | import retrofit2.Invocation; 18 | import retrofit2.http.Streaming; 19 | 20 | /** 21 | * @author Jenly 22 | */ 23 | public class LogInterceptor implements Interceptor { 24 | 25 | @Override 26 | public Response intercept(Chain chain) throws IOException { 27 | Request request = chain.request(); 28 | Log.i(Constants.TAG,String.format("%1$s -> %2$s",request.method(),request.url())); 29 | 30 | if(request.headers() != null){ 31 | Log.d(Constants.TAG,"Headers:" + request.headers()); 32 | } 33 | 34 | RequestBody requestBody = request.body(); 35 | if(requestBody != null){ 36 | final Buffer buffer = new Buffer(); 37 | requestBody.writeTo(buffer); 38 | if(isPlaintext(buffer)){ 39 | Log.d(Constants.TAG,"RequestBody:" + buffer.readUtf8()); 40 | }else{ 41 | Log.d(Constants.TAG, "RequestBody:(binary " + requestBody.contentLength() + "-byte body omitted)"); 42 | } 43 | 44 | } 45 | 46 | Invocation invocation = request.tag(Invocation.class); 47 | if(invocation != null){ 48 | Streaming streaming = invocation.method().getAnnotation(Streaming.class); 49 | if(streaming != null){ 50 | Log.d(Constants.TAG, "Streaming..."); 51 | return chain.proceed(chain.request()); 52 | } 53 | } 54 | Response response = chain.proceed(chain.request()); 55 | MediaType mediaType = response.body().contentType(); 56 | String responseBody = response.body().string(); 57 | Log.d(Constants.TAG,"ResponseBody:" + responseBody); 58 | 59 | return response.newBuilder() 60 | .body(ResponseBody.create(mediaType, responseBody)) 61 | .build(); 62 | } 63 | 64 | private static boolean isPlaintext(Buffer buffer) { 65 | try { 66 | Buffer prefix = new Buffer(); 67 | long byteCount = buffer.size() < 64 ? buffer.size() : 64; 68 | buffer.copyTo(prefix, 0, byteCount); 69 | for (int i = 0; i < 16; i++) { 70 | if (prefix.exhausted()) { 71 | break; 72 | } 73 | int codePoint = prefix.readUtf8CodePoint(); 74 | if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { 75 | return false; 76 | } 77 | } 78 | return true; 79 | } catch (EOFException e) { 80 | Log.w(Constants.TAG, e); 81 | return false; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 |