You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit以object形式创建及AppModule继承BaseApi的技术疑问

我开发了一个使用Retrofit和Hilt的MVVM程序,有两个技术疑问:

  1. 为何多数示例中Retrofit以object形式而非class形式创建?
  2. 为何不应让AppModule继承BaseApi来限制直接访问Retrofit?

我习惯按此方式编写代码,但从未见过同类实现,希望了解专家对该写法存在问题的看法。

Retrofit类:

open class BaseApi protected constructor(){

private fun createBuilder(baseUrl : String): Retrofit {
    val okHttpClient = OkHttpClient.Builder()
        .connectTimeout(15, TimeUnit.SECONDS)
        .build()
    return Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create())
        .client(okHttpClient)
        .build()
}

protected fun getWeatherService(): WeatherService {
    val weatherServiceBaseAddress = "https://api.weatherapi.com/"
    return createBuilder(weatherServiceBaseAddress).create(WeatherService::class.java)
 }
}  

AppModule:

@Module
@InstallIn(SingletonComponent::class)
class AppModule : BaseApi() {

    @Singleton
    @Provides
    fun weatherServiceProvider() = getWeatherService()
}
问题解析与改进建议

1. 为何多数示例用object创建Retrofit而非class?

Retrofit实例是无状态的,它的核心作用是基于配置生成Service接口代理,一旦初始化完成就不需要修改。用object(单例)实现的核心原因:

  • 减少资源浪费:每次创建Retrofit都会初始化OkHttpClient、解析Converter等,重复创建会消耗额外内存和CPU,全局单例只需要初始化一次
  • 简化使用:无需手动管理实例生命周期,直接通过object的静态方式访问,不用考虑初始化时机
  • 符合设计逻辑:绝大多数场景下,App只需要一套Retrofit配置(统一的超时、拦截器、Converter),单例刚好匹配这种全局统一的需求

当然,如果你的App需要多套不同配置的Retrofit(比如不同baseUrl、不同认证逻辑),那用class创建并通过DI容器管理会更灵活,但这种场景相对少见,所以多数示例用object。

2. AppModule继承BaseApi的问题

你的写法虽然能运行,但从架构设计和Hilt的最佳实践来看,存在几个关键问题:

(1)违反单一职责原则

Hilt的@Module类的唯一职责是声明依赖提供规则,专注于告诉Hilt如何创建和注入对象。而BaseApi是负责构建Retrofit和Service的工具类,两者职责完全无关。强行继承会让AppModule的职责混乱,后续维护时很难区分哪些是Module的核心逻辑,哪些是继承来的Api构建逻辑。

(2)破坏Hilt的依赖管理逻辑

Hilt的Module实例是由框架自身管理的,让它继承工具类会把工具类的生命周期和Module绑定在一起:

  • 会提前触发BaseApi的初始化,哪怕暂时不需要创建WeatherService,不符合延迟加载的原则
  • 如果后续需要添加其他Api Service,要么往BaseApi里塞更多方法(让它变成大杂烩),要么尝试多继承(Kotlin不支持),扩展性极差

(3)封装性的实现方式不合理

你想通过protected方法限制直接访问Retrofit,其实更合理的方式是把BaseApi作为依赖注入到Module中,而不是继承。这样既分离了职责,又保证了封装性:

// 调整BaseApi为普通类,去掉protected构造
class BaseApi {
    private fun createBuilder(baseUrl: String): Retrofit {
        val okHttpClient = OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .build()
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)
            .build()
    }

    // 改为public方法,通过依赖注入提供访问
    fun getWeatherService(): WeatherService {
        val weatherServiceBaseAddress = "https://api.weatherapi.com/"
        return createBuilder(weatherServiceBaseAddress).create(WeatherService::class.java)
    }
}

@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    // 提供BaseApi实例
    @Singleton
    @Provides
    fun provideBaseApi(): BaseApi = BaseApi()

    // 通过BaseApi获取WeatherService
    @Singleton
    @Provides
    fun provideWeatherService(baseApi: BaseApi): WeatherService = baseApi.getWeatherService()
}

这种方式下,Retrofit依然不会被直接访问,同时Module和BaseApi各司其职,后续扩展其他Service也很方便——只需要在BaseApi里添加对应方法,再在Module里新增Provider即可。


内容的提问来源于stack exchange,提问作者Ali Doran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 17:39:39