Hello you all :man-raising-hand:, What do you thin...
# android
c
Hello you all 🙋‍♂️, What do you think about this approach? On an Android project with a modularization per feature architecture, would you create a retrofit instance per each feature? Isn't this very costly? My friends and I are currently working on this kind of architecture, and we are in the stage of integrate the REST API. We are trying to isolate as much as we can every feature. But should we create a retrofit instance on each feature module that needs to call a service or should we have another module let's say "data" and handle all the retrofit thing there? What would you do?
t
It does have a performance impact and also you probably want to share the same configuration settings. I would make a single instance that can be managed in the main module. Feature modules can define the retrofit services that they need. The single instance can create those services and provide it to feature modules that they can work with.
👍 1
c
Hello Michael! Thanks for the response, so what we are trying to do is something like this (image attached), I think it's something kind of like you say, but Im a little bit confused on the part that make the single instance and the feature modules can retrieve the services they need. Can you explain it a little bit more please?
t
Yeah this can be tricky sometimes. One way could be that something calls
createNetworkClient
and then manages that instance in the main app module. This might be from a scoped dagger module, Singleton, etc. Then when you build the dependencies for the feature module, you can use that instance to create the services needed for the code in the feature module. You would still have code isolated for modules, but the retrofit instance lives at a higher level module to help build the pieces. Does that help a litte more?
👍 1
c
There is a performance impact to using separate
Retrofit
instance per module, but honestly it probably isn’t noticeable or something to really stress yourself about. What’s more important is how you structure your modules to be clean and understandable. Generally, you’ll want to use one Retrofit instance for each “backend” you’re calling to. Many apps will only have one “backend”, and so you’d only need a single Retrofit instance. Sometimes you might have different backends, for example a normal API backend at https://api.foo.com, a secure backend for processing payments at https://secure.foo.com, and promotional content hosted in a public CMS at https://foo.com. In that example, each backend will likely have different needs in terms of authorization, headers, etc. and so you’d need to configure each Okhttp/Retrofit separately to account for it, but you can safely use that one instance for multiple modules. I’ve been working on an app for a couple years using this approach and it works very well. We use Dagger to inject these Retrofit instances, but that’s not necessary. Here’s our general approach:
Copy code
// create a base OkHttp client with settings shared by all backends. Is a factory (not a Singleton)
fun createOkHttpBuilder(): OkHttpClient.Builder 

// create a base Retrofit client with settings shared by all backends
fun createRetrofitBuilder(okHttpClient: OkHttpClient): Retrofit.Builder. Is a factory (not a Singleton)

// Create shared Retrofit instances shared for all APIs at a single backend. Can be a Singleton, but not necessary. Tradeoff of memory vs startup speed (both of which are likely negligible. My team prefers to keep it a factory, not a Singleton)
fun createApiRetrofit(): Retrofit {
    val okHttp = createOkHttpBuilder()
        .let {
            // API-specific OkHttp configuration
        }
        .build()
    val retrofit = createRetrofitBuilder(okHttp)
        .let {
            // API-specific Retrofit configuration
        }
        .build()
    return retrofit
}

fun createSecureRetrofit(): Retrofit {
    val okHttp = createOkHttpBuilder()
        .let {
            // secure-specific OkHttp configuration
        }
        .build()
    val retrofit = createRetrofitBuilder(okHttp)
        .let {
            // secure-specific Retrofit configuration
        }
        .build()
    return retrofit
}

fun createCmsRetrofit(): Retrofit {
    val okHttp = createOkHttpBuilder()
        .let {
            // secure-specific OkHttp configuration
        }
        .build()
    val retrofit = createRetrofitBuilder(okHttp)
        .let {
            // secure-specific Retrofit configuration
        }
        .build()
    return retrofit
}

fun createLoginApi(): LoginApi {
    return createApiRetrofit().create(LoginApi::class.java);
}

fun createPaymentsApi(): PaymentsApi {
    return createSecureRetrofit().create(PaymentsApi::class.java);
}

fun createBlogApi(): BlogApi {
    return createCmsRetrofit().create(BlogApi::class.java);
}
👍 1
i
@Christopher ElĂ­as I solved something similar recently, I had a separate module for DI where I declared all my dependencies and then I added this DI module to all my feature modules via their
build.gradle
👍 1
this project is a good place to start https://github.com/odaridavid/Clean-MVVM-ArchComponents
c
Thank you all guys! @torresmi & @Casey Brooks , I think I got your points, so I think what I have to do is to provide the retrofit builder on a higher level module, probably make it a singleton as well, and inject those builder instances on the feature modules, probably something like this? app/
Copy code
@Module
object RetrofitModule {

    @Provides
    @Singleton
    internal fun provideRetrofitBuilder(): Retrofit =
        createNetworkClient(BuildConfig.BASE_URL, BuildConfig.DEBUG)
            .build()
}
features/login(android library)
Copy code
@Module
object LoginServiceModule {

    @Provides
    @PerFeatureScope
    internal fun provideLoginService(retrofit: Retrofit): LoginService =
        retrofit.create(LoginService::class.java)
}
I have a more clear concept now that @Casey Brooks explain that he uses a different retrofit instance if he have different BackEnds, that is not may case on this project, but what Im trying to do is that each feature module (Android Library) can have it's own service for more isolation. So in the above example I just remove the creation of the retrofit builder, probably this is a lil bit better, what do you guys think or I misunderstood your words?
👍 1
c
Yup, I think you’re on the right track with that!
c
Hello @Isaac I check the project structure of your repo, It seems like you are taking something like a "clean architecture" modularization (data, domain, presentation) but that is not the case here. We use to have that kind of approach on our older projects but now we change it to a modularization per feature architecture, as it seems to be better with the build times, isolation, you can reuse core modules only when it's need, and If a team have to make a change, that change will be made only on the feature it's belong, so our build times are also faster, due to it doesn't have to compile everythin again! 🙋‍♂️