深入剖析 Android Hilt 的模块配置与初始化模块
一、引言
在 Android 开发中,依赖注入(Dependency Injection,简称 DI)是一种非常重要的设计模式,它可以帮助开发者更好地管理组件之间的依赖关系,提高代码的可测试性、可维护性和可扩展性。Hilt 是 Google 推出的一款用于 Android 的依赖注入框架,它基于 Dagger 构建,提供了更简单、更高效的依赖注入解决方案。
本文将深入分析 Android Hilt 的模块配置与初始化模块,从基本概念入手,逐步介绍其实现原理和源码细节。通过源码级别的分析,我们可以更好地理解 Hilt 是如何工作的,以及如何正确地使用它来管理我们的依赖关系。
二、依赖注入基础回顾
2.1 什么是依赖注入
依赖注入是一种设计模式,它允许我们将对象的依赖关系从对象本身中分离出来,通过外部的方式将依赖注入到对象中。这样做的好处是可以提高代码的可测试性和可维护性,因为我们可以更容易地替换和管理对象的依赖。
以下是一个简单的 Java 示例,展示了依赖注入的基本概念:
java
java
// 定义一个接口
interface Logger {
void log(String message);
}
// 实现 Logger 接口
class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("Console Log: " + message);
}
}
// 定义一个需要依赖 Logger 的类
class UserService {
private final Logger logger;
// 通过构造函数注入依赖
public UserService(Logger logger) {
this.logger = logger;
}
public void createUser(String username) {
// 使用注入的 Logger 进行日志记录
logger.log("Creating user: " + username);
}
}
// 主类,演示依赖注入的使用
public class Main {
public static void main(String[] args) {
// 创建 Logger 实例
Logger logger = new ConsoleLogger();
// 创建 UserService 实例,并注入 Logger
UserService userService = new UserService(logger);
// 调用 UserService 的方法
userService.createUser("JohnDoe");
}
}
在这个示例中,UserService
类依赖于 Logger
接口,通过构造函数将 Logger
实例注入到 UserService
中。这样,UserService
类就不需要自己创建 Logger
实例,而是由外部提供,从而实现了依赖的分离。
2.2 依赖注入的优点
- 可测试性 :通过依赖注入,我们可以更容易地替换对象的依赖,从而方便进行单元测试。例如,在测试
UserService
类时,我们可以注入一个模拟的Logger
实例,而不是使用实际的ConsoleLogger
实例。 - 可维护性 :依赖注入使得代码的依赖关系更加清晰,我们可以更容易地理解和修改代码。当需要更换
Logger
实现时,只需要在注入的地方进行修改,而不需要修改UserService
类的内部代码。 - 可扩展性 :依赖注入可以方便地添加新的依赖或替换现有的依赖,从而提高代码的可扩展性。例如,我们可以轻松地添加一个新的
FileLogger
实现,并将其注入到UserService
中。
三、Hilt 简介
3.1 Hilt 的特点
Hilt 是 Google 为 Android 开发提供的一款依赖注入框架,它基于 Dagger 构建,具有以下特点:
- 简单易用:Hilt 提供了一系列的注解和 API,使得依赖注入的配置和使用变得非常简单。开发者只需要使用注解来标记依赖的提供者和使用者,Hilt 会自动处理依赖的注入过程。
- 集成度高 :Hilt 与 Android 组件(如
Activity
、Fragment
、Service
等)深度集成,开发者可以直接在这些组件中使用依赖注入,无需手动管理依赖的生命周期。 - 性能优化:Hilt 在编译时生成代码,避免了运行时的反射开销,从而提高了应用的性能。
3.2 Hilt 的基本概念
- 组件(Component) :组件是 Hilt 中的核心概念,它负责管理依赖的生命周期和提供依赖的实例。Hilt 提供了一些预定义的组件,如
SingletonComponent
、ActivityRetainedComponent
、ActivityComponent
等,每个组件都有自己的生命周期和作用域。 - 模块(Module) :模块是用于提供依赖的类,通过
@Module
注解标记。模块中可以定义多个提供依赖的方法,这些方法使用@Provides
注解标记。 - 注入点(Injection Point) :注入点是指需要依赖的地方,通常是类的构造函数、字段或方法。通过
@Inject
注解标记注入点,Hilt 会自动将依赖注入到这些地方。
四、Hilt 的模块配置
4.1 模块的定义
在 Hilt 中,模块是用于提供依赖的类,通过 @Module
注解标记。以下是一个简单的 Hilt 模块的示例:
java
java
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(SingletonComponent.class)
public class AppModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
public MyService provideMyService() {
// 返回一个 MyService 实例
return new MyService();
}
}
在这个示例中,AppModule
类是一个 Hilt 模块,使用 @Module
注解标记。provideMyService
方法使用 @Provides
注解标记,用于提供 MyService
实例。@Singleton
注解指定该依赖的作用域为单例,即整个应用中只有一个 MyService
实例。@InstallIn(SingletonComponent.class)
注解指定该模块要安装到 SingletonComponent
中,SingletonComponent
是 Hilt 提供的一个全局单例组件,其生命周期与应用的生命周期相同。
4.2 模块的安装
模块需要安装到特定的组件中才能生效。在上面的示例中,我们使用 @InstallIn(SingletonComponent.class)
注解将 AppModule
安装到 SingletonComponent
中。Hilt 提供了多个预定义的组件,每个组件都有自己的生命周期和作用域,以下是一些常见的组件及其作用:
- SingletonComponent:全局单例组件,其生命周期与应用的生命周期相同。适用于提供全局单例的依赖,如网络服务、数据库服务等。
- ActivityRetainedComponent :Activity 保留组件,其生命周期与
ViewModel
相同。适用于提供在 Activity 重建时需要保留的依赖。 - ActivityComponent:Activity 组件,其生命周期与 Activity 相同。适用于提供与 Activity 相关的依赖,如 Activity 的布局管理器、资源管理器等。
- FragmentComponent:Fragment 组件,其生命周期与 Fragment 相同。适用于提供与 Fragment 相关的依赖,如 Fragment 的视图模型、数据加载器等。
4.3 提供不同类型的依赖
4.3.1 提供具体类的实例
java
java
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(SingletonComponent.class)
public class AppModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
public MyService provideMyService() {
// 返回一个 MyService 实例
return new MyService();
}
}
在这个示例中,provideMyService
方法提供了一个 MyService
类的实例。
4.3.2 提供接口的实现
java
java
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
// 定义一个接口
interface MyInterface {
void doSomething();
}
// 实现 MyInterface 接口
class MyInterfaceImpl implements MyInterface {
@Override
public void doSomething() {
System.out.println("Doing something...");
}
}
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(SingletonComponent.class)
public class AppModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
public MyInterface provideMyInterface() {
// 返回一个 MyInterfaceImpl 实例
return new MyInterfaceImpl();
}
}
在这个示例中,provideMyInterface
方法提供了一个 MyInterface
接口的实现 MyInterfaceImpl
实例。
4.3.3 提供有依赖的实例
java
java
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
// 定义一个依赖类
class Dependency {
public Dependency() {
System.out.println("Dependency created");
}
}
// 定义一个需要依赖 Dependency 的类
class MyService {
private final Dependency dependency;
// 通过构造函数注入依赖
public MyService(Dependency dependency) {
this.dependency = dependency;
}
public void doWork() {
System.out.println("Doing work with dependency");
}
}
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(SingletonComponent.class)
public class AppModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
public Dependency provideDependency() {
// 返回一个 Dependency 实例
return new Dependency();
}
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
public MyService provideMyService(Dependency dependency) {
// 返回一个 MyService 实例,并注入 Dependency
return new MyService(dependency);
}
}
在这个示例中,provideMyService
方法提供了一个 MyService
实例,该实例依赖于 Dependency
实例。provideDependency
方法提供了 Dependency
实例,Hilt 会自动将 Dependency
实例注入到 provideMyService
方法中。
4.4 模块的依赖关系
模块之间可以存在依赖关系,一个模块可以依赖于另一个模块提供的依赖。以下是一个示例:
java
java
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
// 定义一个基础模块
@Module
@InstallIn(SingletonComponent.class)
public class BaseModule {
@Provides
@Singleton
public Dependency1 provideDependency1() {
return new Dependency1();
}
}
// 定义一个依赖于 BaseModule 的模块
@Module
@InstallIn(SingletonComponent.class)
// 使用 @Includes 注解指定该模块依赖于 BaseModule
@Includes(BaseModule.class)
public class AppModule {
@Provides
@Singleton
public MyService provideMyService(Dependency1 dependency1) {
return new MyService(dependency1);
}
}
在这个示例中,AppModule
依赖于 BaseModule
提供的 Dependency1
实例。通过 @Includes(BaseModule.class)
注解,AppModule
可以使用 BaseModule
提供的依赖。
五、Hilt 的初始化模块
5.1 应用级初始化
在 Android 应用中使用 Hilt 时,需要在应用类中进行初始化。以下是一个简单的示例:
java
java
import android.app.Application;
import dagger.hilt.android.HiltAndroidApp;
// 使用 @HiltAndroidApp 注解标记应用类
@HiltAndroidApp
public class MyApplication extends Application {
// 应用类的其他代码
}
在这个示例中,MyApplication
类是应用的入口类,使用 @HiltAndroidApp
注解标记。@HiltAndroidApp
注解会触发 Hilt 的代码生成过程,生成必要的组件和注入器,从而实现依赖注入的初始化。
5.2 组件级初始化
5.2.1 Activity 初始化
在 Activity 中使用 Hilt 时,需要在 Activity 类上使用 @AndroidEntryPoint
注解进行初始化。以下是一个示例:
java
java
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import dagger.hilt.android.AndroidEntryPoint;
import javax.inject.Inject;
// 使用 @AndroidEntryPoint 注解标记 Activity 类
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
// 使用 @Inject 注解标记需要注入的依赖
@Inject
MyService myService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 使用注入的依赖
myService.doWork();
}
}
在这个示例中,MainActivity
类使用 @AndroidEntryPoint
注解标记,myService
字段使用 @Inject
注解标记。Hilt 会自动将 MyService
实例注入到 myService
字段中。
5.2.2 Fragment 初始化
在 Fragment 中使用 Hilt 时,同样需要在 Fragment 类上使用 @AndroidEntryPoint
注解进行初始化。以下是一个示例:
java
java
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import dagger.hilt.android.AndroidEntryPoint;
import javax.inject.Inject;
// 使用 @AndroidEntryPoint 注解标记 Fragment 类
@AndroidEntryPoint
public class MyFragment extends Fragment {
// 使用 @Inject 注解标记需要注入的依赖
@Inject
MyService myService;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_my, container, false);
// 使用注入的依赖
myService.doWork();
return view;
}
}
在这个示例中,MyFragment
类使用 @AndroidEntryPoint
注解标记,myService
字段使用 @Inject
注解标记。Hilt 会自动将 MyService
实例注入到 myService
字段中。
5.2.3 Service 初始化
在 Service 中使用 Hilt 时,需要在 Service 类上使用 @AndroidEntryPoint
注解进行初始化。以下是一个示例:
java
java
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import dagger.hilt.android.AndroidEntryPoint;
import javax.inject.Inject;
// 使用 @AndroidEntryPoint 注解标记 Service 类
@AndroidEntryPoint
public class MyService extends Service {
// 使用 @Inject 注解标记需要注入的依赖
@Inject
AnotherService anotherService;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 使用注入的依赖
anotherService.doSomething();
return super.onStartCommand(intent, flags, startId);
}
}
在这个示例中,MyService
类使用 @AndroidEntryPoint
注解标记,anotherService
字段使用 @Inject
注解标记。Hilt 会自动将 AnotherService
实例注入到 anotherService
字段中。
5.3 初始化流程源码分析
5.3.1 @HiltAndroidApp
注解处理
@HiltAndroidApp
注解是应用级初始化的关键注解,它会触发 Hilt 的代码生成过程。在编译时,Hilt 的注解处理器会扫描使用 @HiltAndroidApp
注解标记的类,并生成相应的组件和注入器。以下是 @HiltAndroidApp
注解的部分源码:
java
java
package dagger.hilt.android;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import dagger.hilt.GeneratesRootInput;
// 使用 @Target 注解指定该注解可以应用于类
@Target(ElementType.TYPE)
// 使用 @GeneratesRootInput 注解标记该注解会生成根输入
@GeneratesRootInput
public @interface HiltAndroidApp {
}
@HiltAndroidApp
注解使用 @Target(ElementType.TYPE)
注解指定该注解可以应用于类,使用 @GeneratesRootInput
注解标记该注解会生成根输入。在编译时,Hilt 的注解处理器会根据 @HiltAndroidApp
注解生成一个名为 MyApplication_HiltComponents
的类,该类包含了应用的组件和注入器的定义。
5.3.2 @AndroidEntryPoint
注解处理
@AndroidEntryPoint
注解是组件级初始化的关键注解,它会在组件(如 Activity、Fragment、Service 等)中实现依赖注入。以下是 @AndroidEntryPoint
注解的部分源码:
java
java
package dagger.hilt.android;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import dagger.hilt.android.internal.androidentrypoint.AndroidEntryPointGenerator;
import dagger.hilt.android.internal.androidentrypoint.HasAndroidEntryPointDependencies;
import dagger.hilt.android.internal.androidentrypoint.MissingAndroidEntryPointDependencies;
import dagger.hilt.android.internal.androidentrypoint.PreComponentEntryPoint;
import dagger.hilt.android.internal.lifecycle.HiltViewModelMap;
import dagger.hilt.internal.GeneratedComponent;
import dagger.hilt.internal.GeneratedComponentManager;
import dagger.hilt.internal.Preconditions;
// 使用 @Target 注解指定该注解可以应用于类
@Target(ElementType.TYPE)
public @interface AndroidEntryPoint {
// 定义一个默认值为 Object.class 的值
Class<?> value() default Object.class;
}
@AndroidEntryPoint
注解使用 @Target(ElementType.TYPE)
注解指定该注解可以应用于类。在编译时,Hilt 的注解处理器会根据 @AndroidEntryPoint
注解生成相应的代码,实现依赖的注入。例如,对于使用 @AndroidEntryPoint
注解标记的 MainActivity
类,会生成一个名为 MainActivity_GeneratedInjector
的类,该类负责将依赖注入到 MainActivity
中。
5.4 生命周期管理
Hilt 与 Android 组件的生命周期紧密集成,确保依赖的生命周期与组件的生命周期一致。例如,在 Activity 销毁时,Hilt 会自动释放与该 Activity 相关的依赖。以下是一个简单的示例,展示了 Hilt 如何管理依赖的生命周期:
java
java
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import dagger.hilt.android.AndroidEntryPoint;
import javax.inject.Inject;
// 使用 @AndroidEntryPoint 注解标记 Activity 类
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
// 使用 @Inject 注解标记需要注入的依赖
@Inject
MyService myService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 使用注入的依赖
myService.doWork();
}
@Override
protected void onDestroy() {
super.onDestroy();
// 在 Activity 销毁时,Hilt 会自动释放与该 Activity 相关的依赖
}
}
在这个示例中,myService
是一个注入的依赖,当 MainActivity
销毁时,Hilt 会自动释放与该 Activity 相关的依赖,确保资源的正确回收。
六、Hilt 模块配置与初始化的高级用法
6.1 作用域注解
Hilt 提供了一些作用域注解,用于控制依赖的生命周期和作用域。常见的作用域注解有 @Singleton
、@ActivityScoped
、@FragmentScoped
等。
6.1.1 @Singleton
注解
@Singleton
注解用于指定依赖的作用域为单例,即整个应用中只有一个该依赖的实例。以下是一个示例:
java
java
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(SingletonComponent.class)
public class AppModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
public MyService provideMyService() {
// 返回一个 MyService 实例
return new MyService();
}
}
在这个示例中,provideMyService
方法提供的 MyService
实例的作用域为单例,整个应用中只有一个 MyService
实例。
6.1.2 @ActivityScoped
注解
@ActivityScoped
注解用于指定依赖的作用域与 Activity 相同,即每个 Activity 实例都有一个独立的该依赖的实例。以下是一个示例:
java
java
import dagger.Module;
import dagger.Provides;
import dagger.hilt.android.scopes.ActivityScoped;
import javax.inject.Inject;
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(ActivityComponent.class)
public class ActivityModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @ActivityScoped 注解指定该依赖的作用域与 Activity 相同
@ActivityScoped
public ActivityService provideActivityService() {
// 返回一个 ActivityService 实例
return new ActivityService();
}
}
// 使用 @AndroidEntryPoint 注解标记 Activity 类
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
// 使用 @Inject 注解标记需要注入的依赖
@Inject
ActivityService activityService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 使用注入的依赖
activityService.doWork();
}
}
在这个示例中,provideActivityService
方法提供的 ActivityService
实例的作用域与 MainActivity
相同,每个 MainActivity
实例都有一个独立的 ActivityService
实例。
6.1.3 @FragmentScoped
注解
@FragmentScoped
注解用于指定依赖的作用域与 Fragment 相同,即每个 Fragment 实例都有一个独立的该依赖的实例。以下是一个示例:
java
java
import dagger.Module;
import dagger.Provides;
import dagger.hilt.android.scopes.FragmentScoped;
import javax.inject.Inject;
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(FragmentComponent.class)
public class FragmentModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @FragmentScoped 注解指定该依赖的作用域与 Fragment 相同
@FragmentScoped
public FragmentService provideFragmentService() {
// 返回一个 FragmentService 实例
return new FragmentService();
}
}
// 使用 @AndroidEntryPoint 注解标记 Fragment 类
@AndroidEntryPoint
public class MyFragment extends Fragment {
// 使用 @Inject 注解标记需要注入的依赖
@Inject
FragmentService fragmentService;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_my, container, false);
// 使用注入的依赖
fragmentService.doWork();
return view;
}
}
在这个示例中,provideFragmentService
方法提供的 FragmentService
实例的作用域与 MyFragment
相同,每个 MyFragment
实例都有一个独立的 FragmentService
实例。
6.2 限定符注解
限定符注解用于区分具有相同类型的不同依赖。当存在多个相同类型的依赖时,Hilt 无法自动确定要注入哪个依赖,此时可以使用限定符注解来指定具体的依赖。以下是一个示例:
java
java
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.SingletonComponent;
import javax.inject.Qualifier;
import javax.inject.Singleton;
// 定义一个限定符注解
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface FirstService {
}
// 定义另一个限定符注解
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface SecondService {
}
// 定义一个服务接口
interface MyService {
void doWork();
}
// 实现 MyService 接口
class FirstServiceImpl implements MyService {
@Override
public void doWork() {
System.out.println("First service is doing work");
}
}
// 实现 MyService 接口
class SecondServiceImpl implements MyService {
@Override
public void doWork() {
System.out.println("Second service is doing work");
}
}
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(SingletonComponent.class)
public class AppModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
// 使用 @FirstService 限定符注解指定该依赖
@FirstService
public MyService provideFirstService() {
// 返回一个 FirstServiceImpl 实例
return new FirstServiceImpl();
}
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
// 使用 @SecondService 限定符注解指定该依赖
@SecondService
public MyService provideSecondService() {
// 返回一个 SecondServiceImpl 实例
return new SecondServiceImpl();
}
}
// 使用 @AndroidEntryPoint 注解标记 Activity 类
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
// 使用 @Inject 注解标记需要注入的依赖,并使用 @FirstService 限定符注解指定具体的依赖
@Inject
@FirstService
MyService firstService;
// 使用 @Inject 注解标记需要注入的依赖,并使用 @SecondService 限定符注解指定具体的依赖
@Inject
@SecondService
MyService secondService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 使用注入的依赖
firstService.doWork();
secondService.doWork();
}
}
在这个示例中,我们定义了两个限定符注解 @FirstService
和 @SecondService
,用于区分 MyService
接口的两个不同实现 FirstServiceImpl
和 SecondServiceImpl
。在 AppModule
中,使用限定符注解分别提供了这两个实现的实例。在 MainActivity
中,使用限定符注解指定要注入的具体依赖。
6.3 多绑定
多绑定允许我们提供多个相同类型的依赖,并将它们收集到一个集合中。Hilt 提供了 @IntoSet
和 @IntoMap
注解来实现多绑定。
6.3.1 @IntoSet
注解
@IntoSet
注解用于将一个依赖添加到一个集合中。以下是一个示例:
java
java
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.SingletonComponent;
import dagger.multibindings.IntoSet;
import javax.inject.Singleton;
import java.util.Set;
// 定义一个服务接口
interface MyService {
void doWork();
}
// 实现 MyService 接口
class FirstServiceImpl implements MyService {
@Override
public void doWork() {
System.out.println("First service is doing work");
}
}
// 实现 MyService 接口
class SecondServiceImpl implements MyService {
@Override
public void doWork() {
System.out.println("Second service is doing work");
}
}
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(SingletonComponent.class)
public class AppModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
// 使用 @IntoSet 注解将该依赖添加到集合中
@IntoSet
public MyService provideFirstService() {
// 返回一个 FirstServiceImpl 实例
return new FirstServiceImpl();
}
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
// 使用 @IntoSet 注解将该依赖添加到集合中
@IntoSet
public MyService provideSecondService() {
// 返回一个 SecondServiceImpl 实例
return new SecondServiceImpl();
}
}
// 使用 @AndroidEntryPoint 注解标记 Activity 类
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
// 使用 @Inject 注解标记需要注入的依赖,注入一个包含 MyService 实例的集合
@Inject
Set<MyService> myServices;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 遍历集合,调用每个服务的 doWork 方法
for (MyService service : myServices) {
service.doWork();
}
}
}
在这个示例中,provideFirstService
和 provideSecondService
方法使用 @IntoSet
注解将 FirstServiceImpl
和 SecondServiceImpl
实例添加到一个集合中。在 MainActivity
中,注入一个包含 MyService
实例的集合,并遍历该集合调用每个服务的 doWork
方法。
6.3.2 @IntoMap
注解
@IntoMap
注解用于将一个依赖添加到一个映射中。以下是一个示例:
java
java
import dagger.Module;
import dagger.Provides;
import dagger.hilt.InstallIn;
import dagger.hilt.android.components.SingletonComponent;
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
import javax.inject.Singleton;
import java.util.Map;
// 定义一个服务接口
interface MyService {
void doWork();
}
// 实现 MyService 接口
class FirstServiceImpl implements MyService {
@Override
public void doWork() {
System.out.println("First service is doing work");
}
}
// 实现 MyService 接口
class SecondServiceImpl implements MyService {
@Override
public void doWork() {
System.out.println("Second service is doing work");
}
}
// 使用 @Module 注解标记该类为 Hilt 模块
@Module
// 使用 @InstallIn 注解指定该模块要安装到的组件
@InstallIn(SingletonComponent.class)
public class AppModule {
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
// 使用 @IntoMap 注解将该依赖添加到映射中
@IntoMap
// 使用 @StringKey 注解指定映射的键
@StringKey("first")
public MyService provideFirstService() {
// 返回一个 FirstServiceImpl 实例
return new FirstServiceImpl();
}
// 使用 @Provides 注解标记该方法为提供依赖的方法
@Provides
// 使用 @Singleton 注解指定该依赖的作用域为单例
@Singleton
// 使用 @IntoMap 注解将该依赖添加到映射中
@IntoMap
// 使用 @StringKey 注解指定映射的键
@StringKey("second")
public MyService provideSecondService() {
// 返回一个 SecondServiceImpl 实例
return new SecondServiceImpl();
}
}
// 使用 @AndroidEntryPoint 注解标记 Activity 类
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
// 使用 @Inject 注解标记需要注入的依赖,注入一个包含 MyService 实例的映射
@Inject
Map<String, MyService> myServiceMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 遍历映射,调用每个服务的 doWork 方法
for (Map.Entry<String, MyService> entry : myServiceMap.entrySet()) {
System.out.println("Key: " + entry.getKey());
entry.getValue().doWork();
}
}
}
在这个示例中,provideFirstService
和 provideSecondService
方法使用 @IntoMap
注解将 FirstServiceImpl
和 SecondServiceImpl
实例添加到一个映射中。使用 @StringKey
注解指定映射的键。在 MainActivity
中,注入一个包含 MyService
实例的映射,并遍历该映射调用每个服务的 doWork
方法。
七、总结与展望
7.1 总结
本文深入分析了 Android Hilt 的模块配置与初始化模块。首先回顾了依赖注入的基础概念,介绍了 Hilt 的特点和基本概念。然后详细讲解了 Hilt 的模块配置,包括模块的定义、安装、提供不同类型的依赖以及模块的依赖关系。接着分析了 Hilt 的初始化模块,包括应用级初始化、组件级初始化、初始化流程源码分析和生命周期管理。最后介绍了 Hilt 模块配置与初始化的高级用法,如作用域注解、限定符注解和多绑定。
通过对 Hilt 的模块配置与初始化模块的深入分析,我们可以看到 Hilt 提供