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