Hilt的使用

添加依赖配置

首先,将 hilt-android-gradle-plugin 插件添加到项目的根级 build.gradle文件中:

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

然后,应用 Gradle 插件并在 app/build.gradle 文件中添加以下依赖项:

...
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'

android {
    ...
}

dependencies {
    implementation "com.google.dagger:hilt-android:2.28-alpha"
    kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
}

注意:同时使用 Hilt 和数据绑定的项目需要 Android Studio 4.0 或更高版本。

Hilt 使用 Java 8 功能。如需在项目中启用 Java 8,请将以下代码添加到 app/build.gradle 文件中:

android {
  ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

注解说明

所有使用 Hilt 的应用都必须包含一个带有 @HiltAndroidApp 注释的 Application

@HiltAndroidApp
class ExampleApplication : Application() { ... }

生成的这一 Hilt 组件会附加到 Application 对象的生命周期,并为其提供依赖项。此外,它也是应用的父组件,这意味着,其他组件可以访问它提供的依赖项。

Application 类中设置了 Hilt 且有了应用级组件后,Hilt 可以为带有 @AndroidEntryPoint 注释的其他 Android 类提供依赖项:

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }

Hilt 目前支持以下 Android 类:
Application(通过使用 @HiltAndroidApp
Activity Fragment View Service BroadcastReceiver

如果您使用 @AndroidEntryPoint 为某个 Android 类添加注释,则还必须为依赖于该类的 Android 类添加注释。例如,如果您为某个 Fragment 添加注释,则还必须为使用该 Fragment 的所有 Activity 添加注释。

@AndroidEntryPoint 会为项目中的每个 Android 类生成一个单独的 Hilt 组件。这些组件可以从它们各自的父类接收依赖项,如组件层次结构中所述。
如需从组件获取依赖项,请使用 @Inject 注释执行字段注入:

@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {

  @Inject lateinit var user: User
  ...
}

定义 Hilt 绑定:

为了执行字段注入,Hilt 需要知道如何从相应组件提供必要依赖项的实例。“绑定”包含将某个类型的实例作为依赖项提供所需的信息。
向 Hilt 提供绑定信息的一种方法是构造函数注入。在某个类的构造函数中使用 @Inject 注释,以告知 Hilt 如何提供该类的实例:

class User @Inject constructor() {
    val name = "唐人"

    override fun toString(): String {
        return name
    }
}

简单示例

@HiltAndroidApp
public class HiltApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
    }
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var user: User
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val tv = findViewById<TextView>(R.id.tv)
        tv.text = user.toString()
    }
}
class User @Inject constructor() {
    val name = "唐人"

    override fun toString(): String {
        return name
    }
}
<application
        android:name=".HiltApp"
        ...
</application>         

注意:凡是具有Hilt注解的类必须是public的,注入的字段不能为私有字段

注意:在 Hilt 对 Android 类的支持方面还要注意以下几点:

  1. Hilt 仅支持扩展 ComponentActivity 的 Activity,如 AppCompatActivity。
  2. Hilt 仅支持扩展 androidx.Fragment 的 Fragment。
  3. Hilt 不支持保留的 Fragment。

对于接口如何注入?

有些类型不能通过构造函数注入。发生这种情况可能有多种原因。例如,您不能通过构造函数注入接口

可以使用 Hilt 模块向 Hilt 提供绑定信息

Hilt 模块是一个带有 @Module 注释的类。与 Dagger 模块一样,它会告知 Hilt 如何提供某些类型的实例。与 Dagger 模块不同的是,您必须使用 @InstallIn 为 Hilt 模块添加注释,以告知 Hilt 每个模块将用在或安装在哪个 Android 类中。

使用 @Binds 注入接口实例

AnalyticsService 为例。如果 AnalyticsService 是一个接口,则您无法通过构造函数注入它,而应向 Hilt 提供绑定信息,方法是在 Hilt 模块内创建一个带有 @Binds 注释的抽象函数。
@Binds 注释会告知 Hilt 在需要提供接口的实例时要使用哪种实现。
带有注释的函数会向 Hilt 提供以下信息:
- 函数返回类型会告知 Hilt 函数提供哪个接口的实例。
- 函数参数会告知 Hilt 要提供哪种实现。

示例:

public class AnalyticsAdapter {
    public final AnalyticsService service;

    @Inject
    public AnalyticsAdapter(AnalyticsService service) {
        this.service = service;
    }
}
public interface AnalyticsService {
    void analyticsMethods();
}
public class AnalyticsServiceImpl implements AnalyticsService {
    @Inject
    public AnalyticsServiceImpl() {
    }

    @Override
    public void analyticsMethods() {
        Log.d("AnalyticsServiceImpl",
                "analyticsMethods(AnalyticsServiceImpl.java:18)");
    }
}
@Module
@InstallIn(ActivityComponent.class)
public abstract class AnalyticsModule {

    /**
     * 使用 @Binds 注入接口实例
     *
     * @param analyticsServiceImpl
     * @return AnalyticsService 
     */
    @Binds
    public abstract AnalyticsService bindAnalyticsService(AnalyticsServiceImpl analyticsServiceImpl);
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var analyticsAdapter: AnalyticsAdapter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        analyticsAdapter.service.analyticsMethods()
    }

Hilt 模块 AnalyticsModule 带有 @InstallIn(ActivityComponent::class) 注释,因为您希望 Hilt 将该依赖项注入 ExampleActivity。此注释意味着,AnalyticsModule 中的所有依赖项都可以在应用的所有 Activity 中使用。

对于第三方库如何注入?

在上面我们知道了接口的注入方式,那么还有一种无法直接通过构造函数注入的那就是第三方库,比如Retrofit、OkhttpClient或者通过Builder构建者模式创建的实例,接下来让我们看看该如何做吧。

使用 @Provides 注入实例

@Module
@InstallIn(ActivityComponent.class)
public class AnalyticsModule {

  @Provides
  public static AnalyticsService provideAnalyticsService(
    // Potential dependencies of this type
  ) {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

为同一类型提供多个绑定?
如果您需要让 Hilt 以依赖项的形式提供同一类型的不同实现,必须向 Hilt 提供多个绑定。您可以使用限定符为同一类型定义多个绑定。

限定符是一种注释,当为某个类型定义了多个绑定时,您可以使用它来标识该类型的特定绑定。

首先,定义要用于为 @Binds@Provides 方法添加注释的限定符@Qualifier

场景:用户分享操作(抽象),有两个具体实现:登录状态、注销状态,如果要实现多个绑定,就需要使用到限定符

//登录状态限定符
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface In {
}

//注销状态
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Out {
}

用户分享抽象:

public interface UserController {
    void share();
} 

具体实现:

public class LoginIn implements UserController {
    @Inject
    public LoginIn() {
    }

    @Override
    public void share() {
        Log.d("LoginIn",
                "share(LoginIn.java:12)转发成功");
    }
}
public class LoginOut implements UserController {
    @Inject
    public LoginOut() {
    }

    @Override
    public void share() {
        Log.d("LoginOut",
                "share(LoginOut.java:12)转发失败");
    }
}

限定符标记:

@Module
@InstallIn(ActivityComponent.class)
public abstract class LoginModule {

    @Binds
    @In
    public abstract UserController providerLoginIn(LoginIn in);

    @Binds
    @Out
    public abstract UserController providerLoginOut(LoginOut out);

}

使用:

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {

 @Inject
 @Out
 UserControllervar userController;
  ...
}

Hilt 中的预定义限定符:

Hilt 提供了一些预定义的限定符。例如,由于您可能需要来自应用或 Activity 的 Context 类,因此 Hilt 提供了 @ApplicationContext@ActivityContext 限定符。

假设本例中的 AnalyticsAdapter类需要 Activity 的上下文。以下代码演示了如何向 AnalyticsAdapter 提供 Activity 上下文:

public class AnalyticsAdapter {
    public final AnalyticsService service;

    @Inject
    public AnalyticsAdapter(@ActivityContext  Context context, AnalyticsService service) {
        this.service = service;
        Log.d("AnalyticsAdapter",
            "AnalyticsAdapter(AnalyticsAdapter.java:19)"+context.getPackageName());
    }
}

组件作用域

默认情况下,Hilt 中的所有绑定都未限定作用域。这意味着,每当应用请求绑定时,Hilt 都会创建所需类型的一个新实例。
在本例中,每当 Hilt 提供 AnalyticsAdapter 作为其他类型的依赖项或通过字段注入提供它(如在 ExampleActivity 中)时,Hilt 都会提供 AnalyticsAdapter 的一个新实例。
不过,Hilt 也允许将绑定的作用域限定为特定组件。Hilt 只为绑定作用域限定到的组件的每个实例创建一次限定作用域的绑定,对该绑定的所有请求共享同一实例。
下表列出了生成的每个组件的作用域注释:

注入器面向的对象Hilt组件作用域
ApplicationApplicationComponent@Singleton
View ModelActivityRetainedComponent@ActivityRetainedScope
ActivityActivityComponent@ActivityScoped
FragmentFragmentComponent@FragmentScoped
ViewViewComponent@ViewScoped
带有 @WithFragmentBindings 注释的 ViewViewWithFragmentComponent@ViewScoped
ServiceServiceComponent@ServiceScoped

在本例中,如果您使用 @ActivityScopedAnalyticsAdapter 的作用域限定为 ActivityComponent,Hilt 会在相应 Activity 的整个生命周期内提供 AnalyticsAdapter 的同一实例:

@ActivityScoped
public class AnalyticsAdapter {

  private final AnalyticsService service;

  @Inject
  AnalyticsAdapter(AnalyticsService service) {
    this.service = service;
  }
  ...
}

假设 AnalyticsService 的内部状态要求每次都使用同一实例 - 不只是在 MainActivity 中,而是在应用中的任何位置。在这种情况下,将 AnalyticsService 的作用域限定为 ApplicationComponent 是一种恰当的做法。结果是,每当组件需要提供 AnalyticsService 的实例时,都会提供同一实例。

以下示例演示了如何将绑定的作用域限定为 Hilt 模块中的某个组件。绑定的作用域必须与其安装到的组件的作用域一致,因此在本例中,您必须将 AnalyticsService 安装在 ApplicationComponent 中,而不是安装在 ActivityComponent 中:

// If AnalyticsService is an interface.
@Module
@InstallIn(ApplicationComponent.class)
public abstract class AnalyticsModule {

  @Singleton
  @Binds
  public abstract AnalyticsService bindAnalyticsService(
    AnalyticsServiceImpl analyticsServiceImpl
  );
}

// If you don't own AnalyticsService.
@Module
@InstallIn(ApplicationComponent.class)
public class AnalyticsModule {

  @Singleton
  @Provides
  public static AnalyticsService provideAnalyticsService() {
      return new Retrofit.Builder()
               .baseUrl("https://example.com")
               .build()
               .create(AnalyticsService.class);
  }
}

组件层次结构

将模块安装到组件后,其绑定就可以用作该组件中其他绑定的依赖项,也可以用作组件层次结构中该组件下的任何子组件中其他绑定的依赖项,这里就实现了Dagger2中的依赖与子组件功能:
在这里插入图片描述

组件默认绑定

每个 Hilt 组件都附带一组默认绑定,Hilt 可以将其作为依赖项注入您自己的自定义绑定。请注意,这些绑定对应于常规 Activity 和 Fragment 类型,而不对应于任何特定子类。这是因为,Hilt 会使用单个 Activity 组件定义来注入所有 Activity。每个 Activity 都有此组件的不同实例

Android 组件默认绑定
ApplicationComponentApplication
ActivityRetainedComponentApplication
ActivityComponentApplicationActivity
FragmentComponentApplicationActivityFragment
ViewComponentApplicationActivityView
ViewWithFragmentComponentApplicationActivityFragmentView
ServiceComponentApplicationService
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴唐人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值