深入剖析 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 提供

相关推荐
努力学习的小廉31 分钟前
深入了解linux系统—— 进程地址空间
android·linux·服务器
diaostar4 小时前
Android OKHttp原理简单说明
android·okhttp
el psy congroo4 小时前
Kotlin-访问权限控制
开发语言·kotlin
XuanRanDev4 小时前
Kotlin 作用域函数全解析:let、run、with、apply、also 应该怎么选?
java·kotlin
b2894lxlx6 小时前
flutter3.29 build.gradle.kts设置安卓签名
android·flutter
androidwork7 小时前
Kotlin扩展函数提升Android开发效率
android·开发语言·kotlin
居然是阿宋7 小时前
Android SDK 开发中的 AAR 与 JAR 区别详解
android·java·jar
练习本8 小时前
AI大模型驱动的智能座舱研发体系重构
android·人工智能·重构·架构
姜行运8 小时前
C++【继承】
android·开发语言·c++
pq113_68 小时前
OrangePi Zero 3学习笔记(Android篇)1 - 搭建环境
android·orangepi zero 3