├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── dhy │ │ │ └── retrofitrxtest │ │ │ ├── API.kt │ │ │ ├── App.kt │ │ │ ├── MainActivity.kt │ │ │ ├── MockDataApiInterceptor.kt │ │ │ └── ResponsePacket.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.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 │ └── test │ └── java │ └── com │ └── dhy │ └── retrofitrxtest │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── dhy │ │ │ └── retrofitrxutil │ │ │ ├── BaseErrorHandler.kt │ │ │ ├── Error.kt │ │ │ ├── Extension.kt │ │ │ ├── IError.java │ │ │ ├── IErrorHandler.kt │ │ │ ├── IObserverX.kt │ │ │ ├── IResponseStatus.java │ │ │ ├── MultListenerDialog.kt │ │ │ ├── ObserverX.kt │ │ │ ├── ObserverXBuilder.kt │ │ │ ├── StyledProgress.kt │ │ │ ├── StyledProgressGenerator.kt │ │ │ ├── ThrowableBZ.kt │ │ │ └── sample │ │ │ ├── SampleErrorHandler.kt │ │ │ └── SampleStyledProgressGenerator.kt │ └── res │ │ ├── drawable │ │ └── net_progress_bg.xml │ │ ├── layout │ │ └── net_progress_dialog.xml │ │ └── values │ │ ├── colors.xml │ │ └── ids.xml │ └── test │ └── java │ └── com │ └── dhy │ └── retrofitrxutil │ └── ExampleUnitTest.java └── 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/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/dictionaries 41 | .idea/libraries 42 | 43 | # Keystore files 44 | # Uncomment the following line if you do not want to check your keystore files in. 45 | #*.jks 46 | 47 | # External native build folder generated in Android Studio 2.2 and later 48 | .externalNativeBuild 49 | 50 | # Google Services (e.g. APIs or Firebase) 51 | google-services.json 52 | 53 | # Freeline 54 | freeline.py 55 | freeline/ 56 | freeline_project_description.json 57 | /.idea 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxNet [![](https://jitpack.io/v/DonaldDu/RxNet.svg)](https://jitpack.io/#DonaldDu/RxNet) [JitPack](https://jitpack.io/#DonaldDu/RxNet) 2 | # Android里面网络请求一般怎么写? 3 | 4 | ## Kotlin协程 5 | 很多人说用Kotlin协程,看了后发现不太好用。 6 | 7 | - 外面需要一个asyncUI {},看着都不爽 8 | - 如果接口出错了,需要在每个调用处,写逻辑来处理。不能全局处理错误,比如网络超时。 9 | - 代码不容易懂,比如: deferred1.wait(TOAST)这个wait(TOAST)是什么东西。 10 | - 没有全局进度框 11 | 12 | 以下代码引用于 [用 Kotlin 协程把网络请求玩出花来](https://www.jianshu.com/p/272430328b6e) 13 | ``` 14 | asyncUI { 15 | // 假设这是两个不同的 api 请求 16 | val deferred1 = bg { 17 | Server.getApiStore().login1("173176360", "123456").execute() 18 | } 19 | 20 | val deferred2 = bg { 21 | Server.getApiStore().login2("173176360", "123456").execute() 22 | } 23 | 24 | // 后台请求着 api,此时我还可以在 UI 协程中做我想做的事情 25 | textView.text = "loading" 26 | delay(5, TimeUnit.SECONDS) 27 | 28 | // 等 UI 协程中的事情做完了,专心等待 api 请求完成(其实 api 请求有可能已经完成了) 29 | // 通过提供 ExceptionHandleType 进行异常的过滤 30 | val response = deferred1.wait(TOAST) 31 | deferred2.wait(THROUGH) // deferred2 的结果我不关心 32 | 33 | // 此时两个请求肯定都完成了,并且 deferred1 没有异常发生 34 | textView.text = response.toString() 35 | } 36 | ``` 37 | ## 纯OkHttp 38 | - 代码写起来也比较麻烦,没有提示功能。 39 | - 不能全局处理错误,比如网络超时。 40 | - 没有全局进度框 41 | - 需要切换线程以操作UI 42 | ``` 43 | //代码同样来源上面引用的文章 44 | callback = { 45 | onSuccess = { res -> 46 | // TODO 47 | } 48 | 49 | onFail = { error -> 50 | // TODO 51 | } 52 | } 53 | request.execute(callback) 54 | ``` 55 | ## 封装OkHttp 56 | 还有人自己封装OkHttp,这个就看个人了。 57 | 58 | 我觉得这个不太好用,假如有个接口调用10次,就要写10次接口地址和‘toClass>()’,显然这样不好。 59 | 60 | - 代码有点冗余(服务地址和返回参对象类)。 61 | - ~~不能全局处理错误,比如网络超时~~(没用过,不太确定有没有)。 62 | - ~~没有全局进度框~~(没用过,不太确定有没有)。 63 | 64 | 以下代码引用于 [RxHttp 2000+star,协程请求,仅需三步](https://juejin.im/post/6856550856796897287?utm_source=gold_browser_extension) 65 | ``` 66 | val response = RxHttp.get("/service/...") 67 | .toClass>() 68 | .await() 69 | if (response.code == 200) { 70 | //拿到data字段(Student)刷新UI 71 | } else { 72 | //拿到msg字段给出错误提示 73 | } 74 | ``` 75 | # 干货 76 | 那么,有没有一种方法能 77 | - 全局默认进度框,同时支持自定义进度框。 78 | - 全局默认处理错误,同时支持自定义处理错误。 79 | - 多个依次请求也能友好支持。 80 | - 页面结束,自动取消请求。 81 | 82 | ## 有的:RxNet=RxJava3+Retrofit2+OkHttp3 83 | 84 | 下面就是调用示例。调用返回的对象是接口中申明的类型,有完整的代码提示,IDE中可以看到Hint 85 | ``` 86 | interface API { 87 | @GET("Simple?net=1&bz=1") 88 | fun simple(): Observable> 89 | 90 | @GET("NetError?net=0&bz=1") 91 | fun netError(): Observable> 92 | 93 | @GET("BzError?net=1&bz=0") 94 | fun bzError(): Observable> 95 | 96 | @GET("authorizeFailed") 97 | fun authorizeFailed(): Observable> 98 | } 99 | 100 | private fun subscribeX() { 101 | //简单模式,通常都用这个 102 | api.simple().subscribeX(context) { 103 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 104 | } 105 | //构造方法模式,看名字就知道功能了 106 | api.simple().subscribeXBuilder(context) 107 | .progress { 108 | null//null means no default and custom progress 109 | }.failed { 110 | true//return error handled or not 111 | }.successOnly(true) 112 | .response { 113 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 114 | } 115 | } 116 | ``` 117 | ## 多个请求依次发起 118 | 需要额外引用 119 | > implementation "com.github.DonaldDu:XIntent:1.5.3"//Waterfall 120 | 121 | ``` 122 | //多个请求依次发起,不调用 next 就自动结束流程。请求间切换时,进度框不会闪烁 123 | buttonMultReq.setOnClickListener { 124 | Waterfall.flow { 125 | apiSample.subscribeX(context) { 126 | Log.i("TAG", "apiSample1") 127 | next()//进入下一个flow,可以带任意类型的数据如:next("DATA") 128 | } 129 | }.flow { 130 | apiSample.subscribeX(context) { 131 | Log.i("TAG", "apiSample2") 132 | next() 133 | } 134 | }.flow { 135 | apiSample.subscribeX(context) { 136 | Log.i("TAG", "apiSample3") 137 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 138 | } 139 | } 140 | } 141 | ``` 142 | ## 延时返回结果 143 | 有时,想请求慢一点,动画需要时间展示。比如检查App更新,如果太快,屏幕会闪一下,然后显示没有更新的结果。如果延时900ms就可以解决这个问题(个人感觉好些)。 144 | ``` 145 | buttonDelay.setOnClickListener { 146 | val start = System.currentTimeMillis() 147 | apiSample.delayResponse(5000) 148 | .subscribeX(context) { 149 | val cost = System.currentTimeMillis() - start 150 | Log.i("TAG", "apiSample cost $cost") 151 | } 152 | } 153 | ``` 154 | ## 实现方式 155 | 为了支持默认进度框和全局错误处理需要实现两个类,StyledProgressGenerator 和 IErrorHandler。已经有了默认实现,需要根据实际需要作一些调整就行了。 156 | ``` 157 | class SampleStyledProgressGenerator : StyledProgressGenerator { 158 | override fun generate(observer: IObserverX): StyledProgress? { 159 | val context = observer.context 160 | return if (context is FragmentActivity) { 161 | MultListenerDialog.getInstance(context, observer) 162 | } else null 163 | } 164 | } 165 | 166 | class SampleErrorHandler : BaseErrorHandler() { 167 | override fun showDialog(context: Context, msg: String): Dialog? { 168 | return AlertDialog.Builder(context) 169 | .setMessage(msg) 170 | .setPositiveButton("OK", null).show() 171 | } 172 | 173 | override fun isAuthorizeFailed(activity: Activity, error: IError): Boolean { 174 | return error.code == 9001 175 | } 176 | 177 | override fun onLogout(context: Context) { 178 | val msg = "onLogout" 179 | Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() 180 | Log.i(TAG, msg) 181 | } 182 | 183 | override fun isDebug(): Boolean = true 184 | 185 | companion object { 186 | private val TAG = IErrorHandler::class.java.simpleName 187 | } 188 | } 189 | ``` 190 | ## 进度框样式调整 191 | 默认实现的是这个类MultListenerDialog,可以参考这个自己实现。如果只是调整比较小,也可以直接创建一个同名的Layout(R.layout.net_progress_dialog)自己随意调整就好。 192 | ## 手动控制进度框 193 | 如果在接口调用以外的地方需要控制进度框,可以调用以下两个方法。 194 | 195 | 比如先压缩图片,再上传。在启动压缩前最好显示进度框,上传完成后会自动关闭进度框。还可以实现压缩过程中取消后,就不上传了。 196 | 197 | >需要特别注意下:如果 Activity中也有同名的方法时,调用以下方法执行的是Activity中定义的,而不是下面的。 198 | 199 | ``` 200 | fun FragmentActivity.showProgress(): MultListenerDialog { 201 | val dialog = MultListenerDialog.getInstance(this) 202 | dialog.showProgress() 203 | return dialog 204 | } 205 | 206 | fun FragmentActivity.dismissProgress(delay: Boolean = true) { 207 | MultListenerDialog.getInstance(this).dismissProgress(delay) 208 | } 209 | ``` 210 | ## 引入依赖 [JitPack](https://jitpack.io/#DonaldDu/RxNet) 211 | ``` 212 | dependencies { 213 | implementation 'com.github.DonaldDu:RxNet:x.x.x'//JitPack version 214 | } 215 | ``` 216 | 217 | # 最后 218 | 开源不易,写文章更不易,劳烦大家给本文点个赞,可以的话,再给个star,感激不尽 219 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 28 7 | defaultConfig { 8 | applicationId "com.dhy.retrofitrxtest" 9 | minSdkVersion 15 10 | //noinspection OldTargetApi 11 | targetSdkVersion 28 12 | versionCode 1 13 | versionName "1.0" 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | compileOptions { 23 | sourceCompatibility = 1.8 24 | targetCompatibility = 1.8 25 | } 26 | packagingOptions { 27 | exclude 'META-INF/*.kotlin_module' 28 | } 29 | } 30 | 31 | dependencies { 32 | implementation fileTree(include: ['*.jar'], dir: 'libs') 33 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 34 | implementation 'androidx.appcompat:appcompat:1.2.0' 35 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1' 36 | 37 | implementation 'com.squareup.retrofit2:retrofit:2.9.0' 38 | implementation 'com.squareup.retrofit2:converter-gson:2.9.0' 39 | implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0' 40 | 41 | implementation "com.github.DonaldDu:XIntent:1.5.3" 42 | //noinspection GradleDependency 43 | implementation 'com.squareup.okhttp3:okhttp:3.14.7' 44 | testImplementation 'junit:junit:4.13' 45 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 47 | implementation project(':lib') 48 | } 49 | -------------------------------------------------------------------------------- /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/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/dhy/retrofitrxtest/API.kt: -------------------------------------------------------------------------------- 1 | package com.dhy.retrofitrxtest 2 | 3 | import io.reactivex.rxjava3.core.Observable 4 | import retrofit2.http.GET 5 | 6 | interface API { 7 | @GET("Simple?net=1&bz=1") 8 | fun simple(): Observable> 9 | 10 | @GET("NetError?net=0&bz=1") 11 | fun netError(): Observable> 12 | 13 | @GET("BzError?net=1&bz=0") 14 | fun bzError(): Observable> 15 | 16 | @GET("authorizeFailed") 17 | fun authorizeFailed(): Observable> 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/dhy/retrofitrxtest/App.kt: -------------------------------------------------------------------------------- 1 | package com.dhy.retrofitrxtest 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import com.dhy.retrofitrxutil.ObserverX 6 | import com.dhy.retrofitrxutil.sample.SampleErrorHandler 7 | import com.dhy.retrofitrxutil.sample.SampleStyledProgressGenerator 8 | 9 | class App : Application() { 10 | override fun onCreate() { 11 | super.onCreate() 12 | ObserverX.setProgressGenerator(SampleStyledProgressGenerator()) 13 | ObserverX.setErrorHandler(SampleErrorHandler()) 14 | } 15 | 16 | companion object { 17 | fun getInstance(context: Context): App { 18 | return context.applicationContext as App 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/dhy/retrofitrxtest/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.dhy.retrofitrxtest 2 | 3 | import android.content.Context 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.view.View 7 | import android.view.ViewGroup 8 | import android.widget.Toast 9 | import androidx.appcompat.app.AppCompatActivity 10 | import com.dhy.retrofitrxutil.delayResponse 11 | import com.dhy.retrofitrxutil.subscribeX 12 | import com.dhy.retrofitrxutil.subscribeXBuilder 13 | import com.dhy.xintent.Waterfall 14 | import kotlinx.android.synthetic.main.activity_main.* 15 | import okhttp3.OkHttpClient 16 | import retrofit2.Retrofit 17 | import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory 18 | import retrofit2.converter.gson.GsonConverterFactory 19 | 20 | 21 | class MainActivity : AppCompatActivity(), View.OnClickListener { 22 | private lateinit var context: Context 23 | private lateinit var api: API 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | setContentView(R.layout.activity_main) 27 | 28 | context = this 29 | initApi() 30 | val root = findViewById(R.id.root) 31 | for (i in 0 until root.childCount) { 32 | root.getChildAt(i).setOnClickListener(this) 33 | } 34 | val apiSample = api.simple() 35 | buttonMultReq.setOnClickListener { 36 | Waterfall.flow { 37 | apiSample.subscribeX(context) { 38 | Log.i("TAG", "apiSample1") 39 | next()//进入下一个flow,可以带任意类型的数据如:next("DATA") 40 | } 41 | }.flow { 42 | apiSample.subscribeX(context) { 43 | Log.i("TAG", "apiSample2") 44 | next() 45 | } 46 | }.flow { 47 | apiSample.subscribeX(context) { 48 | Log.i("TAG", "apiSample3") 49 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 50 | } 51 | } 52 | } 53 | buttonFinish.setOnClickListener { 54 | apiSample.subscribeX(context) { 55 | Log.i("TAG", "apiSample") 56 | } 57 | buttonFinish.postDelayed({ 58 | finish() 59 | }, 500) 60 | } 61 | 62 | buttonDelay.setOnClickListener { 63 | val start = System.currentTimeMillis() 64 | apiSample.delayResponse(5000) 65 | .subscribeX(context) { 66 | val cost = System.currentTimeMillis() - start 67 | Log.i("TAG", "apiSample cost $cost") 68 | } 69 | } 70 | } 71 | 72 | private fun initApi() { 73 | val okHttpClient = OkHttpClient.Builder().addInterceptor(MockDataApiInterceptor()).build() 74 | val retrofit = Retrofit.Builder() 75 | .client(okHttpClient) 76 | .addConverterFactory(GsonConverterFactory.create()) 77 | .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) 78 | .baseUrl("https://www.demo.com/") 79 | .build() 80 | api = retrofit.create(API::class.java) 81 | } 82 | 83 | @Suppress("unused") 84 | private fun subscribeX() { 85 | //简单模式,通常都用这个 86 | api.simple().subscribeX(context) { 87 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 88 | } 89 | //构造方法模式,看名字都基本能知道功能了 90 | api.simple().subscribeXBuilder(context) 91 | .progress { 92 | null//null means no default and custom progress 93 | }.failed { 94 | true//return error handled or not 95 | }.successOnly(true) 96 | .response { 97 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 98 | } 99 | } 100 | 101 | override fun onClick(v: View) { 102 | when (v.id) { 103 | R.id.buttonOK -> 104 | api.simple().subscribeX(context) { 105 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 106 | } 107 | 108 | R.id.buttonNetError -> 109 | api.netError().subscribeX(context) { 110 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 111 | } 112 | 113 | R.id.buttonBzError -> 114 | api.bzError().subscribeX(context) { 115 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 116 | } 117 | 118 | R.id.buttonAuthorizeFailed -> 119 | api.authorizeFailed().subscribeX(context) { 120 | Toast.makeText(context, "response:" + it.message, Toast.LENGTH_SHORT).show() 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/dhy/retrofitrxtest/MockDataApiInterceptor.kt: -------------------------------------------------------------------------------- 1 | package com.dhy.retrofitrxtest 2 | 3 | import okhttp3.* 4 | import java.io.IOException 5 | 6 | class MockDataApiInterceptor : Interceptor { 7 | companion object { 8 | const val allOk = "{\"result\":{\"code\":0,\"desc\":\"success\"}}" 9 | const val bzError = "{\"result\":{\"code\":1,\"desc\":\"bz error\"}}" 10 | const val authorizeFailed = "{\"result\":{\"code\":9001,\"desc\":\"登录失效,请重新登录\"},\"data\":null,\"validResult\":null}" 11 | } 12 | 13 | 14 | @Throws(IOException::class) 15 | override fun intercept(chain: Interceptor.Chain): Response { 16 | Thread.sleep(1500) 17 | val request = chain.request() 18 | val url = chain.request().url().toString() 19 | val net = url.contains("net=1") 20 | val bz = url.contains("bz=1") 21 | println("intercept: url=$url") 22 | return if (net && bz) { 23 | createResponse(request, 200, "ok", allOk) 24 | } else { 25 | if (net && !bz) { 26 | createResponse(request, 200, "ok", bzError) 27 | } else if (!net && bz) {//net error 28 | createResponse(request, 404, "page not found") 29 | } else {//authorizeFailed 30 | createResponse(request, 200, "ok", authorizeFailed) 31 | } 32 | } 33 | } 34 | 35 | private fun createResponse(request: Request, statusCode: Int, message: String, body: String? = null): Response { 36 | val builder = Response.Builder() 37 | .code(statusCode) 38 | .message(message) 39 | .request(request) 40 | .protocol(Protocol.HTTP_1_0) 41 | .addHeader("Content-Type", "application/json") 42 | .body(ResponseBody.create(MediaType.parse("application/json"), body ?: "")) 43 | return builder.build() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/dhy/retrofitrxtest/ResponsePacket.java: -------------------------------------------------------------------------------- 1 | package com.dhy.retrofitrxtest; 2 | 3 | import com.dhy.retrofitrxutil.IResponseStatus; 4 | import com.google.gson.annotations.SerializedName; 5 | 6 | import java.io.Serializable; 7 | import java.util.Map; 8 | 9 | public class ResponsePacket implements Serializable, IResponseStatus { 10 | public DATA data; 11 | 12 | public void setMessage(String message) { 13 | if (result == null) result = new Result(); 14 | result.message = message; 15 | result.error_code = LOCAL_ERROR; 16 | } 17 | 18 | @Override 19 | public boolean isSuccess() { 20 | return result != null && result.error_code == 0; 21 | } 22 | 23 | @Override 24 | public int getCode() { 25 | return result != null ? result.error_code : LOCAL_ERROR; 26 | } 27 | 28 | @Override 29 | public int httpCode() { 30 | return 0; 31 | } 32 | 33 | @Override 34 | public String getMessage() { 35 | return (result != null ? result.message : ""); 36 | } 37 | 38 | private Result result; 39 | 40 | private static class Result { 41 | @SerializedName("code") 42 | int error_code; 43 | @SerializedName("desc") 44 | String message; 45 | } 46 | 47 | public Map validResult; 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /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 |