GithubLink as an aside: physical tools cost money, but software tools are free! Give a star to encourage!

The status quo

What about Android projects with multi-server interfaces?

Method 1: Put the server address in the Header

This is done by placing the server address (or address name, such as’ SERVER_HOST_A ‘) in the interface Header and dynamically changing the request address using an interceptor. It’s a bit cumbersome to add a Header to everything except the default server interface. It doesn’t look good. It’s not neat.

interface ApiHeaderCase {
    /************************** server A ****************************/
    @Headers("host:$SERVER_HOST_A")
    @GET("user/loginWithScanCode")
    fun aMethod1(@Query("id") id: Int): Observable<ResponseBody>

    /************************** server B ****************************/
    @Headers("host:$SERVER_HOST_B")
    @GET("user/loginWithScanCode")
    fun bMethod1(@Query("id") id: Int): Observable<ResponseBody>
}
Copy the code

Method 2: Multiple sets of service classes, instantiate into multiple objects, and accurately find the service to which the interface belongs

Define multiple classes, each defining a set of service interfaces. They are then instantiated into multiple objects, and the exact object is used to invoke the interface. This method is the most efficient, but during development, it may not be able to quickly know which service the interface belongs to. You need to look at the code to know exactly, so it can be said that the code prompt ability is less.

interface ApiA {
    @GET("user/loginWithScanCode")
    fun methodA(@Query("id") id: Int): Observable<ResponseBody>
}

interface ApiB {
    @GET("user/loginWithScanCode")
    fun methodB(@Query("id") id: Int): Observable<ResponseBody>
}
Copy the code

Method 3: Write them all together, instantiate them into multiple objects, and call methods exactly

Write all the interfaces in one class and instantiate them into multiple objects based on the service address. To ensure accurate method invocation, we can prefix each interface with the service name to reduce the problem of method tuning.

interface ApiAllInOne { /************************** server A ****************************/ @GET("user/loginWithScanCode") fun aMethod1(@Query("id") id: Int): Observable<ResponseBody> /************************** server B ****************************/ @GET("user/loginWithScanCode") fun bMethod1(@Query("id") id: Int): Observable<ResponseBody> } const val SERVER_HOST_A = "https://www.a.com/" const val SERVER_HOST_B = "https://www.b.com/"  fun getApi(retrofit: Retrofit, host: String): ApiAllInOne { return retrofit.newBuilder() .baseUrl(host).build() .create(ApiAllInOne::class.java) } fun showNomalUseCase(retrofit: Retrofit) { val apiA = getApi(retrofit, SERVER_HOST_A)//save as single instance for repeated usage apiA.aMethod1(1).subscribe() apiA.bMethod1(1).subscribe()//invalid usage, but no compile error val apiB = getApi(retrofit, SERVER_HOST_B) apiB.bMethod1(1).subscribe() apiB.aMethod1(1).subscribe()//invalid usage, but no compile error }Copy the code

Is there an easier way?

B: Sure, and it’s super convenient!

Defines the interface

Define all interfaces in one KT file for easy lookup and maintenance.

    interface ApiHolder : ApiA, ApiB

    @BaseUrl("https://www.a.com/")
    interface ApiA {
        @GET("user/loginWithScanCode")
        fun methodA(@Query("id") id: Int): Observable<ResponseBody>
    }

    @BaseUrl("https://www.b.com/")
    interface ApiB {
        @GET("user/loginWithScanCode")
        fun methodB(@Query("id") id: Int): Observable<ResponseBody>
    }
Copy the code

Build tools

Generally need a tool class, easy to configure interceptors and so on. If there is no custom requirement, it can be instantiated directly.

You can override the invokeApi method to globally assign threads to each Observable.

class ApiUtil : ApiHolderUtil<ApiHolder>(ApiHolder::class) { companion object { val apiUtil = ApiUtil() val api = apiUtil.api } override  fun invokeApi(api: Any, method: Method, args: Array<*>?) : Any { val observable = super.invokeApi(api, method, args) as Observable<*> return observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) } }Copy the code

Dynamically update the service address

You can also dynamically update service addresses, such as switching between test services and formal services.

    //update api baseUrl when needed
    apiUtil.updateApi(ApiA::class, "https://www.a2.com/")
Copy the code

Call interface

    api.methodA(1).subscribe()
    api.methodB(1).subscribe()
Copy the code

Introduction of depend on

dependencies {
    implementation 'com.github.DonaldDu:ApiHolder:x.x.x'//JitPack version
}
Copy the code

The tripartite library used in this project

  • OkHttp3
  • Retrofit2
  • Rxjava3 (can be changed to rxjavA2)
API 'com. Squareup. Okhttp3: okhttp: 4.7.2' API "com. Squareup. Retrofit2: retrofit: 2.9.0" API "Com. Squareup. Retrofit2: converter - gson: 2.9.0" API "com. Squareup. Retrofit2: adapter - rxjava3:2.9.0" API 'the IO. Reactivex. Rxjava3: rxandroid: 3.0.0'Copy the code

Other instructions

rxjava3 ->rxjava2

You can adjust it to RXjavA2 as needed, and it is recommended to use the latest version.

/ / rewrite ApiHolderUtil method, RxJava3CallAdapterFactory - > RxJava2CallAdapterFactory can. protected open fun getRetrofit(client: OkHttpClient): Retrofit { return Retrofit.Builder() .validateEagerly(validateEagerly) .addConverterFactory(getGsonConverterFactory()) .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .baseUrl("http://www.demo.com/") .client(client) .build() }Copy the code

Timeout

You can set different timeouts for each set of services

@BaseUrl("https://www.b.com/")
@Timeout(read = 100, timeUnit = TimeUnit.SECONDS)
interface ApiB {
    @GET("user/loginWithScanCode")
    fun methodB(@Query("id") id: Int): Observable<ResponseBody>
}
Copy the code

My open source project

  • RxNet does network requests. It’s incredibly simple
  • DynamicServer dynamic configuration service

The last

Open source is not easy, writing articles is not easy, please give this article point like, if you can, and give a star, grateful