深入剖析 Android Hilt 的模块配置与初始化模块(五)

深入剖析 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 组件(如 ActivityFragmentService 等)深度集成,开发者可以直接在这些组件中使用依赖注入,无需手动管理依赖的生命周期。
  • 性能优化:Hilt 在编译时生成代码,避免了运行时的反射开销,从而提高了应用的性能。

3.2 Hilt 的基本概念

  • 组件(Component) :组件是 Hilt 中的核心概念,它负责管理依赖的生命周期和提供依赖的实例。Hilt 提供了一些预定义的组件,如 SingletonComponentActivityRetainedComponentActivityComponent 等,每个组件都有自己的生命周期和作用域。
  • 模块(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 接口的两个不同实现 FirstServiceImplSecondServiceImpl。在 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();
        }
    }
}

在这个示例中,provideFirstServiceprovideSecondService 方法使用 @IntoSet 注解将 FirstServiceImplSecondServiceImpl 实例添加到一个集合中。在 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();
        }
    }
}

在这个示例中,provideFirstServiceprovideSecondService 方法使用 @IntoMap 注解将 FirstServiceImplSecondServiceImpl 实例添加到一个映射中。使用 @StringKey 注解指定映射的键。在 MainActivity 中,注入一个包含 MyService 实例的映射,并遍历该映射调用每个服务的 doWork 方法。

七、总结与展望

7.1 总结

本文深入分析了 Android Hilt 的模块配置与初始化模块。首先回顾了依赖注入的基础概念,介绍了 Hilt 的特点和基本概念。然后详细讲解了 Hilt 的模块配置,包括模块的定义、安装、提供不同类型的依赖以及模块的依赖关系。接着分析了 Hilt 的初始化模块,包括应用级初始化、组件级初始化、初始化流程源码分析和生命周期管理。最后介绍了 Hilt 模块配置与初始化的高级用法,如作用域注解、限定符注解和多绑定。

通过对 Hilt 的模块配置与初始化模块的深入分析,我们可以看到 Hilt 提供

相关推荐
_祝你今天愉快23 分钟前
深入剖析Java中ThreadLocal原理
android
张力尹1 小时前
谈谈 kotlin 和 java 中的锁!你是不是在协程中使用 synchronized?
android
流浪汉kylin2 小时前
Android 斜切图片
android
PuddingSama2 小时前
Android 视图转换工具 Matrix
android·前端·面试
RichardLai883 小时前
[Flutter学习之Dart基础] - 控制语句
android·flutter
archko3 小时前
compose map 源码解析
android
大熊的瓜地3 小时前
从零开始写android 的智能指针
android
甜辣小悦羊3 小时前
Android Studio 的安装教程
android·ide·android studio
louisgeek3 小时前
Android Intent
android
树獭非懒3 小时前
Android重学笔记|别再滥用广播了
android·客户端