使用Hilt实现组件化的基本内容
1. Hilt 核心注解说明 📝
graph TD
A[HiltAndroidApp] --> B[应用级注解]
C[AndroidEntryPoint] --> D[组件级注解]
E[Module] --> F[模块级注解]
G[Inject] --> H[注入点注解]
I[Provides] --> J[提供者注解]
K[Singleton] --> L[作用域注解]
2. 注解详细说明 📌
// 1. @HiltAndroidApp
// 作用:应用程序入口,初始化Hilt
@HiltAndroidApp
class MyApplication : Application()
// 2. @AndroidEntryPoint
// 作用:标记可以接收依赖注入的Android组件
@AndroidEntryPoint
class MainActivity : AppCompatActivity()
// 3. @Module
// 作用:定义依赖提供者模块
@Module
@InstallIn(SingletonComponent::class)
object AppModule
// 4. @InstallIn
// 作用:指定模块安装的组件范围
@InstallIn(SingletonComponent::class) // 应用级别
@InstallIn(ActivityComponent::class) // Activity级别
@InstallIn(FragmentComponent::class) // Fragment级别
// 5. @Inject
// 作用:标记注入点或构造函数
class Repository @Inject constructor()
// 6. @Provides
// 作用:在模块中提供依赖实例
@Provides
fun provideDatabase(): Database
// 7. @Singleton
// 作用:标记单例作用域
@Singleton
class UserRepository
// 8. @HiltViewModel
// 作用:标记ViewModel类
@HiltViewModel
class MainViewModel @Inject constructor()
3. Hilt 组件层次 🏗️
graph TD
A[SingletonComponent] --> B[ActivityRetainedComponent]
B --> C[ActivityComponent]
C --> D[FragmentComponent]
C --> E[ViewComponent]
D --> F[ViewWithFragmentComponent]
style A fill:#f9f,stroke:#333
style B fill:#bbf,stroke:#333
style C fill:#bfb,stroke:#333
style D fill:#fbf,stroke:#333
4. 作用域注解对应关系 🎯
// 组件与作用域对应关系
SingletonComponent::class -> @Singleton
ActivityRetainedComponent::class -> @ActivityRetainedScoped
ActivityComponent::class -> @ActivityScoped
FragmentComponent::class -> @FragmentScoped
ViewComponent::class -> @ViewScoped
ViewWithFragmentComponent::class -> @ViewScoped
ServiceComponent::class -> @ServiceScoped
5. 完整使用流程 🔄
sequenceDiagram
participant App as Application
participant Module as Hilt Module
participant Component as Android Component
participant ViewModel as ViewModel
App->>App: @HiltAndroidApp
App->>Module: @Module @InstallIn
Module->>Module: @Provides/@Singleton
Component->>Component: @AndroidEntryPoint
Component->>ViewModel: @HiltViewModel
ViewModel->>Module: @Inject
6. 实际使用示例 📱
// 1. 应用入口
@HiltAndroidApp
class MyApplication : Application()
// 2. 定义模块
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
// 提供网络客户端
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().build()
}
// 提供Retrofit实例
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://api.example.com")
.build()
}
}
// 3. 使用依赖注入
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
// 注入ViewModel
private val viewModel: MainViewModel by viewModels()
// 注入其他依赖
@Inject
lateinit var repository: UserRepository
}
// 4. ViewModel注入
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: UserRepository,
@ApplicationContext private val context: Context
) : ViewModel()
7. 注解功能总结表 📊
| 注解 | 作用 | 使用位置 |
|---|---|---|
| @HiltAndroidApp | 初始化Hilt | Application类 |
| @AndroidEntryPoint | 启用依赖注入 | Android组件类 |
| @Module | 定义依赖提供者 | 模块类 |
| @InstallIn | 指定组件范围 | 模块类 |
| @Inject | 标记注入点 | 构造函数/字段 |
| @Provides | 提供依赖实例 | 模块方法 |
| @Singleton | 单例作用域 | 类/提供方法 |
| @HiltViewModel | ViewModel注入 | ViewModel类 |
8. 使用流程总结 🌟
- 初始化阶段
graph LR
A[添加Hilt依赖] --> B[配置Application]
B --> C[创建Module]
C --> D[定义组件]
- 注入阶段
graph LR
A[标记注入点] --> B[提供依赖]
B --> C[使用依赖]
C --> D[管理生命周期]
- 使用建议
- 合理规划模块结构
- 注意作用域范围
- 避免循环依赖
- 保持接口简洁
- 合理使用限定符
Hilt 通过这些注解系统,实现了一个完整的依赖注入框架,大大简化了Android应用的依赖管理。
Hilt在Android组件化中的完整注解和依赖流程
graph TD
A[Application Module<br>'dagger.hilt.android.plugin'] --> B[Hilt Application<br>HiltAndroidApp]
B --> C{Feature Modules<br>各功能模块}
C --> D[Login Module<br>Module<br>InstallIn]
C --> E[Home Module<br>Module<br>InstallIn]
C --> F[Profile Module<br>Module<br>InstallIn]
D --> G[LoginComponent<br>Module<br>InstallIn<br>Provides]
E --> H[HomeComponent<br>Module<br>InstallIn<br>Provides]
F --> I[ProfileComponent<br>Module<br>InstallIn<br>Provides]
G --> J[LoginRepository<br>Inject<br>Singleton]
G --> K[LoginViewModel<br>HiltViewModel<br>Inject]
H --> L[HomeRepository<br>Inject<br>Singleton]
H --> M[HomeViewModel<br>HiltViewModel<br>Inject]
J --> N[Activity/Fragment<br>AndroidEntryPoint]
K --> N
L --> O[Activity/Fragment<br>AndroidEntryPoint]
M --> O
这个流程图展示了:
- 从顶层Application到底层使用处的完整依赖链
- 每个组件使用的关键注解
- 模块间的依赖关系
- 组件的层级结构
主要层级和注解:
- 应用层:
@HiltAndroidApp - 模块层:
@Module,@InstallIn - 组件层:
@Provides,@Singleton - 实现层:
@Inject,@HiltViewModel - 使用层:
@AndroidEntryPoint
让我详细解释这个图中每个组件对应的注解和含义:
1. Application层:
// A[Application Module]
// 主应用模块,包含基础配置
build.gradle:
plugins {
id 'dagger.hilt.android.plugin'
}
// B[Hilt Application]
@HiltAndroidApp // 标记为Hilt应用入口
class MyApplication : Application() {
// 初始化Hilt的应用程序类
}
2. Feature Modules层:
// C{Feature Modules}
// 各个功能模块的容器,无具体注解
// 通常在build.gradle中配置:
plugins {
id 'dagger.hilt.android.plugin'
}
// D[Login Module], E[Home Module], F[Profile Module]
// 各个功能模块,通常包含:
@InstallIn(SingletonComponent::class) // 安装到单例组件
@Module // 标记为Hilt模块
object FeatureModule {
// 模块配置
}
3. Component层:
// G[LoginComponent], H[HomeComponent], I[ProfileComponent]
// 各个功能的组件定义
// 例如LoginComponent:
@InstallIn(ActivityComponent::class) // 安装到Activity作用域
@Module
object LoginComponent {
@Provides
fun provideLoginRepository(): LoginRepository {
// 提供Repository实例
}
@Provides
fun provideLoginViewModel(): LoginViewModel {
// 提供ViewModel实例
}
}
4. Repository和ViewModel层:
// J[LoginRepository]
interface LoginRepository {
// 接口定义
}
@Singleton // 标记为单例
class LoginRepositoryImpl @Inject constructor(
// 构造注入
) : LoginRepository
// K[LoginViewModel]
@HiltViewModel // 标记为Hilt管理的ViewModel
class LoginViewModel @Inject constructor(
private val repository: LoginRepository
) : ViewModel()
// L[HomeRepository], M[HomeViewModel]同理
5. 使用处的注解:
// 在Activity中使用
@AndroidEntryPoint // 标记为注入点
class LoginActivity : AppCompatActivity() {
@Inject
lateinit var repository: LoginRepository // 字段注入
private val viewModel: LoginViewModel by viewModels() // ViewModel注入
}
完整的依赖关系:
// 1. 应用层
@HiltAndroidApp
↓
// 2. 模块层
@Module + @InstallIn(SingletonComponent::class)
↓
// 3. 组件层
@Module + @InstallIn(ActivityComponent::class)
↓
// 4. 实现层
@Inject(构造注入)
@Singleton(单例标记)
@HiltViewModel(ViewModel注入)
↓
// 5. 使用层
@AndroidEntryPoint(注入点)
@Inject(字段注入)
常见作用域注解:
@Singleton // 应用级单例
@ActivityScoped // Activity级作用域
@FragmentScoped // Fragment级作用域
@ViewModelScoped // ViewModel级作用域
@ServiceScoped // Service级作用域
这样的注解体系确保了:
- 依赖的正确注入
- 作用域的合理控制
- 组件的生命周期管理
- 模块间的解耦
- 测试的便利性
@Provider和@inject的区别
@Provides 的主要作用是告诉Hilt如何创建一个依赖实例。让我详细解释:
1. 基本用法:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
// @Provides 告诉Hilt如何创建Repository实例
@Provides
fun provideRepository(): Repository {
return RepositoryImpl()
}
}
2. 创建第三方库实例:
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
// 对于不能修改源码的第三方库,使用@Provides创建实例
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(LoggingInterceptor())
.build()
}
}
3. 依赖其他提供的实例:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
// Retrofit依赖OkHttpClient
@Provides
fun provideRetrofit(
okHttpClient: OkHttpClient // 自动注入之前提供的OkHttpClient
): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.build()
}
// Api服务依赖Retrofit
@Provides
fun provideApiService(
retrofit: Retrofit // 自动注入之前提供的Retrofit
): ApiService {
return retrofit.create(ApiService::class.java)
}
}
4. @Provides vs @Inject:
// 方式1:使用@Provides
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideRepository(): Repository {
return RepositoryImpl()
}
}
// 方式2:使用@Inject构造函数
class RepositoryImpl @Inject constructor() : Repository {
// 实现...
}
// 区别:
// 1. @Provides适用于无法修改源码的类(如第三方库)
// 2. @Inject适用于自己的类,更简洁
5. 提供多个实例:
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
@Provides
@Named("memory") // 限定符
fun provideMemoryDatabase(): Database {
return MemoryDatabase()
}
@Provides
@Named("disk") // 限定符
fun provideDiskDatabase(): Database {
return DiskDatabase()
}
}
// 使用时
class Repository @Inject constructor(
@Named("memory") private val memoryDb: Database,
@Named("disk") private val diskDb: Database
)
6. 条件提供:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideDatabase(
@ApplicationContext context: Context
): Database {
return if (BuildConfig.DEBUG) {
// 调试版本使用内存数据库
MemoryDatabase()
} else {
// 正式版本使用磁盘数据库
DiskDatabase(context)
}
}
}
7. 作用域控制:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
// 单例作用域
@Provides
@Singleton
fun provideSingletonRepository(): Repository {
return RepositoryImpl()
}
// 默认作用域(每次注入都创建新实例)
@Provides
fun provideRepository(): Repository {
return RepositoryImpl()
}
}
- 实际应用场景:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
// 1. 网络相关
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.build()
}
// 2. 数据库相关
@Provides
@Singleton
fun provideDatabase(
@ApplicationContext context: Context
): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"app.db"
).build()
}
// 3. 工具类
@Provides
@Singleton
fun provideSharedPreferences(
@ApplicationContext context: Context
): SharedPreferences {
return context.getSharedPreferences(
"app_prefs",
Context.MODE_PRIVATE
)
}
}
总结 @Provides 的主要用途:
- 创建第三方库实例
- 配置复杂对象
- 提供接口实现
- 条件创建实例
- 控制实例作用域
- 管理依赖关系
使用建议:
- 优先使用
@Inject构造函数 - 第三方库使用
@Provides - 需要特殊配置时使用
@Provides - 合理使用作用域注解
- 注意依赖的顺序
使用Hilt来配置Android项目的组件化
1. 项目结构:
app/
├─ base/ // 基础库
├─ common/ // 公共库
├─ feature_a/ // 功能模块A
├─ feature_b/ // 功能模块B
└─ feature_c/ // 功能模块C
2. 基础配置:
Application配置:
// app模块
@HiltAndroidApp
class MyApplication : Application()
// build.gradle
plugins {
id 'dagger.hilt.android.plugin'
id 'kotlin-kapt'
}
3. 各模块依赖注入配置:
3.1基础模块(base):
// BaseModule.kt
@Module
@InstallIn(SingletonComponent::class)
object BaseModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"app_database"
).build()
}
@Provides
@Singleton
fun provideNetworkClient(): NetworkClient {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
3.2公共模块(common):
// CommonModule.kt
@Module
@InstallIn(SingletonComponent::class)
object CommonModule {
@Provides
@Singleton
fun provideImageLoader(): ImageLoader {
return GlideImageLoader()
}
@Provides
@Singleton
fun provideLogger(): Logger {
return TimberLogger()
}
}
4. 功能模块配置:
功能模块A:
// FeatureAModule.kt
@Module
@InstallIn(ViewModelComponent::class)
object FeatureAModule {
@Provides
fun provideFeatureARepository(
database: AppDatabase,
networkClient: NetworkClient
): FeatureARepository {
return FeatureARepositoryImpl(database, networkClient)
}
}
// FeatureAViewModel.kt
@HiltViewModel
class FeatureAViewModel @Inject constructor(
private val repository: FeatureARepository,
private val imageLoader: ImageLoader
) : ViewModel()
- 组件间通信:
使用EventBus:
@Module
@InstallIn(SingletonComponent::class)
object EventModule {
@Provides
@Singleton
fun provideEventBus(): EventBus {
return EventBus.getDefault()
}
}
- 界面注入:
Activity注入:
@AndroidEntryPoint
class FeatureAActivity : AppCompatActivity() {
@Inject
lateinit var imageLoader: ImageLoader
private val viewModel: FeatureAViewModel by viewModels()
}
Fragment注入:
@AndroidEntryPoint
class FeatureAFragment : Fragment() {
@Inject
lateinit var logger: Logger
private val viewModel: FeatureAViewModel by viewModels()
}
- 组件化路由:
@Module
@InstallIn(SingletonComponent::class)
object RouterModule {
@Provides
@Singleton
fun provideRouter(): Router {
return Router.Builder()
.addFeatureA()
.addFeatureB()
.addFeatureC()
.build()
}
}
// 使用路由
class Navigator @Inject constructor(
private val router: Router
) {
fun navigateToFeatureA() {
router.navigate(Route.FEATURE_A)
}
}
- 测试配置:
// 测试模块
@Module
@InstallIn(SingletonComponent::class)
object TestModule {
@Provides
@Singleton
fun provideMockNetworkClient(): NetworkClient {
return MockNetworkClient()
}
}
// 测试用例
@HiltAndroidTest
class FeatureATest {
@get:Rule
val hiltRule = HiltAndroidRule(this)
@Inject
lateinit var repository: FeatureARepository
@Test
fun testFeatureA() {
// 测试代码
}
}
- 完整示例:
Application:
@HiltAndroidApp
class MyApplication : Application() {
@Inject
lateinit var router: Router
@Inject
lateinit var logger: Logger
override fun onCreate() {
super.onCreate()
initializeComponents()
}
private fun initializeComponents() {
router.init()
logger.init()
}
}
ViewModel:
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: MainRepository,
private val eventBus: EventBus,
private val navigator: Navigator
) : ViewModel() {
fun onFeatureAClick() {
navigator.navigateToFeatureA()
}
}
Repository:
class MainRepositoryImpl @Inject constructor(
private val database: AppDatabase,
private val networkClient: NetworkClient,
private val logger: Logger
) : MainRepository {
// 实现代码
}
- 最佳实践:
模块间依赖:
// build.gradle
dependencies {
implementation project(':base')
implementation project(':common')
// 按需加载功能模块
implementation project(':feature_a')
implementation project(':feature_b')
}
使用Hilt实现组件化的关键点是:
- 合理划分模块
- 正确配置依赖注入
- 实现模块间通信
- 管理组件生命周期
- 便于测试和维护
Hilt框架中Module之间的依赖关系如何设置的
在Hilt中,主要通过 @InstallIn 和模块间的依赖注入来体现模块互相依赖关系。让我详细解释:
1. 基础依赖关系:
// 1. 在common模块定义接口
interface LoginRepository {
suspend fun login(username: String, password: String): Result
}
// 2. 在login模块实现
@Module
@InstallIn(SingletonComponent::class) // 安装到单例组件,其他模块可以使用
object LoginModule {
@Provides
@Singleton
fun provideLoginRepository(): LoginRepository {
return LoginRepositoryImpl()
}
}
// 3. 在其他模块使用
@HiltViewModel
class HomeViewModel @Inject constructor(
private val loginRepository: LoginRepository // 注入login模块的依赖
) : ViewModel()
2. 使用 @Binds 实现依赖:
// 更推荐的方式是使用 @Binds
@Module
@InstallIn(SingletonComponent::class)
interface LoginModule {
@Binds
@Singleton
fun bindLoginRepository(
impl: LoginRepositoryImpl
): LoginRepository
}
3. 多模块依赖:
// 1. 定义依赖模块
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit = // ...
}
// 2. 使用其他模块的依赖
@Module
@InstallIn(SingletonComponent::class)
object LoginModule {
@Provides
fun provideLoginApi(
retrofit: Retrofit // 注入NetworkModule提供的依赖
): LoginApi = retrofit.create(LoginApi::class.java)
}
4. 包含其他模块:
// 使用 includes 属性包含其他模块
@Module(
includes = [
NetworkModule::class,
DatabaseModule::class
]
)
@InstallIn(SingletonComponent::class)
object AppModule {
// ...
}
5. 作用域依赖:
// 1. SingletonComponent(应用级别)
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(): AppDatabase = // ...
}
// 2. ViewModelComponent(ViewModel级别)
@Module
@InstallIn(ViewModelComponent::class)
object ViewModelModule {
@Provides
fun provideRepository(
database: AppDatabase // 可以注入SingletonComponent的依赖
): Repository = // ...
}
6. 组件依赖层级:
SingletonComponent(顶层)
↓
ActivityRetainedComponent
↓
ActivityComponent
↓
FragmentComponent
↓
ViewComponent
↓
ViewWithFragmentComponent
7. 限定符区分依赖:
// 1. 定义限定符
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class OtherInterceptorOkHttpClient
// 2. 提供不同的实现
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@AuthInterceptorOkHttpClient
@Provides
@Singleton
fun provideAuthOkHttpClient(): OkHttpClient = // ...
@OtherInterceptorOkHttpClient
@Provides
@Singleton
fun provideOtherOkHttpClient(): OkHttpClient = // ...
}
// 3. 在其他模块使用
@Module
@InstallIn(SingletonComponent::class)
object LoginModule {
@Provides
fun provideLoginApi(
@AuthInterceptorOkHttpClient okHttpClient: OkHttpClient
): LoginApi = // ...
}
8. 测试替换依赖:
@Module
@InstallIn(SingletonComponent::class)
object TestModule {
@Provides
@Singleton
fun provideLoginRepository(): LoginRepository {
return FakeLoginRepository() // 测试时替换实现
}
}
最佳实践:
1. 模块划分:
// 1. 基础模块
@Module
@InstallIn(SingletonComponent::class)
object CoreModule
// 2. 功能模块
@Module
@InstallIn(SingletonComponent::class)
object FeatureModule
// 3. 应用模块
@Module(
includes = [
CoreModule::class,
FeatureModule::class
]
)
@InstallIn(SingletonComponent::class)
object AppModule
2. 依赖注入规则:
// 1. 单向依赖
Feature模块 → Core模块
Feature模块 ↛ Feature模块
// 2. 通过接口解耦
interface DataRepository
class DataRepositoryImpl @Inject constructor() : DataRepository
3. 作用域控制:
// 1. 全局单例
@Singleton
@Provides
fun provideDatabase(): Database
// 2. 特定作用域
@ActivityScoped
@Provides
fun provideNavigator(): Navigator
总结:
@InstallIn定义依赖的作用域includes属性包含其他模块- 通过注入建立模块间依赖
- 使用限定符区分相同类型的依赖
- 遵循依赖注入的最佳实践
这样可以构建清晰、可维护的模块依赖关系。
组件化通信方案
1. LiveData + ViewModel:
// 共享ViewModel方式
@Module
@InstallIn(SingletonComponent::class)
object SharedViewModelModule {
@Singleton
@Provides
fun provideSharedViewModel(): SharedViewModel {
return SharedViewModel()
}
}
// 使用
class FeatureAFragment {
private val sharedViewModel: SharedViewModel by activityViewModels()
fun sendMessage() {
sharedViewModel.message.value = "Hello"
}
}
2. Flow:
// 共享Flow
object MessageBus {
private val _messageFlow = MutableSharedFlow<Message>()
val messageFlow = _messageFlow.asSharedFlow()
suspend fun sendMessage(message: Message) {
_messageFlow.emit(message)
}
}
// 使用
class FeatureAViewModel {
init {
viewModelScope.launch {
MessageBus.messageFlow.collect { message ->
// 处理消息
}
}
}
}
3. 接口下沉:
// 定义接口在base模块
interface IFeatureA {
fun getFeatureData(): String
fun doSomething()
}
// 实现在feature模块
class FeatureAImpl : IFeatureA {
override fun getFeatureData() = "data"
override fun doSomething() {
// 实现
}
}
// 服务注册
@Module
@InstallIn(SingletonComponent::class)
object ServiceModule {
@Provides
@Singleton
fun provideFeatureA(): IFeatureA = FeatureAImpl()
}
4. ServiceLoader:
// 定义服务接口
interface IFeatureService {
fun doFeatureWork()
}
// 实现服务
@AutoService(IFeatureService::class)
class FeatureServiceImpl : IFeatureService {
override fun doFeatureWork() {
// 实现
}
}
// 使用服务
val service = ServiceLoader.load(IFeatureService::class.java)
.first()
5. ARouter:
// 路由注解
@Route(path = "/feature/main")
class FeatureActivity : Activity() {
// 接收参数
@Autowired
lateinit var param: String
}
// 发起调用
ARouter.getInstance()
.build("/feature/main")
.withString("param", "data")
.navigation()
6. StateFlow:
class SharedState {
private val _state = MutableStateFlow<UIState>(UIState.Initial)
val state = _state.asStateFlow()
fun updateState(newState: UIState) {
_state.value = newState
}
}
// 使用
@HiltViewModel
class FeatureViewModel @Inject constructor(
private val sharedState: SharedState
) : ViewModel() {
init {
viewModelScope.launch {
sharedState.state.collect { state ->
// 处理状态更新
}
}
}
}
7. 广播:
// 本地广播
class LocalBroadcastManager {
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 处理消息
}
}
fun register(context: Context) {
LocalBroadcastManager.getInstance(context)
.registerReceiver(receiver, IntentFilter("action"))
}
}
8. Channel:
object MessageChannel {
private val channel = Channel<Message>()
suspend fun send(message: Message) {
channel.send(message)
}
suspend fun receive(): Message {
return channel.receive()
}
}
9. 组合使用示例:
class ModuleCommunicator @Inject constructor(
private val sharedState: SharedState,
private val messageChannel: MessageChannel,
private val router: ARouter
) {
// 状态共享
fun updateState(state: UIState) {
sharedState.updateState(state)
}
// 消息传递
suspend fun sendMessage(message: Message) {
messageChannel.send(message)
}
// 页面跳转
fun navigateToFeature(params: Map<String, Any>) {
router.build("/feature/main")
.with(params)
.navigation()
}
}
各方案对比:
方案 优点 缺点
LiveData 生命周期感知 范围局限
Flow 响应式、灵活 需要协程支持
接口下沉 简单直接 耦合较强
ServiceLoader 解耦合好 配置复杂
ARouter 功能完善 学习成本高
StateFlow 状态管理方便 需要协程支持
广播 使用简单 性能开销大
Channel 异步处理好 使用相对复杂
选择建议:
- 简单场景:LiveData或接口下沉
- 复杂场景:Flow或ARouter
- 状态共享:StateFlow
- 异步通信:Channel
- 可以组合使用多种方案
每种通信方案都有其适用场景,需要根据具体需求选择合适的方案!
通信流程图
组件间通信方案
│
├── 1. LiveData + ViewModel
│ ├── SharedViewModel (共享数据)
│ ├── LiveData (数据持有)
│ └── Observer (数据观察)
│
├── 2. Flow/StateFlow
│ ├── MutableSharedFlow (事件流)
│ ├── StateFlow (状态流)
│ └── collect (数据收集)
│
├── 3. 接口下沉
│ ├── IService (基础接口)
│ ├── ServiceImpl (实现类)
│ └── @Provides (依赖注入)
│
├── 4. ARouter
│ ├── @Route (路由注解)
│ ├── withXXX (参数传递)
│ └── navigation (页面跳转)
│
└── 5. ServiceLoader
├── Interface (服务接口)
├── @AutoService (服务注解)
└── ServiceLoader.load (加载服务)
各方案对比表:
| 通信方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| LiveData+ViewModel | 页面间通信 | 生命周期感知 | 作用域限制 |
| Flow/StateFlow | 响应式通信 | 灵活性强 | 需要协程 |
| 接口下沉 | 简单通信 | 实现简单 | 耦合较强 |
| ARouter | 复杂路由 | 功能完整 | 配置复杂 |
| ServiceLoader | 服务发现 | 解耦合好 | 性能较低 |
具体实现示例:
// 1. LiveData + ViewModel
class SharedViewModel : ViewModel() {
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data
fun updateData(newData: String) {
_data.value = newData
}
}
// 2. Flow/StateFlow
object MessageBus {
private val _messageFlow = MutableSharedFlow<Message>()
val messageFlow = _messageFlow.asSharedFlow()
suspend fun sendMessage(msg: Message) {
_messageFlow.emit(msg)
}
}
// 3. 接口下沉
// base模块
interface IUserService {
fun getUserInfo(): User
}
// user模块
class UserServiceImpl : IUserService {
override fun getUserInfo() = User()
}
// 4. ARouter
// 定义路由
@Route(path = "/user/detail")
class UserDetailActivity : Activity()
// 调用路由
ARouter.getInstance()
.build("/user/detail")
.withString("userId", "123")
.navigation()
// 5. ServiceLoader
@AutoService(IService::class)
class ServiceImpl : IService {
override fun doSomething() {}
}
通信流程示例:
模块A -> 模块B通信流程
│
├── 方案1: LiveData
│ ModuleA -> SharedViewModel -> LiveData.setValue -> ModuleB.observe
│
├── 方案2: Flow
│ ModuleA -> MessageBus.emit -> Flow -> ModuleB.collect
│
├── 方案3: 接口
│ ModuleA -> IService.call -> ServiceImpl -> ModuleB
│
└── 方案4: ARouter
ModuleA -> ARouter.build -> navigation -> ModuleB
选择建议:
场景 推荐方案
├── 简单页面通信 LiveData+ViewModel
├── 复杂数据流 Flow/StateFlow
├── 模块间接口调用 接口下沉
├── 页面跳转 ARouter
└── 服务发现 ServiceLoader
注意事项:
1. 性能考虑
├── 避免频繁跨模块通信
└── 合理使用缓存机制
2. 内存管理
├── 及时解除订阅
└── 避免内存泄漏
3. 线程处理
├── 注意线程切换
└── 异步操作处理
每种方案都有其适用场景,实际使用时可以根据需求组合使用多种方案!
组件化的流程总结图
Hilt组件化流程
│
├── 1. Application层
│ ├── @HiltAndroidApp
│ ├── 自定义Application
│ └── 生成Application_HiltComponents
│
├── 2. Module层
│ ├── @Module (依赖提供模块)
│ ├── @Provides/@Binds (提供依赖)
│ └── @InstallIn (指定作用域)
│
├── 3. Component作用域
│ ├── SingletonComponent (应用级别)
│ ├── ActivityComponent (Activity级别)
│ └── ViewModelComponent (ViewModel级别)
│
└── 4. 注入层
├── @AndroidEntryPoint (标记注入点)
├── @Inject (构造注入/属性注入)
└── @HiltViewModel (ViewModel注入)
| 层级 | 主要注解 | 作用 |
|---|---|---|
| Application层 | @HiltAndroidApp | 初始化Hilt,生成组件 |
| Module层 | @Module, @Provides, @InstallIn | 提供依赖,指定作用域 |
| Component层 | SingletonComponent等 | 定义依赖作用域范围 |
| 注入层 | @AndroidEntryPoint, @Inject | 实现依赖注入 |
| 关键注解说明: |
- 应用层注解:
@HiltAndroidApp
// 作用:初始化Hilt,生成应用级别组件
// 使用位置:Application类
- 模块注解:
@Module
// 作用:声明依赖提供模块
// 使用位置:模块类
@InstallIn(XXXComponent::class)
// 作用:指定模块安装的组件范围
// 使用位置:Module类上
@Provides
// 作用:提供依赖实例的方法
// 使用位置:Module中的方法
@Binds
// 作用:绑定接口实现
// 使用位置:抽象Module中的抽象方法
- 作用域注解:
@Singleton
// 作用:应用级单例
// 使用位置:提供方法或类
@ActivityScoped
// 作用:Activity级别单例
// 使用位置:提供方法或类
@ViewModelScoped
// 作用:ViewModel级别单例
// 使用位置:提供方法或类
- 注入注解:
@AndroidEntryPoint
// 作用:标记可注入的Android组件
// 使用位置:Activity/Fragment/Service等
@Inject
// 作用:标记需要注入的属性或构造函数
// 使用位置:属性或构造函数
@HiltViewModel
// 作用:标记可注入的ViewModel
// 使用位置:ViewModel类
- 限定符注解:
@Qualifier
// 作用:区分同类型的不同实现
// 使用位置:自定义限定符注解
@Named("name")
// 作用:通过名称区分同类型实现
// 使用位置:提供方法和注入点
使用流程:
// 1. Application配置
@HiltAndroidApp
class MyApplication : Application()
// 2. 模块配置
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(): Database
}
// 3. 注入使用
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var database: Database
}
组件化结构:
App
├── :app(主模块)
│ └── @HiltAndroidApp
├── :base(基础模块)
│ └── @Module + @InstallIn
├── :common(公共模块)
│ └── @Module + @InstallIn
└── :feature_xxx(功能模块)
└── @AndroidEntryPoint
核心就是通过不同的注解来管理依赖注入的范围和生命周期!
Hilt的注解都必须使用吗?
1. 最简配置示例:
// 1. 应用层(必需)
@HiltAndroidApp
class MyApplication : Application()
// 2. 模块层(必需)
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideRepository(): Repository {
return RepositoryImpl()
}
}
如果只用这两层,会遇到以下问题:
// 问题1:无法注入依赖
class LoginViewModel {
// 无法获取Repository实例
private val repository: Repository // ❌ 编译错误
}
// 问题2:无法在Android组件中使用
class LoginActivity : AppCompatActivity() {
// 无法注入ViewModel
private val viewModel: LoginViewModel // ❌ 编译错误
}
2. 完整配置示例:
// 1. 应用层
@HiltAndroidApp
class MyApplication : Application()
// 2. 模块层
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides // 必需,告诉Hilt如何创建实例
@Singleton // 可选,控制作用域
fun provideRepository(): Repository {
return RepositoryImpl()
}
}
// 3. 实现层
@HiltViewModel // 必需,支持ViewModel注入
class LoginViewModel @Inject constructor(
private val repository: Repository // 通过构造注入获取依赖
) : ViewModel()
// 4. 使用层
@AndroidEntryPoint // 必需,标记注入点
class LoginActivity : AppCompatActivity() {
private val viewModel: LoginViewModel by viewModels() // 正常工作 ✅
}
3. 各注解的必要性:
// 1. @HiltAndroidApp - 必需
// 初始化Hilt,没有它其他注解都无法工作
// 2. @Module @InstallIn - 必需
// 告诉Hilt在哪里找到依赖的实现
// 3. @Provides @Singleton - 必需至少一个
// @Provides:告诉Hilt如何创建实例
// 或者使用 @Inject constructor() 构造注入
// 4. @HiltViewModel - 在使用ViewModel时必需
// 支持ViewModel的依赖注入
// 5. @AndroidEntryPoint - 在Android组件中使用时必需
// 标记可以接收依赖注入的Android组件
4. 最小配置示例:
// 如果是简单的依赖注入,可以这样简化:
// 1. 应用层(必需)
@HiltAndroidApp
class MyApplication : Application()
// 2. 实现类(使用构造注入替代@Provides)
class Repository @Inject constructor() {
// ...
}
// 3. 使用处(必需)
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var repository: Repository
}
5. 为什么不能只用应用层和模块层:
// 问题1:无法创建实例
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
// ❌ 缺少@Provides或@Inject,Hilt不知道如何创建实例
fun provideRepository(): Repository
}
// 问题2:无法注入到Android组件
class MainActivity : AppCompatActivity() {
// ❌ 缺少@AndroidEntryPoint,无法接收注入
@Inject
lateinit var repository: Repository
}
总结:
@HiltAndroidApp- 必需,初始化Hilt@Module @InstallIn- 提供依赖时必需@Provides/@Inject- 必需至少一个,告诉Hilt如何创建实例@HiltViewModel- 使用ViewModel时必需@AndroidEntryPoint- 在Android组件中使用时必需
建议:
- 不要过度简化,遵循Hilt的标准用法
- 根据实际需求选择合适的注解组合
- 保持依赖注入的清晰和可维护性
组件化在Gradle编译中的优势
graph TD
A[非组件化编译] --> B[所有代码一起编译]
B --> C[任何修改都需要全量编译]
C --> D[较长的编译时间]
E[组件化编译] --> F[独立模块并行编译]
F --> G[增量编译]
G --> H[显著减少编译时间]
主要优势:
1. 并行编译:
// build.gradle
android {
// 开启并行编译
dexOptions {
preDexLibraries = true
maxProcessCount = 8 // 并行数
}
}
// 各模块可以同时编译
:app
:module_login ↘
:module_home → 并行执行
:module_profile ↗
2. 增量编译:
// 只编译修改的模块
修改login模块 → 只重新编译login模块
修改home模块 → 只重新编译home模块
// 非组件化
修改任何代码 → 需要重新编译整个项目
3. 编译缓存优化:
// build.gradle
android {
buildCache {
enabled = true
}
}
// 模块级缓存
:module_login:build → module_login缓存
:module_home:build → module_home缓存
:module_profile:build → module_profile缓存
4. 依赖隔离:
// app/build.gradle
dependencies {
if (isDebug) {
implementation project(':module_login')
implementation project(':module_home')
} else {
runtimeOnly project(':module_login')
runtimeOnly project(':module_home')
}
}
// 避免不必要的依赖编译
5. 资源隔离:
// 每个模块独立的资源
module_login/
src/main/res/
module_home/
src/main/res/
// 避免资源冲突和重复编译
6. 构建变体优化:
// 模块级别的构建变体
android {
productFlavors {
free {
// 免费版本
}
paid {
// 付费版本
}
}
}
// 可以只编译需要的模块和变体
7. 编译速度对比:
非组件化项目:
修改一行代码 → 编译时间:3-5分钟
组件化项目:
修改一个模块 → 编译时间:30秒-1分钟
8. 内存使用优化:
// gradle.properties
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m
// 每个模块可以独立控制内存使用
// 避免整体项目的内存压力
9. CI/CD优势:
# Jenkins pipeline
stages:
- compile_login_module
- compile_home_module
- compile_profile_module
# 可以并行执行,加快构建速度
10. 调试优化:
// 开发时可以只运行单个模块
./gradlew :module_login:assembleDebug
// 避免编译整个项目
// 加快开发调试速度
实际收益统计:
1. 编译时间
- 全量编译:减少40-60%
- 增量编译:减少70-90%
2. 内存使用
- 峰值减少:30-50%
- GC压力减少:40-60%
3. 开发效率
- 调试速度提升:50-80%
- 代码修改反馈更快
最佳实践建议:
1. 合理划分模块粒度
2. 做好模块间依赖管理
3. 使用buildSrc统一依赖
4. 配置proper gradle.properties
5. 使用并行编译
6. 启用构建缓存
7. 控制资源文件大小
8. 及时清理无用资源
这些优势使得组件化在大型项目中特别有价值,能显著提升开发效率和编译速度。
单独Module的配置运行
1. 首先在根目录的gradle.properties中添加配置开关:
# gradle.properties
isRunAlone=true
debugModule=module_login # 当前调试的模块名
2. 在每个Feature模块的build.gradle中配置:
// module_login/build.gradle
def isRunAlone = rootProject.ext.isRunAlone
def debugModule = rootProject.ext.debugModule
if (isRunAlone && debugModule == "module_login") {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
defaultConfig {
if (isRunAlone && debugModule == "module_login") {
applicationId "com.example.login" // 独立运行时的包名
}
}
sourceSets {
main {
if (isRunAlone && debugModule == "module_login") {
// 独立运行时使用的清单文件
manifest.srcFile 'src/main/alone/AndroidManifest.xml'
// 独立运行时的资源文件
java.srcDirs += "src/main/alone/java"
res.srcDirs += "src/main/alone/res"
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
3. 创建独立运行时的清单文件:
<!-- module_login/src/main/alone/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.login">
<application
android:name=".LoginApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
4. 创建独立运行时的Application:
// module_login/src/main/alone/java/com/example/login/LoginApplication.kt
@HiltAndroidApp
class LoginApplication : Application() {
override fun onCreate() {
super.onCreate()
// 独立运行时的初始化
}
}
5. 模块间通信的处理:
// 使用接口隔离
interface LoginService {
fun login(username: String, password: String)
}
// 在独立运行时提供mock实现
if (isRunAlone) {
class MockLoginService : LoginService {
override fun login(username: String, password: String) {
// mock实现
}
}
}
6. 依赖配置:
dependencies {
// 基础依赖
implementation project(':common')
// 独立运行时的依赖
if (isRunAlone && debugModule == "module_login") {
implementation 'androidx.appcompat:appcompat:1.x.x'
implementation 'com.google.android.material:material:1.x.x'
// 其他需要的依赖
}
}
7. 快速切换脚本:
// 创建gradle task快速切换模块
task switchToLogin() {
doLast {
def propertiesFile = file('gradle.properties')
def properties = new Properties()
properties.load(propertiesFile.newDataInputStream())
properties.setProperty('isRunAlone', 'true')
properties.setProperty('debugModule', 'module_login')
properties.store(propertiesFile.newWriter(), null)
}
}
使用方法:
# 命令行切换到登录模块
./gradlew switchToLogin
# 只编译登录模块
./gradlew :module_login:assembleDebug
# 直接运行登录模块
./gradlew :module_login:installDebug
目录结构:
module_login/
├── build.gradle
├── src/
│ ├── main/
│ │ ├── java/
│ │ ├── res/
│ │ └── AndroidManifest.xml
│ └── alone/
│ ├── java/
│ ├── res/
│ └── AndroidManifest.xml
优势:
- 显著减少编译时间
- 加快开发调试速度
- 便于模块独立测试
- 支持并行开发
- 减少内存占用
注意事项:
- 合理管理依赖
- 处理好模块间通信
- 维护好独立运行的配置
- 注意资源命名冲突
- 保持清单文件的同步
这样配置后,可以实现快速切换和独立编译运行特定模块,大大提高开发效率。
Hilt配置完组件化后的编译过程
让我详细解释使用Hilt的组件化项目的编译流程:
graph TD
A[项目编译开始] --> B[Hilt注解处理]
B --> C{各Feature模块并行处理}
C --> D[Login模块]
C --> E[Home模块]
C --> F[Profile模块]
D --> G[生成Login_HiltComponents]
E --> H[生成Home_HiltComponents]
F --> I[生成Profile_HiltComponents]
G --> J[合并依赖图]
H --> J
I --> J
J --> K[生成最终Application_HiltComponents]
K --> L[完成编译]
详细编译流程:
1. 初始化阶段:
// 1. 检查Hilt配置
@HiltAndroidApp
class MyApplication : Application()
// 2. 收集所有模块的Hilt组件
modules/
├── app/
│ └── @HiltAndroidApp
├── feature_login/
│ └── @Module @InstallIn
├── feature_home/
│ └── @Module @InstallIn
└── feature_profile/
└── @Module @InstallIn
2. 注解处理阶段:
// 1. 处理@HiltAndroidApp
class Hilt_MyApplication : Application() {
private var injected = false
@Override
fun onCreate() {
// 初始化Hilt组件
DaggerMyApplication_HiltComponents_SingletonC
.builder()
.build()
}
}
// 2. 处理各模块的@Module
@Generated("dagger.hilt.processor.internal.root.RootProcessor")
class LoginModule_ProvideLoginRepositoryFactory {
// 生成Factory类
}
3. 依赖图构建:
// 1. 模块级依赖图
@Component(modules = [
LoginModule::class,
HomeModule::class,
ProfileModule::class
])
interface AppComponent {
// 组件依赖关系
}
// 2. 作用域管理
@Singleton
@Component.Builder
interface Builder {
// 构建器定义
}
4. 代码生成阶段:
// 1. 生成各模块的组件工厂
class DaggerLoginComponent {
static Builder builder() {
return new Builder();
}
}
// 2. 生成最终的应用组件
class DaggerMyApplication_HiltComponents {
// 整合所有模块的依赖
}
5. 并行编译优化:
// build.gradle
android {
// 开启并行编译
dexOptions {
preDexLibraries = true
maxProcessCount = 8
}
}
6. 增量编译支持:
// 只重新编译修改的模块
@Module
@InstallIn(SingletonComponent::class)
object LoginModule {
// 修改此模块只会触发相关代码重新编译
}
7. 编译时验证:
// 1. 作用域验证
@ActivityScoped
class LoginViewModel @Inject constructor() // 编译时检查作用域
// 2. 依赖验证
class HomeViewModel @Inject constructor(
private val repository: HomeRepository // 编译时检查依赖可用性
)
8. 组件合并过程:
graph LR
A[Login组件] --> D[合并]
B[Home组件] --> D
C[Profile组件] --> D
D --> E[最终应用组件]
9. 编译产物:
build/
├── generated/
│ └── hilt/
│ ├── components/
│ ├── factories/
│ └── proxies/
└── intermediates/
└── hilt/
关键优化点:
1. 并行处理:
// 各模块可以并行处理
LoginModule_Proxy ─┐
HomeModule_Proxy ─┼─> 最终合并
ProfileModule_Proxy┘
2. 增量编译:
// 修改单个模块只重新生成相关代码
修改LoginModule ─> 只重新生成Login相关代码
3. 缓存优化:
android {
buildCache {
enabled = true
}
}
4. 内存优化:
// gradle.properties
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=512m
编译性能对比:
传统编译:
修改一个模块 -> 重新编译所有代码
编译时间:3-5分钟
Hilt组件化编译:
修改一个模块 -> 只重新编译变更模块
编译时间:30秒-1分钟
最佳实践:
1. 模块划分:
app/
├── common/
├── feature_login/
├── feature_home/
└── feature_profile/
2. 依赖管理:
// 使用buildSrc统一管理依赖
object Deps {
const val hiltAndroid = "com.google.dagger:hilt-android:2.x.x"
const val hiltCompiler = "com.google.dagger:hilt-compiler:2.x.x"
}
3. 编译配置:
// 优化编译配置
android {
compileSdkVersion 33
buildToolsVersion "33.0.0"
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
这样的编译流程能够显著提升组件化项目的编译效率和开发体验。
Application_HiltComponents的详细讲解
Application_HiltComponents 是Hilt在编译时生成的一个最终的依赖图类,它包含了所有模块的依赖关系。让我详细解释:
1. 编译过程:
// 1. 各模块的依赖定义
@Module
@InstallIn(SingletonComponent::class)
object LoginModule {
@Provides
fun provideLoginRepository(): LoginRepository
}
@Module
@InstallIn(SingletonComponent::class)
object HomeModule {
@Provides
fun provideHomeRepository(): HomeRepository
}
// 2. Hilt编译时会生成代理类
// build/generated/source/kapt/debug/
LoginModule_Proxy
HomeModule_Proxy
ProfileModule_Proxy
// 3. 最终生成总的依赖组件类
DaggerApplication_HiltComponents
2. 生成的组件结构:
// 简化版的生成代码结构
public final class DaggerApplication_HiltComponents {
// 单例组件
public final class SingletonC {
// 所有模块提供的依赖
private final LoginRepository loginRepository;
private final HomeRepository homeRepository;
private final ProfileRepository profileRepository;
// 构造函数,初始化所有依赖
SingletonC() {
this.loginRepository = new LoginRepositoryImpl();
this.homeRepository = new HomeRepositoryImpl();
this.profileRepository = new ProfileRepositoryImpl();
}
// 获取依赖的方法
LoginRepository getLoginRepository() {
return loginRepository;
}
HomeRepository getHomeRepository() {
return homeRepository;
}
}
// Activity组件
public final class ActivityC {
// 持有上层组件引用
private final SingletonC singletonC;
// Activity级别的依赖
private final ActivityViewModel viewModel;
}
// 其他作用域的组件...
}
3. 依赖注入过程:
// 1. 应用启动时
@HiltAndroidApp
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Hilt自动调用生成的代码
DaggerApplication_HiltComponents.builder()
.build()
.inject(this)
}
}
// 2. 注入到Activity
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var loginRepository: LoginRepository
override fun onCreate(savedInstanceState: Bundle?) {
// Hilt自动从组件中获取依赖
super.onCreate(savedInstanceState)
}
}
4. 组件层级关系:
DaggerApplication_HiltComponents
├── SingletonComponent(应用级别)
│ ├── LoginRepository
│ ├── HomeRepository
│ └── ProfileRepository
│
├── ActivityRetainedComponent
│ └── ViewModels
│
├── ActivityComponent
│ └── Activity级别依赖
│
└── FragmentComponent
└── Fragment级别依赖
- 实际作用:
// 1. 管理依赖的生命周期
@Singleton // 应用级别单例
class LoginRepository @Inject constructor()
@ActivityScoped // Activity级别单例
class Navigator @Inject constructor()
// 2. 处理依赖关系
class LoginViewModel @Inject constructor(
private val repository: LoginRepository, // 从SingletonComponent获取
private val navigator: Navigator // 从ActivityComponent获取
)
// 3. 确保依赖的正确性
// 编译时检查:
// - 依赖是否都能提供
// - 作用域是否正确
// - 循环依赖是否存在
总结:
-
Application_HiltComponents是:- 所有依赖的容器
- 管理依赖的生命周期
- 处理依赖注入关系
- 确保依赖的正确性
-
它的作用:
- 集中管理所有依赖
- 自动处理依赖注入
- 维护依赖的作用域
- 提供类型安全的依赖获取
-
对开发者来说:
- 不需要直接使用这个类
- Hilt自动处理所有依赖注入
- 只需要关注依赖的定义和使用
这就是为什么Hilt能够自动处理依赖注入,而我们只需要使用注解来定义依赖关系。
流程总结图
graph TD
A[开始编译] --> B[扫描Hilt注解]
B --> C1[LoginModule]
B --> C2[HomeModule]
B --> C3[ProfileModule]
C1 --> D1[生成LoginModule_Proxy]
C2 --> D2[生成HomeModule_Proxy]
C3 --> D3[生成ProfileModule_Proxy]
D1 --> E[合并所有Proxy]
D2 --> E
D3 --> E
E --> F[生成DaggerApplication_HiltComponents]
F --> G[SingletonComponent]
F --> H[ActivityComponent]
F --> I[FragmentComponent]
G --> J[运行时依赖注入]
详细解释每个步骤:
graph TD
A[1. 编译开始] --> B[2. 注解处理]
B --> C[3. 生成代理类]
C --> D[4. 合并依赖图]
D --> E[5. 生成最终组件]
E --> F[6. 运行时注入]
subgraph "2. 注解处理"
B1[HiltAndroidApp]
B2[Module]
B3[Provides]
B4[Inject]
end
subgraph "3. 代理类生成"
C1[LoginModule_Proxy]
C2[HomeModule_Proxy]
C3[ProfileModule_Proxy]
end
subgraph "4. 依赖图合并"
D1[验证依赖关系]
D2[检查作用域]
D3[合并提供方法]
end
subgraph "5. 组件生成"
E1[SingletonComponent]
E2[ActivityComponent]
E3[FragmentComponent]
end
subgraph "6. 运行时"
F1[初始化组件]
F2[注入依赖]
F3[管理生命周期]
end
使用流程:
graph LR
A[定义依赖] --> B[编译处理]
B --> C[生成代码]
C --> D[运行注入]
subgraph "定义依赖"
A1[Module]
A2[Provides]
A3[Inject]
end
subgraph "编译处理"
B1[注解处理器]
B2[生成代理]
B3[依赖验证]
end
subgraph "生成代码"
C1[组件类]
C2[工厂类]
C3[注入器]
end
subgraph "运行注入"
D1[初始化]
D2[获取依赖]
D3[生命周期]
end
组件层级:
graph TD
A[SingletonComponent] --> B[ActivityRetainedComponent]
B --> C[ActivityComponent]
C --> D[FragmentComponent]
D --> E[ViewComponent]
E --> F[ViewWithFragmentComponent]
subgraph "作用域"
A1[Singleton]
C1[ActivityScoped]
D1[FragmentScoped]
end
这些流程图展示了:
- Hilt的编译过程
- 依赖处理流程
- 组件生成过程
- 运行时注入流程
- 组件层级关系
希望这些图能帮助你更好地理解Hilt的工作原理!
Navigation在组件化中的应用
结合Compose来详细说明Navigation在组件化项目中的应用:
1. 基础导航配置:
// common/navigation/NavigationGraph.kt
sealed class NavigationGraph(val route: String) {
object Login : NavigationGraph("login")
object Home : NavigationGraph("home")
data class Profile(val userId: String? = null) :
NavigationGraph("profile?userId={userId}") {
fun createRoute(userId: String) = "profile?userId=$userId"
}
}
// app/MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject lateinit var navigationService: NavigationService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
AppNavigation()
}
}
}
自定义NavigationGraph
// 1. 直接使用字符串容易出错
navController.navigate("login") // 可能拼写错误
navController.navigate("profile/123") // 格式可能错误
// 2. 自定义后
sealed class NavigationGraph(val route: String) {
object Login : NavigationGraph("login")
data class Profile(val userId: String? = null) :
NavigationGraph("profile/{userId}")
}
// 使用时更安全
navController.navigate(NavigationGraph.Login.route) // IDE自动完成
navController.navigate(NavigationGraph.Profile("123").route) // 类型安全
2. 主导航图设置:
// app/navigation/AppNavigation.kt
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = NavigationGraph.Login.route
) {
// 登录模块导航
loginNavigation(navController)
// 首页模块导航
homeNavigation(navController)
// 个人中心模块导航
profileNavigation(navController)
}
}
3. Feature模块导航配置:
// feature_login/navigation/LoginNavigation.kt
fun NavGraphBuilder.loginNavigation(
navController: NavController
) {
navigation(
startDestination = "login_main",
route = NavigationGraph.Login.route
) {
composable("login_main") {
LoginScreen(
onNavigateToHome = {
navController.navigate(NavigationGraph.Home.route) {
popUpTo(NavigationGraph.Login.route) { inclusive = true }
}
}
)
}
}
}
// feature_login/ui/LoginScreen.kt
@Composable
fun LoginScreen(
onNavigateToHome: () -> Unit,
viewModel: LoginViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsState()
LoginContent(
uiState = uiState,
onLoginClick = { username, password ->
viewModel.login(username, password)
},
onLoginSuccess = onNavigateToHome
)
}
NavGraphBuilder的详解
NavGraphBuilder 是 Jetpack Navigation 组件提供的系统类,用于构建导航图。让我详细解释其使用方式:
- 扩展函数定义:
// 1. 基本扩展函数结构
fun NavGraphBuilder.loginNavigation(
navController: NavController
) {
// 这是一个NavGraphBuilder的扩展函数
// 用于模块化导航图的构建
}
// 2. 在主导航图中使用
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = NavigationGraph.Login.route
) {
// 这里的it或this就是NavGraphBuilder
loginNavigation(navController)
homeNavigation(navController)
profileNavigation(navController)
}
}
- 完整的模块导航示例:
// feature_login/navigation/LoginNavigation.kt
fun NavGraphBuilder.loginNavigation(
navController: NavController
) {
// 创建一个子导航图
navigation(
startDestination = "login_main", // 子图的起始目的地
route = NavigationGraph.Login.route // 子图的路由
) {
// 登录主页面
composable("login_main") {
LoginScreen(
onNavigateToHome = {
navController.navigate(NavigationGraph.Home.route) {
// 导航选项
popUpTo(NavigationGraph.Login.route) {
inclusive = true // 包含当前路由
}
}
}
)
}
// 注册页面
composable("login_register") {
RegisterScreen()
}
// 忘记密码页面
composable("login_forgot_password") {
ForgotPasswordScreen()
}
}
}
- 常用的 NavGraphBuilder 函数:
fun NavGraphBuilder.loginNavigation(
navController: NavController
) {
// 1. composable - 添加组合页面
composable(
route = "login",
arguments = listOf(
navArgument("userId") { type = NavType.StringType }
),
deepLinks = listOf(
navDeepLink { uriPattern = "example://login/{userId}" }
)
) {
LoginScreen()
}
// 2. navigation - 创建子导航图
navigation(
startDestination = "login_main",
route = "login_graph"
) {
// 子导航图内容
}
// 3. dialog - 添加对话框目的地
dialog("login_dialog") {
LoginDialog()
}
}
- 带动画的导航:
fun NavGraphBuilder.loginNavigation(
navController: NavController
) {
composable(
route = "login",
enterTransition = {
slideIntoContainer(AnimatedContentScope.SlideDirection.Left)
},
exitTransition = {
slideOutOfContainer(AnimatedContentScope.SlideDirection.Right)
}
) {
LoginScreen()
}
}
- 参数传递:
fun NavGraphBuilder.loginNavigation(
navController: NavController
) {
composable(
route = "login?userId={userId}&type={type}",
arguments = listOf(
navArgument("userId") {
type = NavType.StringType
nullable = true
defaultValue = null
},
navArgument("type") {
type = NavType.StringType
defaultValue = "normal"
}
)
) { backStackEntry ->
// 获取参数
val userId = backStackEntry.arguments?.getString("userId")
val type = backStackEntry.arguments?.getString("type")
LoginScreen(userId = userId, type = type)
}
}
- 嵌套导航:
fun NavGraphBuilder.loginNavigation(
navController: NavController
) {
navigation(
startDestination = "login_main",
route = "login_graph"
) {
composable("login_main") {
LoginScreen()
}
// 嵌套的认证流程
navigation(
startDestination = "auth_password",
route = "auth_graph"
) {
composable("auth_password") {
PasswordScreen()
}
composable("auth_2fa") {
TwoFactorAuthScreen()
}
}
}
}
使用建议:
- 每个功能模块创建独立的导航扩展函数
- 使用 navigation 创建子导航图
- 合理组织页面层级
- 注意参数传递的类型安全
- 适当添加转场动画
这样的模块化导航结构可以让代码更清晰,更易维护,同时也支持功能模块的独立开发和测试。
4. 参数传递:
// feature_profile/navigation/ProfileNavigation.kt
fun NavGraphBuilder.profileNavigation(
navController: NavController
) {
navigation(
startDestination = "profile_main",
route = NavigationGraph.Profile.route
) {
composable(
route = "profile_main?userId={userId}",
arguments = listOf(
navArgument("userId") {
type = NavType.StringType
nullable = true
}
)
) { backStackEntry ->
val userId = backStackEntry.arguments?.getString("userId")
ProfileScreen(userId = userId)
}
}
}
5. 统一导航服务:
// common/navigation/NavigationService.kt
@Singleton
class NavigationServiceImpl @Inject constructor() {
private lateinit var navController: NavController
fun init(navController: NavController) {
this.navController = navController
}
fun navigateToProfile(userId: String) {
navController.navigate(
NavigationGraph.Profile.createRoute(userId)
)
}
}
// 在Compose中使用
@Composable
fun SomeScreen(
navigationService: NavigationService = hiltViewModel()
) {
Button(onClick = {
navigationService.navigateToProfile("123")
}) {
Text("Go to Profile")
}
}
6. Deep Links支持:
// feature_home/navigation/HomeNavigation.kt
fun NavGraphBuilder.homeNavigation(
navController: NavController
) {
navigation(
startDestination = "home_main",
route = NavigationGraph.Home.route
) {
composable(
route = "home_main",
deepLinks = listOf(
navDeepLink {
uriPattern = "example://home"
}
)
) {
HomeScreen()
}
}
}
7. 动画过渡:
// common/navigation/NavigationAnimation.kt
@Composable
fun NavigationAnimation(
content: @Composable AnimatedVisibilityScope.() -> Unit
) {
AnimatedVisibility(
visible = true,
enter = slideInHorizontally() + fadeIn(),
exit = slideOutHorizontally() + fadeOut(),
content = content
)
}
// 在导航中使用
composable(
route = "profile_main",
enterTransition = {
slideIntoContainer(AnimatedContentScope.SlideDirection.Left)
},
exitTransition = {
slideOutOfContainer(AnimatedContentScope.SlideDirection.Right)
}
) {
NavigationAnimation {
ProfileScreen()
}
}
8. 状态保持:
// feature_home/ui/HomeScreen.kt
@Composable
fun HomeScreen(
viewModel: HomeViewModel = hiltViewModel()
) {
// 使用rememberSaveable保持状态
var selectedTab by rememberSaveable { mutableStateOf(0) }
Column {
TabRow(selectedTabIndex = selectedTab) {
// Tabs
}
when (selectedTab) {
0 -> Tab1Content()
1 -> Tab2Content()
}
}
}
优势:
-
声明式导航:
- 更符合Compose的声明式UI理念
- 代码更清晰易读
-
类型安全:
- 参数传递更安全
- 路由定义集中管理
-
状态管理:
- 与Compose状态管理无缝集成
- 支持状态保持和恢复
-
动画支持:
- 内置转场动画
- 自定义动画更简单
-
测试友好:
- 可以方便地测试导航逻辑
- 支持UI测试
使用建议:
- 统一管理导航路由
- 合理使用动画过渡
- 注意状态保持
- 处理好返回栈
- 做好深链接支持
这样的导航架构能让组件化的Compose项目更加清晰和易维护。
总结
Navigation在组件化中主要解决了这两个核心问题。
1. 模块间跳转:
// 1. 在common模块定义导航路由
sealed class NavigationGraph(val route: String) {
object Login : NavigationGraph("login")
object Home : NavigationGraph("home")
object Profile : NavigationGraph("profile")
}
// 2. 在各个功能模块中定义导航
fun NavGraphBuilder.loginNavigation(
navController: NavController
) {
navigation(
startDestination = "login_main",
route = NavigationGraph.Login.route
) {
composable("login_main") {
LoginScreen(
onLoginSuccess = {
// 跳转到home模块
navController.navigate(NavigationGraph.Home.route)
}
)
}
}
}
2. 参数传递:
// 1. 定义带参数的路由
sealed class NavigationGraph(val route: String) {
// 必选参数
object ProductDetail : NavigationGraph("product/{productId}") {
fun createRoute(productId: String) = "product/$productId"
}
// 可选参数
object Profile : NavigationGraph("profile?userId={userId}") {
fun createRoute(userId: String?) = "profile?userId=$userId"
}
}
// 2. 配置参数
fun NavGraphBuilder.productNavigation(
navController: NavController
) {
composable(
route = NavigationGraph.ProductDetail.route,
arguments = listOf(
navArgument("productId") {
type = NavType.StringType
}
)
) { backStackEntry ->
// 获取参数
val productId = backStackEntry.arguments?.getString("productId")
ProductDetailScreen(productId = productId)
}
}
3. 完整的跨模块通信示例:
// 1. 定义导航事件
sealed class NavigationEvent {
data class ToProductDetail(val productId: String) : NavigationEvent()
data class ToUserProfile(val userId: String?) : NavigationEvent()
object ToCart : NavigationEvent()
}
// 2. 导航管理器
@Singleton
class NavigationManager @Inject constructor() {
private val _navigationEvent = MutableSharedFlow<NavigationEvent>()
val navigationEvent = _navigationEvent.asSharedFlow()
suspend fun navigate(event: NavigationEvent) {
_navigationEvent.emit(event)
}
}
// 3. 在ViewModel中使用
@HiltViewModel
class ProductViewModel @Inject constructor(
private val navigationManager: NavigationManager
) : ViewModel() {
fun onProductClick(productId: String) {
viewModelScope.launch {
navigationManager.navigate(
NavigationEvent.ToProductDetail(productId)
)
}
}
}
// 4. 在主Activity中处理导航
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var navigationManager: NavigationManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
// 收集导航事件
LaunchedEffect(Unit) {
navigationManager.navigationEvent.collect { event ->
when (event) {
is NavigationEvent.ToProductDetail -> {
navController.navigate(
NavigationGraph.ProductDetail.createRoute(
event.productId
)
)
}
is NavigationEvent.ToUserProfile -> {
navController.navigate(
NavigationGraph.Profile.createRoute(
event.userId
)
)
}
NavigationEvent.ToCart -> {
navController.navigate(NavigationGraph.Cart.route)
}
}
}
}
// 导航图
NavHost(
navController = navController,
startDestination = NavigationGraph.Home.route
) {
homeNavigation(navController)
productNavigation(navController)
profileNavigation(navController)
cartNavigation(navController)
}
}
}
}
4. 导航的优势:
graph TD
A[Navigation优势] --> B[类型安全]
A --> C[参数传递]
A --> D[生命周期管理]
A --> E[深链接支持]
B --> B1[编译时检查路由]
B --> B2[参数类型检查]
C --> C1[必选参数]
C --> C2[可选参数]
C --> C3[参数默认值]
D --> D1[自动处理返回栈]
D --> D2[状态保存恢复]
E --> E1[支持外部跳转]
E --> E2[支持应用内跳转]
5. 最佳实践:
// 1. 路由统一管理
sealed class NavigationGraph(val route: String) {
// 所有路由定义在一处
}
// 2. 参数封装
data class ProductDetailArgs(
val productId: String,
val from: String? = null
)
// 3. 导航扩展
fun NavController.navigateToProduct(args: ProductDetailArgs) {
navigate(NavigationGraph.ProductDetail.createRoute(args))
}
// 4. 动画支持
fun NavGraphBuilder.productAnimation(
content: @Composable () -> Unit
) {
composable(
route = NavigationGraph.ProductDetail.route,
enterTransition = { slideInHorizontally() },
exitTransition = { slideOutHorizontally() }
) {
content()
}
}
总结:
-
Navigation解决了:
- 模块间跳转
- 参数传递
- 类型安全
- 生命周期管理
-
使用建议:
- 统一管理路由
- 封装导航逻辑
- 规范参数传递
- 合理使用动画
这样可以让组件化项目的导航更加清晰和可维护。