依赖注入
概述
依赖注入(Dependency Injection,简称DI)是一种设计模式,它允许我们将对象的依赖关系从代码中解耦出来,由外部容器负责创建和管理这些依赖关系。在Android开发中,依赖注入可以帮助我们编写更加清晰、可维护和可测试的代码。
主流框架
-
Hilt
- Google官方推荐的依赖注入框架
- 基于Dagger构建
- 专门为Android优化
- 配置简单,使用方便
-
Dagger2
- 完全静态编译时依赖注入框架
- 高性能,零反射
- 编译时验证
- 学习曲线较陡
-
Koin
- 纯Kotlin实现的依赖注入框架
- 轻量级,配置简单
- 基于DSL语法
- 运行时依赖注入
实现示例
1. Hilt使用示例
// 1. 在Application类上添加注解
@HiltAndroidApp
class MyApplication : Application()
// 2. 定义需要注入的类
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.build()
}
}
// 3. 在Activity中使用
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var retrofit: Retrofit
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// retrofit可以直接使用
}
}2. Dagger2使用示例
// 1. 定义Component接口
@Singleton
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
}
// 2. 定义Module
@Module
class NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder().build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://api.example.com")
.build()
}
}
// 3. 在Activity中使用
class MainActivity : AppCompatActivity() {
@Inject
lateinit var retrofit: Retrofit
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(application as MyApplication).appComponent.inject(this)
}
}3. Koin使用示例
// 1. 定义Module
val networkModule = module {
single { OkHttpClient.Builder().build() }
single {
Retrofit.Builder()
.client(get())
.baseUrl("https://api.example.com")
.build()
}
}
// 2. 在Application中启动Koin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
modules(networkModule)
}
}
}
// 3. 在Activity中使用
class MainActivity : AppCompatActivity() {
private val retrofit: Retrofit by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// retrofit可以直接使用
}
}面试常见问题
1. 依赖注入的优势是什么?
- 解耦:减少类之间的依赖关系
- 可测试性:便于编写单元测试
- 可维护性:集中管理依赖关系
- 可复用性:依赖可以在不同地方复用
- 灵活性:易于切换实现
2. Hilt与Dagger2的区别?
-
Hilt:
- 更简单的配置
- 标准化的组件集成
- Android特定的作用域
- 预定义的绑定
-
Dagger2:
- 更灵活的配置
- 完全控制依赖图
- 通用的Java/Kotlin框架
- 更陡的学习曲线
3. 构造函数注入和字段注入的区别?
-
构造函数注入:
- 依赖不可变
- 编译时强制检查
- 更清晰的依赖关系
- 推荐使用
-
字段注入:
- 依赖可变
- 可能出现空指针
- 依赖关系不明确
- 不推荐使用
4. 如何处理循环依赖?
- 使用Lazy注入
- 重构设计避免循环依赖
- 使用Provider接口
- 使用接口解耦
5. Dagger2的工作原理是什么?
-
编译时处理:
- 通过注解处理器生成代码
- 构建依赖图
- 验证依赖关系
- 生成工厂类
-
依赖图构建:
- Component作为依赖的容器
- Module提供依赖实例
- Binds绑定接口实现
- Scope控制实例生命周期
6. Hilt的作用域注解有哪些?
- @Singleton:应用级别单例
- @ActivityScoped:Activity级别单例
- @FragmentScoped:Fragment级别单例
- @ViewScoped:View级别单例
- @ServiceScoped:Service级别单例
7. 依赖注入框架的性能优化
-
编译时优化:
- 使用编译时注解处理
- 避免反射操作
- 减少依赖图复杂度
-
运行时优化:
- 合理使用作用域
- 懒加载依赖
- 避免不必要的实例创建
最佳实践
-
选择合适的框架
- 项目规模
- 团队熟悉度
- 性能要求
- 维护成本
-
依赖注入原则
- 优先使用构造函数注入
- 避免过度使用Singleton
- 合理划分Module
- 保持依赖图简单
-
作用域管理
- 合理使用作用域注解
- 注意内存泄漏
- 及时释放资源
-
测试策略
- 编写单元测试
- 使用测试替身
- 模拟依赖
注意事项
-
性能考虑
- 避免过度使用依赖注入
- 注意初始化性能
- 合理使用懒加载
-
内存管理
- 注意对象生命周期
- 避免内存泄漏
- 及时清理资源
-
调试与维护
- 使用调试工具
- 保持依赖关系清晰
- 及时更新文档
高级特性
- 多绑定
@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {
@Binds
@IntoSet
abstract fun bindGoogleAnalytics(analytics: GoogleAnalytics): AnalyticsService
@Binds
@IntoSet
abstract fun bindFirebaseAnalytics(analytics: FirebaseAnalytics): AnalyticsService
}- 限定符
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@AuthInterceptorOkHttpClient
@Provides
fun provideAuthInterceptorOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(AuthInterceptor())
.build()
}
}- 子组件
@Subcomponent
interface UserComponent {
@Subcomponent.Factory
interface Factory {
fun create(): UserComponent
}
}源码分析
1. Dagger2生成代码分析
// Dagger生成的工厂类示例
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Retrofit> retrofitProvider;
public MainActivity_MembersInjector(Provider<Retrofit> retrofitProvider) {
this.retrofitProvider = retrofitProvider;
}
public static MembersInjector<MainActivity> create(Provider<Retrofit> retrofitProvider) {
return new MainActivity_MembersInjector(retrofitProvider);
}
@Override
public void injectMembers(MainActivity instance) {
instance.retrofit = retrofitProvider.get();
}
}2. Hilt生成代码分析
// Hilt生成的Component示例
@Component(
modules = {
ApplicationContextModule.class,
ActivityRetainedComponentManager_LifecycleModule.class,
NetworkModule.class
}
)
@Singleton
public abstract class DaggerHiltAndroidApp_HiltComponents_SingletonC implements HiltAndroidApp_HiltComponents.SingletonC {
// ...
}