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

多模块Android+Kotlin+Hilt项目:模块无法访问Application类

多模块Android应用集成Hilt:全局访问Application/配置类的解决方案

问题概述

基于Kotlin开发多模块Android应用,通过模块拆分实现关注点分离,集成Hilt做依赖解耦时,遇到模块无法访问Application类的问题:

  • 原有的AppSettings类整合了应用配置常量(如颜色)、Context、单例SQLite实例,此前用静态+WeakReference方式访问,现在要转用DI实现解耦和可测试性
  • 纠结@HiltAndroidApp标注的Application类放在哪个模块,以及如何让配置类在所有模块全局可用

项目结构与模块依赖

项目结构

project
|- App
|- Common
|- Core
|- DTO
|- Repository

模块可见性关系

Project
|
|- App
|   |- Common
|   |- DTO
|   |- Core
|
|- Common
|   |- DTO
| 
|- Core
|   |- Common
| 
|- DTO
|
|- Repository
|   |- Common
|   |- DTO

核心矛盾分析

  1. Application类的位置限制:@HiltAndroidApp注解必须放在App模块,因为Hilt会基于这个类生成主组件代码,其他模块不能放置该注解类,否则Hilt无法正常工作。
  2. 配置与Application的耦合:如果把包含配置常量的AppSettings作为Application类放在App模块,依赖Common的模块无法访问它;如果放在Common模块,违反Hilt的Application类位置规则。
  3. 错误的实例创建方式:手动new AppSettings()获取实例是错误的,Application由系统初始化,手动创建会导致Context无效、非单例等问题。

解决方案

1. 拆分职责,解耦配置与Application

把AppSettings的职责拆分为两部分:

  • 配置常量类:在Common模块创建AppConfig,仅存放静态配置值,让所有依赖Common的模块直接访问:
    // Common模块
    object AppConfig {
        const val ACTION_BAR_TITLE_COLOR = "#0D0D0D"
        // 其他配置常量...
    }
    
  • Application类:在App模块创建MyApp,仅负责Hilt初始化和提供Application级资源:
    // App模块
    @HiltAndroidApp
    class MyApp : MultiDexApplication() {
        // 这里不需要放配置常量,仅保留Application生命周期相关逻辑
    }
    

2. 通过Hilt提供全局可访问的Application级资源

在App模块创建Hilt模块,提供Context、SQLite等需要Application级别的资源,所有模块通过Hilt注入获取:

// App模块
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    // 提供Application Context
    @Provides
    @Singleton
    fun provideAppContext(@ApplicationContext context: Context): Context {
        return context
    }

    // 提供单例SQLite实例
    @Provides
    @Singleton
    fun provideSqliteDatabase(context: Context): SQLiteDatabase {
        return SQLiteDatabase.openOrCreateDatabase(
            context.filesDir.path + "/app.db",
            null
        )
    }
}

3. 用接口统一封装资源(可选)

如果需要一个统一的类封装配置、Context、数据库,可以在Common模块定义接口,App模块实现后通过Hilt绑定:

// Common模块:定义接口
interface AppResources {
    val actionBarTitleColor: String
    val appContext: Context
    val sqliteDatabase: SQLiteDatabase
}

// App模块:实现接口
class AppResourcesImpl @Inject constructor(
    @ApplicationContext private val context: Context,
    private val sqliteDatabase: SQLiteDatabase
) : AppResources {
    override val actionBarTitleColor: String = AppConfig.ACTION_BAR_TITLE_COLOR
    override val appContext: Context = context
    override val sqliteDatabase: SQLiteDatabase = sqliteDatabase
}

// App模块:绑定接口与实现
@Module
@InstallIn(SingletonComponent::class)
abstract class AppBindingModule {
    @Binds
    @Singleton
    abstract fun bindAppResources(impl: AppResourcesImpl): AppResources
}

4. 正确注入依赖

在任意模块的类中,通过构造注入获取所需资源:

// Common模块的TestClass
class TestClass @Inject constructor(
    private val appResources: AppResources
) {
    fun whatever() {
        val color = appResources.actionBarTitleColor
        val db = appResources.sqliteDatabase
    }
}

注意:Android组件(Activity/Fragment)需要添加@AndroidEntryPoint注解才能使用Hilt注入;非Android类可以通过Hilt的EntryPoint获取实例,或者在被注入的类中通过构造注入传递。

关键注意事项

  • 禁止手动创建Application实例:Application由系统初始化,手动new会导致Context无效、内存泄漏等问题。
  • 遵循Hilt模块规则:@HiltAndroidApp必须在App模块,其他模块的Hilt模块通过@InstallIn指定合适的组件(如SingletonComponent)实现全局可用。
  • 依赖倒置:让底层模块依赖抽象接口,而非具体实现,避免模块间的强耦合。

内容的提问来源于stack exchange,提问作者Diego Perez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 19:09:33