Android面试题库
架构设计
依赖注入

依赖注入

概述

依赖注入(Dependency Injection,简称DI)是一种设计模式,它允许我们将对象的依赖关系从代码中解耦出来,由外部容器负责创建和管理这些依赖关系。在Android开发中,依赖注入可以帮助我们编写更加清晰、可维护和可测试的代码。

主流框架

  1. Hilt

    • Google官方推荐的依赖注入框架
    • 基于Dagger构建
    • 专门为Android优化
    • 配置简单,使用方便
  2. Dagger2

    • 完全静态编译时依赖注入框架
    • 高性能,零反射
    • 编译时验证
    • 学习曲线较陡
  3. 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的区别?

  1. Hilt

    • 更简单的配置
    • 标准化的组件集成
    • Android特定的作用域
    • 预定义的绑定
  2. Dagger2

    • 更灵活的配置
    • 完全控制依赖图
    • 通用的Java/Kotlin框架
    • 更陡的学习曲线

3. 构造函数注入和字段注入的区别?

  1. 构造函数注入

    • 依赖不可变
    • 编译时强制检查
    • 更清晰的依赖关系
    • 推荐使用
  2. 字段注入

    • 依赖可变
    • 可能出现空指针
    • 依赖关系不明确
    • 不推荐使用

4. 如何处理循环依赖?

  • 使用Lazy注入
  • 重构设计避免循环依赖
  • 使用Provider接口
  • 使用接口解耦

5. Dagger2的工作原理是什么?

  1. 编译时处理

    • 通过注解处理器生成代码
    • 构建依赖图
    • 验证依赖关系
    • 生成工厂类
  2. 依赖图构建

    • Component作为依赖的容器
    • Module提供依赖实例
    • Binds绑定接口实现
    • Scope控制实例生命周期

6. Hilt的作用域注解有哪些?

  1. @Singleton:应用级别单例
  2. @ActivityScoped:Activity级别单例
  3. @FragmentScoped:Fragment级别单例
  4. @ViewScoped:View级别单例
  5. @ServiceScoped:Service级别单例

7. 依赖注入框架的性能优化

  1. 编译时优化

    • 使用编译时注解处理
    • 避免反射操作
    • 减少依赖图复杂度
  2. 运行时优化

    • 合理使用作用域
    • 懒加载依赖
    • 避免不必要的实例创建

最佳实践

  1. 选择合适的框架

    • 项目规模
    • 团队熟悉度
    • 性能要求
    • 维护成本
  2. 依赖注入原则

    • 优先使用构造函数注入
    • 避免过度使用Singleton
    • 合理划分Module
    • 保持依赖图简单
  3. 作用域管理

    • 合理使用作用域注解
    • 注意内存泄漏
    • 及时释放资源
  4. 测试策略

    • 编写单元测试
    • 使用测试替身
    • 模拟依赖

注意事项

  1. 性能考虑

    • 避免过度使用依赖注入
    • 注意初始化性能
    • 合理使用懒加载
  2. 内存管理

    • 注意对象生命周期
    • 避免内存泄漏
    • 及时清理资源
  3. 调试与维护

    • 使用调试工具
    • 保持依赖关系清晰
    • 及时更新文档

高级特性

  1. 多绑定
@Module
@InstallIn(SingletonComponent::class)
abstract class AnalyticsModule {
    @Binds
    @IntoSet
    abstract fun bindGoogleAnalytics(analytics: GoogleAnalytics): AnalyticsService
 
    @Binds
    @IntoSet
    abstract fun bindFirebaseAnalytics(analytics: FirebaseAnalytics): AnalyticsService
}
  1. 限定符
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class AuthInterceptorOkHttpClient
 
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @AuthInterceptorOkHttpClient
    @Provides
    fun provideAuthInterceptorOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(AuthInterceptor())
            .build()
    }
}
  1. 子组件
@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 {
    // ...
}