一、Hilt 干依靠注入的

Hilt是干嘛的

Hilt,一个依靠注入结构,谷歌搞出来的,依据Dagger,可是愈加简练易用。

什么是依靠注入 (Dependency Injection)

依靠注入是一种规划形式。首要就是解耦!解耦!解耦!。

依靠注入是一种规划形式,它经过在目标之间传递依靠联系,使得目标能够更灵敏、可测验和可保护。在Android开发中,依靠注入能够协助咱们办理宽和耦运用程序中的各种组件,如Activity、Fragment、ViewModel、Repository等。

既生Dagger 何生 Hilt ?

安卓中的Hilt是一种用于依靠注入(Dependency Injection)的库。Hilt是由Google开发的,它是依据Dagger的一个扩展库,旨在简化Android运用程序中的依靠注入进程。

Hilt供给了一套注解和生成代码,用于在Android运用程序中主动完结依靠注入的装备。它简化了Dagger的运用,减少了样板代码的编写,提高了开发效率。

Hilt还供给了一些特定于Android的功能,如对Android组件的注入支撑,以及在不同生命周期中办理依靠联系的才能

Hilt 大约怎么用

  • Application 注解下
  • 在需求的符号为依靠注入的Activity、Fragment注解下 @AndroidEntryPoint

运用Hilt进行依靠注入时,首要需求在运用程序的Application类上增加@HiltAndroidApp注解,以奉告Hilt该运用程序将运用Hilt进行依靠注入。然后,能够在其他组件(如Activity、Fragment)中运用@AndroidEntryPoint注解来符号需求进行依靠注入的当地。Hilt会依据注解生成必要的代码,在运行时完结依靠注入。

Hilt还供给了一些其他的注解,用于符号和装备依靠联系。例如,

  • 能够运用@Inject注解来符号需求注入的依靠项,
  • 运用@Module注解来符号用于供给依靠项的模块类,
  • 运用@Provides注解来符号供给依靠项的办法等。

二、什么是TM的依靠注入,给个比方

比方咱们创立一辆轿车,轿车需求引擎。

  • 假如不必依靠注入,创立轿车,轿车内部还要创立引擎,这样就 耦合 耦合 偶然
  • 假如运用依靠注入,创立轿车不需求创立引擎,而是从外部将Engine目标传递Car类,这样就 解耦 解耦 解耦

不运用依靠注入,轿车内部创立引擎,耦合耦合耦合,

class Engine {
    void start() {
        System.out.println("Engine started");
    }
}
class Car {
    private Engine engine;
    Car() {
        engine = new Engine();  // Car类自己创立Engine目标
    }
    void drive() {
        engine.start();
        System.out.println("Car is driving");
    }
}

运用依靠注入,传递引擎目标给轿车,轿车内部不创立引擎目标,解耦解耦解耦

class Car {
    private Engine engine;
    Car(Engine engine) {  // Engine目标经过结构函数注入
        this.engine = engine;
    }
    void drive() {
        engine.start();
        System.out.println("Car is driving");
    }
}

现在,咱们能够在创立Car目标时,把一个Engine目标传递给它:

Engine engine = new Engine();
Car car = new Car(engine);
car.drive();

上面的比照。尽管简略,可是是个挺好的比方

“Engine目标经过结构函数注入”是指咱们经过Car类的结构函数将Engine目标传递给Car类。这是一种依靠注入的办法,被称为结构函数注入。

在这个比方中,Car类需求一个Engine目标来作业。在没有运用依靠注入的情况下,Car类会自己创立一个Engine目标。但这样做的问题是,Car类与Engine类严密耦合在一起,假如咱们想要更换一个不同类型的Engine,或者咱们想要在测验时运用一个模仿的Engine,咱们就需求修正Car类的代码。

当咱们运用结构函数注入时,咱们不再在Car类内部创立Engine目标,而是在创立Car目标时,从外部将Engine目标传递给Car类。这样,Car类就不再依靠于Engine类的详细完结,咱们能够很简单地更换不同类型的Engine,或者在测验时运用一个模仿的Engine,而不需求修正Car类的代码。

也许你会说,不就是传个参吗,说得这么费事????
某种程度上,你能够这么说,其时,这个玩意,能放大了玩。


依靠注入,可不仅仅是高级的传参 (李姐会了解)

人家,可传参,牛逼多了。

    1. Activity和Fragment:在Android开发中,Activity和Fragment常常需求访问一些共享的资源或服务,例如网络恳求Retrofit数据库访问、ViewModel等。假如没有依靠注入,咱们或许需求在每个Activity或Fragment中手动创立这些目标,这会导致代码重复,而且使得Activity和Fragment与这些服务严密耦合,难以进行单元测验。经过运用依靠注入,咱们能够在一个统一的当地装备这些服务,然后在需求的当地主动注入,这使得代码愈加明晰,更简单测验。
    1. 主App和Module:在一个模块化的运用中,不同的模块或许需求访问一些共享的服务。假如没有依靠注入,咱们或许需求经过一些复杂的办法来让这些模块获取到这些服务,这会使得代码变得复杂,而且难以办理。经过运用依靠注入,咱们能够在主App中装备这些服务,然后在各个模块中主动注入,这使得代码愈加明晰,更简单办理。
  • 3、办理单例依靠项:在 Android 运用程序中,有一些依靠项是单例的,如数据库、网络客户端等。运用 Hilt 能够更轻松地办理这些单例依靠项,同时避免了手动办理单例依靠项的复杂性。

这个说是不是不会觉得是简答传参了!

依靠注入使得咱们的代码愈加模块化,每个类都只关注自己的职责,不需求关心其依靠目标的创立和办理。这使得咱们的代码更简单重用。


三、Hilt的一些常见注解

在开端app比方之前,还需求需求将一些注解前面阐明一下。

Hilt运用了一系列的注解来简化依靠注入的进程。以下是一些最常用的Hilt注解:

@HiltAndroidApp

  1. @HiltAndroidApp: 这个注解用于Application类,它会触发Hilt的代码生成,包含一个运用等级的组件,这个组件能够为其他Hilt组件(如Activity组件)供给依靠。
@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint

  1. @AndroidEntryPoint: 这个注解用于Android组件,如Activity、Fragment、Service等,它告知Hilt这些组件能够接纳依靠注入。
@AndroidEntryPoint class MainActivity : AppCompatActivity() { @Inject lateinit var someClass: SomeClass }

@Inject

  1. @Inject: 这个注解用于字段、结构函数或办法,告知Hilt需求注入依靠。关于字段,Hilt会主动注入相应的依靠;关于结构函数,Hilt会运用它来创立类的实例;关于办法,Hilt会在注入后调用它。
class SomeClass @Inject constructor(private val someDependency: SomeDependency)

@Module

  1. @Module: 这个注解用于目标,这些目标供给了一系列的依靠供给办法。这些办法用@Provides注解符号,Hilt会在需求时调用它们。

@Provides

  1. @Provides: 这个注解用于在@Module注解的类中的办法,这些办法供给了依靠的实例。Hilt会在需求时调用这些办法。
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Singleton
    @Provides
    fun provideSomeDependency(): SomeDependency {
        return SomeDependency()
    }
}

@InstallIn

  1. @InstallIn: 这个注解用于@Module注解的类,指定这个模块装置在哪个Hilt组件中。

@Singleton

  1. @Singleton: 这个注解用于@Provides注解的办法或@Inject注解的结构函数,告知Hilt供给的依靠是单例的。
@Singleton
class SomeSingletonClass @Inject constructor()

@ViewModelInject

  1. @ViewModelInject: 这个注解用于ViewModel的结构函数,告知Hilt怎么创立ViewModel的实例。
class MyViewModel @ViewModelInject constructor(private val repository: Repository) : ViewModel()

@Assisted

  1. @Assisted:用于符号 ViewModel 的结构函数参数,以便在运用 assisted injection 时注入这些参数。

@AssistedInject

  1. @AssistedInject:用于符号运用 assisted injection 创立的 ViewModel 的结构函数。

这些注解使得Hilt能够主动处理依靠的创立和注入,大大简化了依靠注入的进程。

注解 描绘
@HiltAndroidApp 用于Application类,触发Hilt的代码生成,包含一个运用等级的组件,这个组件能够为其他Hilt组件(如Activity组件)供给依靠。
@AndroidEntryPoint 用于Android组件,如Activity、Fragment、Service等,告知Hilt这些组件能够接纳依靠注入。
@Inject 用于字段结构函数办法,告知Hilt需求注入依靠。关于字段,Hilt会主动注入相应的依靠关于结构函数,Hilt会运用它来创立类的实例关于办法,Hilt会在注入后调用它
@Module 用于目标,这些目标供给了一系列的依靠供给办法。这些办法用@Provides注解符号,Hilt会在需求时调用它们。
@Provides 用于在@Module注解的类中的办法,这些办法供给了依靠的实例。Hilt会在需求时调用这些办法。
@InstallIn 用于@Module注解的类,指定这个模块装置在哪个Hilt组件中。
@Singleton 用于@Provides注解的办法或@Inject注解的结构函数,告知Hilt供给的依靠是单例的。
@ViewModelInject 用于ViewModel的结构函数,告知Hilt怎么创立ViewModel的实例。
  • @Module@Provides:这两个注解一般一起运用,界说在一个类中,这个类供给了一系列的依靠供给办法。这些办法用@Provides注解符号,Hilt会在需求时调用它们。

四、上个App

四、1、hilt预备

app目录下 build.gradle


plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    // 增加kapt注解处理器
    id 'kotlin-kapt'
    // hilt所需
    id 'com.google.dagger.hilt.android'
}
dependencies {
    implementation 'com.google.dagger:hilt-android:2.44'
    kapt 'com.google.dagger:hilt-android-compiler:2.44'
}

项目等级build

plugins {
    id 'com.android.application' version '8.0.0' apply false
    id 'com.android.library' version '8.0.0' apply false
    id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
    // hilt
    id "com.google.dagger.hilt.android"  version "2.44" apply false
}

这里需求阐明一下,比较新的版本是如上这么写。

假如是比较旧的android studio,则是classpath的写法。比方:

buildscript {
    dependencies {
        classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44'
    }
}

以上,装备完结。

四.2、轿车和引擎的比方

// 运用 @HiltAndroidApp 注解符号一个运用,这是运用 Hilt 进行依靠注入的进口点。
// Hilt 需求一个进口点来供给依靠,这一般是 AndroidApplication 类。
// 这个注解会触发 Hilt 的代码生成,生成的代码包含一个运用等级的组件,这个组件能够为其他 Hilt 组件(如 Activity 组件)供给依靠。
@HiltAndroidApp
class MyApplication : Application() {
    // 这个类一般不需求写任何代码,除非你需求在运用启动时执行一些操作。
}
android:name=".MyApplication"

Engine


// 运用 @Inject 注解符号结构函数,告知 Hilt 怎么创立这个类的实例。
// 当 Hilt 需求供给一个 Engine 实例时,它会调用这个结构函数。
// 在这个比方中,Engine 类没有任何依靠,所以结构函数没有任何参数。
class Engine @Inject constructor() {
    fun start() {
        println("Engine started")
    }
}

Car

// 运用 @Inject 注解符号结构函数,告知 Hilt 怎么创立这个类的实例。
// 当 Hilt 需求供给一个 Car 实例时,它会调用这个结构函数。
// 在这个比方中,Car 类依靠于 Engine 类,所以结构函数有一个 Engine 类型的参数。
// Hilt 会查找供给 Engine 实例的办法(在这个比方中,就是 Engine 类的结构函数),然后用供给的 Engine 实例来创立 Car 实例。
class Car @Inject constructor(private val engine: Engine) {
    fun drive() {
        engine.start()
        println("Car is driving")
    }
}

Activity

/**
 *
 *  运用 @AndroidEntryPoint 注解符号一个 Android 组件,告知 Hilt 这个组件能够接纳依靠注入。
 *  Hilt 支撑多种 Android 组件,包含 Activity、Fragment、View、Service、BroadcastReceiver 等。
 *  当一个组件被符号为 @AndroidEntryPoint,Hilt 会为这个组件生成一个组件类(在这个比方中,是 MainActivityComponent)。
 *  这个组件类是用来供给依靠的,它是由 Hilt 主动生成的,你无需手动创立。
 */
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var car: Car
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Use the car
        car.drive()
    }
}

运行结果:

System.out: Engine started
System.out: Car is driving

嗯。假如仅仅这么用,那么的确平平无奇,似乎是一个高级传参。
可是,假如仅仅这样,那咱们还H个什么H。

可是,这是一个挺好的小比方。


五、实际开发中一些常用的场景

SharedPreferences的注入

// 创立一个Hilt模块,用于供给SharedPreferences实例
@Module
@InstallIn(SingletonComponent::class)
object SharedPreferencesModule {
    @Provides
    fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
        return context.getSharedPreferences("pref_name", Context.MODE_PRIVATE)
    }
}
// 在Activity中,你能够运用@Inject注解来恳求一个SharedPreferences实例。
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var sharedPreferences: SharedPreferences
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 运用SharedPreferences
        val editor = sharedPreferences.edit()
        editor.putString("key", "value")
        editor.apply()
    }
}

上述代码中,@AndroidEntryPoint是一个Hilt注解,用于指示Hilt应该为这个Activity供给依靠。然后,经过在sharedPreferences字段上增加@Inject注解,Hilt就会知道它需求为这个字段供给一个SharedPreferences实例。这个实例是由SharedPreferencesModule模块中的provideSharedPreferences办法供给的。

留意,你能够随时运用sharedPreferences字段,Hilt会保证在onCreate办法调用时,它现已被正确初始化

你也能够将这种形式运用于其他的比方,例如网络服务、视图模型、数据库和数据仓库等。

多模块项目

在多模块项目中,Hilt能够协助你更好地办理跨模块的依靠。例如,假设你有一个data模块和一个app模块,data模块供给了一个Repository类,app模块需求运用这个Repository

首要,在data模块中,你界说了一个Repository类,并用@Inject注解符号其结构函数:

// 在 data 模块中
class Repository @Inject constructor() {
    // ...
}

然后,在app模块中,你能够直接在需求的当地注入Repository

// 在 app 模块中
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var repository: Repository
    // ...
}

在这个比方中,你不需求在app模块中手动创立Repository的实例,Hilt会主动为你处理。


再来一个依靠办理的比方

认为简化的Retrofit为比方

假设咱们有一个NetworkModule,它供给了一个Retrofit实例:

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Singleton
    @Provides
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .build()
    }
}

在这个比方中,NetworkModule是一个Hilt模块,它在运用等级的组件(SingletonComponent)中供给了一个Retrofit实例。这个Retrofit实例是一个单例,因为咱们运用了@Singleton注解。

然后,咱们有一个Repository类,它需求这个Retrofit实例来建议网络恳求:

class Repository @Inject constructor(private val retrofit: Retrofit) {
    // ...
}

在这个比方中,Repository类经过结构函数注入获取了Retrofit实例。咱们不需求手动创立Retrofit实例,Hilt会主动为咱们处理。

最后,咱们有一个ViewModel,它需求这个Repository来获取数据:

class MyViewModel @ViewModelInject constructor(private val repository: Repository) : ViewModel() {
    // ...
}

在这个比方中,MyViewModel经过@ViewModelInject注解获取了Repository实例。咱们不需求手动创立Repository实例,Hilt会主动为咱们处理。

这就是一个典型的依靠链:MyViewModel依靠于RepositoryRepository依靠于Retrofit。经过Hilt,咱们能够轻松地办理这个依靠链,而无需手动创立和办理每个依靠。这使得代码愈加明晰和直观,也使得新成员更简单了解项目的结构。


ViewModel注入

// Hilt供给了一个HiltViewModel注解,它答应你在ViewModel的结构函数中运用@Inject。
@HiltViewModel
class MyViewModel @Inject constructor(private val userRepository: UserRepository) : ViewModel() {
    // ...
}
// 在Activity或Fragment中,你能够运用由Hilt供给的ViewModel实例。
@AndroidEntryPoint
class MyActivity : AppCompatActivity() {
    private val viewModel: MyViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Use viewModel here
    }
}

数据库的注入

// 创立一个Hilt模块,用于供给Room数据库实例
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {
    @Provides
    fun provideDatabase(@ApplicationContext context: Context): MyDatabase {
        return Room.databaseBuilder(
            context,
            MyDatabase::class.java, "database-name"
        ).build()
    }
    @Provides
    fun provideUserDao(database: MyDatabase): UserDao {
        return database.userDao()
    }
}
// 在需求UserDao的当地,运用@Inject注解来恳求一个UserDao实例。
class UserRepository @Inject constructor(private val userDao: UserDao) {
    // ...
}

不是每一个Activity都需求依靠注入,假如这个activity只在当前页面运用,那么没必要依靠注入。


Hilt篇,大约就到这里吧。