@Inject

        @Inject 注解在 Hilt(基于 Dagger 的依赖注入框架)中是实现依赖注入的核心机制,其作用是告诉框架 “此处需要某个依赖对象”,并让框架自动完成对象的创建和赋值。在 Kotlin 中,它主要用于构造函数注入属性注入

一、@Inject 在构造函数中的作用:定义依赖来源

        在 Kotlin 中,@Inject 最常标注在类的主构造函数上,用于声明该类的依赖项,让框架知道 “这个类需要哪些对象才能初始化”。

class UserRepository @Inject constructor(
    private val apiService: ApiService,  // 网络服务依赖
    private val database: AppDatabase    // 数据库依赖
) {
    // 使用依赖执行逻辑
    fun fetchUser() = apiService.getUser()
}

二、@Inject 在属性中的作用:简化成员变量注入

        在 Kotlin 中,@Inject 也可直接标注在类的属性上(即 “属性注入”),用于为已有的成员变量赋值。这种方式无需修改构造函数,适合在无法修改构造函数(如 Android 组件)的场景中使用。

@AndroidEntryPoint  // 启用 Hilt 注入
class MainActivity : AppCompatActivity() {
    @Inject lateinit var userRepository: UserRepository  // 属性注入
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        userRepository.fetchUser()  // 直接使用注入的依赖
    }
}
场景 构造函数注入 属性注入
适用场景 自定义类(如 Repository、Service) Android 组件(Activity、Fragment)
依赖必要性 依赖为类的核心功能(必须存在) 依赖为可选功能或辅助功能
可测试性 更优(依赖通过构造函数传入,方便 Mock) 次之(需通过反射或框架机制注入)
代码简洁性 需修改构造函数,适合新建类 无需修改构造函数,适合已有类的改造

@BindingAdapter

         @BindingAdapter是 Android Data Binding 库中的注解,用于将自定义属性绑定到视图,简化视图与数据的交互逻辑。扩展 XML 属性(为视图添加自定义属性(如 imageUrl),封装视图逻辑(将复杂操作(如图片加载、事件监听)封装在 Adapter 中),支持多属性绑定(监听多个属性变化,触发视图更新),类型转换(自动处理数据类型与视图属性的适配)。

代码示例:

// 1. 图片加载(带错误处理)
@BindingAdapter("imageUrl", "errorRes", requireAll = false)
fun ImageView.loadImage(url: String?, errorRes: Int = R.drawable.ic_error) {
    if (url.isNullOrEmpty()) {
        setImageResource(errorRes)
        return
    }
    Glide.with(context)
        .load(url)
        .error(errorRes)
        .into(this)
}

// 2. 文本格式化(支持 String/Int 类型)
@BindingAdapter("formattedText")
fun TextView.setFormattedText(value: Any?) {
    text = when (value) {
        is String -> value
        is Int -> context.getString(R.string.number_format, value)
        else -> ""
    }
}

// 3. 点击事件绑定(带防抖处理)
@BindingAdapter("safeClick")
fun View.setSafeClickListener(listener: () -> Unit) {
    setOnClickListener {
        // 防抖实现(200ms内只响应一次)
        val currentTime = System.currentTimeMillis()
        if (currentTime - lastClickTime > 200) {
            lastClickTime = currentTime
            listener()
        }
    }
}
private var lastClickTime: Long = 0

// 4. 多属性组合(文本+颜色)
@BindingAdapter("text", "textColor", requireAll = false)
fun TextView.setTextAndColor(text: String?, color: Int?) {
    this.text = text ?: ""
    color?.let { setTextColor(it) }
}

// 5. 双向绑定示例(自定义属性的双向数据流动)
@BindingAdapter("android:text")
fun EditText.bindText(text: String?) {
    if (this.text.toString() != text) {
        setText(text)
    }
}

@InverseBindingAdapter(attribute = "android:text")
fun EditText.getText(): String = text.toString()

xml使用 

<!-- 布局文件中使用自定义属性 -->
<ImageView
    app:imageUrl="@{viewModel.avatarUrl}"
    app:errorRes="@drawable/ic_placeholder"
    ... />

<TextView
    app:formattedText="@{viewModel.count}"
    app:textColor="@{viewModel.isActive ? @color/primary : @color/gray}"
    ... />

<Button
    app:safeClick="@{() -> viewModel.onSubmit()}"
    ... />

<EditText
    android:text="@={viewModel.inputText}"
    ... />

@Singleton 

        @Singleton是依赖注入框架中用于标识单例模式的注解,其核心作用是确保被标注的类在应用全局范围内仅存在一个实例,并由框架自动管理实例的创建与生命周期。比如在 Android 开发中,借助 Dagger 或 Hilt 框架时,给提供实例的方法添加@Singleton注解,配合@InstallIn(SingletonComponent::class)等配置,就能让框架生成应用级的单例对象,像网络请求所需的 Retrofit 实例或数据库连接管理器等,都可通过这种方式实现全局唯一访问。 

例如,在 Android 开发中使用 Hilt 框架时,可通过以下方式定义单例组件:

// 定义单例组件(Hilt 方式)
@Module
@InstallIn(SingletonComponent::class)  // 应用级单例
object NetworkModule {

    @Singleton  // 标记为单例
    @Provides
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Singleton
    @Provides
    fun provideOkHttpClient(): OkHttpClient {
        return OkHttpClient.Builder()
            .addInterceptor(HttpLoggingInterceptor())
            .build()
    }
}

单例注入与使用

定义好单例后,可在 ViewModel 或 Activity 中直接注入使用:

// 在 ViewModel 中注入单例
class MyViewModel @Inject constructor(
    private val retrofit: Retrofit  // 自动注入单例实例
) : ViewModel() {
    // 使用注入的 Retrofit 实例发起网络请求
    fun fetchData() {
        val apiService = retrofit.create(ApiService::class.java)
        // ...
    }
}

// 在 Activity 中注入单例
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    
    @Inject lateinit var okHttpClient: OkHttpClient  // 自动注入单例
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView{
            // 使用注入的 OkHttpClient 实例
            val request = Request.Builder()
                .url("https://example.com")
                .build()
            
            okHttpClient.newCall(request).enqueue(...)
        }
    }
}

@AndroidEntryPoint和@HiltViewModel

        @AndroidEntryPoint 是一个用于 Android 组件(如 Activity、Fragment、Service 等)的注解,标记该组件为 Hilt 的入口点,使其能够使用依赖注入。

        Hilt 会为每个被注解的组件生成一个依赖容器,该容器继承自应用的根组件,从而支持在组件中使用 @Inject 注解注入依赖项(如 ViewModel、Repository 等)。同时,Hilt 会自动处理组件与依赖的生命周期绑定。

        例如,在一个 Activity 中使用 @AndroidEntryPoint 注解后,就可以直接通过 by viewModels() 注入 ViewModel。

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    // 注入依赖(如 ViewModel)
    private val viewModel: MyViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 使用注入的依赖
        viewModel.getData()
    }
}

        @HiltViewModel 是专门用于 ViewModel 的注解,标记该 ViewModel 可由 Hilt 自动创建并注入依赖。

        使用这个注解后,无需手动实现 ViewModelProvider.Factory,Hilt 会自动生成工厂类。ViewModel 的构造函数中可以直接注入依赖(如 Repository、UseCase 等),并且 Hilt 会确保 ViewModel 的生命周期与 Activity/Fragment 正确绑定。

        例如,一个 ViewModel 类使用 @HiltViewModel 注解后,其构造函数中可以注入 Repository 实例,而无需手动创建 ViewModel 工厂。

@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: MyRepository
) : ViewModel() {
    // 使用注入的依赖执行操作
    fun getData() = repository.fetchData()
}

        二者的关系是:@AndroidEntryPoint 是使用 Hilt 的前提条件,只有被该注解标记的组件才能使用依赖注入;@HiltViewModel 是在 @AndroidEntryPoint 的基础上,进一步简化 ViewModel 的依赖注入。在实际开发中,通常需要先在 Application 类上添加 @HiltAndroidApp 注解,然后在 Activity/Fragment 上使用 @AndroidEntryPoint,最后在 ViewModel 上使用 @HiltViewModel,这样就可以实现 ViewModel 的依赖注入,减少样板代码,提高代码的可测试性和可维护性。

  • 统一的依赖注入解决方案:Hilt 为 Android 中的所有常见组件(如 Activity、Fragment、Service 等)提供了标准化的注入方案。开发者无需再为每个组件单独配置依赖,从而消除了配置的复杂性。

  • 自动化的生命周期管理:Hilt 会自动处理依赖的生命周期,并将其与 Android 组件的生命周期绑定。例如,一个 ActivityScoped 的依赖会在 Activity 创建时被创建,并在 Activity 销毁时自动销毁,这有效防止了内存泄漏。

  • 简化 ViewModel 注入:通过 @HiltViewModel@AndroidEntryPoint 注解,Hilt 彻底消除了手动编写 ViewModelProvider.Factory 的繁琐工作,让 ViewModel 的创建和依赖注入变得非常简单。

  • 提高可测试性:依赖注入的核心优势之一就是提高代码的可测试性。Hilt 强制你通过构造函数注入依赖,这使得在单元测试中更容易对依赖进行 Mock 或 Stub,从而实现独立的组件测试。

  • 减少样板代码:Hilt 最大的意义就是通过注解和代码生成,将 Dagger 复杂且重复的配置(如 @Component@Subcomponent)隐藏起来,让开发者用更少的代码实现强大的依赖注入功能。

Hilt 的工作原理(编译时与运行时)

Hilt 的核心工作原理是在编译时生成代码,而不是在运行时通过反射来注入依赖。这确保了应用的性能和稳定性。

1. 编译时 (Compile-Time) 的工作流程

这是 Hilt 最重要的阶段,所有的魔法都在这里发生。

  • 扫描注解:Hilt 的注解处理器会扫描你的整个项目,寻找所有 Hilt 相关的注解,比如 @HiltAndroidApp, @AndroidEntryPoint, @HiltViewModel, @Inject, @Module, 和 @Provides

  • 生成组件和工厂:基于这些注解,注解处理器会自动生成所有必要的 Dagger 类,包括:

    • Dagger 组件(Components):Hilt 为每个 @AndroidEntryPoint 组件(如 ActivityFragment)生成一个 Dagger 组件。这些组件负责管理依赖的生命周期和提供依赖实例。

    • 注入工厂:Hilt 会为所有用 @Inject 构造函数注解的类生成一个工厂。当需要创建这个类的实例时,Hilt 会使用这个工厂来提供依赖并创建对象。

    • ViewModel 工厂:对于被 @HiltViewModel 注解的 ViewModel,Hilt 会自动生成一个 ViewModelProvider.Factory 的实现,这个工厂会处理 ViewModel 的创建及其依赖的注入。

  • 构建依赖图:在代码生成过程中,Hilt 会构建一个完整的依赖图。它会检查所有 @Inject 标记的依赖,并确保在模块中能找到对应的 @Provides 函数来提供这些依赖。如果存在循环依赖或无法找到依赖的情况,编译器会立即报错,而不是等到运行时才暴露问题。

2. 运行时 (Runtime) 的工作流程

在应用运行时,Hilt 几乎不进行额外的工作,因为它的大部分工作已经在编译时完成了。

  • 入口点初始化:当你启动应用时,Hilt 会创建一个应用级别的单例容器,即 SingletonComponent。它会执行所有标记了 @Singleton@Provides 的代码,准备好全局唯一的依赖实例。

  • 组件生命周期绑定:当一个被 @AndroidEntryPoint 注解的组件(如 MainActivity)被创建时,Hilt 会自动创建一个与该组件生命周期绑定的子组件(例如 ActivityComponent)。这个组件可以访问其父组件(SingletonComponent)中提供的所有依赖。

  • 属性注入:在组件的生命周期方法(如 onCreate)被调用之前,Hilt 会通过编译时生成的代码,执行属性注入。它会找到所有用 @Inject 注解的属性,并从相应的组件中获取实例,然后赋值给这些属性。

  • ViewModel 注入:当你使用 by viewModels() 注入 ViewModel 时,Hilt 会使用编译时生成的 ViewModel 工厂来创建 ViewModel 实例,并根据 ViewModel 构造函数中的 @Inject 声明,自动注入所需的依赖。

总而言之,Hilt 的核心工作机制是编译时代码生成,这使得它能够提供强大的依赖注入功能,同时避免了反射带来的性能开销。它通过将 Dagger 复杂的配置抽象化,让开发者能够以更简单、更“Android”的方式实现依赖注入,从而显著提升开发效率和代码质量。

简单扩展:

        @Volatile 的核心价值在于解决多线程环境下的可见性问题指令重排序问题,但它是一种 “轻量级” 的同步机制,无法保证原子性。在实际开发中,它适用于状态标记、单例模式等场景,而复杂的线程安全需求仍需结合 synchronized、原子类或并发工具类实现。

        @JvmStatic是 Kotlin 中用于 Java 互操作性的注解,主要作用是让 Kotlin 代码中定义的成员在编译为 Java 字节码后,能以静态成员的形式被 Java 代码直接调用。在 Kotlin 中,类的成员函数、属性默认是实例成员(需通过对象实例调用),而@JvmStatic注解可将其转换为 Java 层面的静态成员,避免 Java 代码因 Kotlin 语法特性产生调用不便。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐