Dagger技术的使用学习

场景是假设:我们有一个 LoginActivity,需要一个 LoginViewModel,它又依赖 UserRepository,UserRepository 再依赖一个 ApiService(网络请求)。

1. 业务层类:ApiService、UserRepository、LoginViewModel

1.1 ApiService(第三方/网络封装类,通常不能直接 @Inject 构造)

arduino 复制代码
// ApiService.java
public class ApiService {

    private final String baseUrl;

    public ApiService(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    public boolean login(String username, String password) {
        // 假装这里发网络请求
        System.out.println("Request to " + baseUrl + " login: " + username);
        // 简化起见直接返回 true
        return true;
    }
}

逐行说明:

  • public class ApiService {

    定义一个普通的 Java 类,代表网络层封装。

  • private final String baseUrl;

    用于保存接口域名。

  • public ApiService(String baseUrl) { ... }

    构造函数需要一个 baseUrl 参数,这个在项目中常常来自配置或 BuildConfig。

    注意:我们没有@Inject,因为往往这个类来自三方库或比较通用,习惯用 Module 提供。

  • public boolean login(...) { ... }

    一个模拟登录方法,这里只是打印日志并返回 true

1.2 UserRepository(可以使用构造函数注入)

arduino 复制代码
// UserRepository.java
import javax.inject.Inject;

public class UserRepository {

    private final ApiService apiService;

    @Inject
    public UserRepository(ApiService apiService) {
        this.apiService = apiService;
    }

    public boolean login(String username, String password) {
        // 业务仓库层,把网络调用组织在一起
        return apiService.login(username, password);
    }
}

逐行说明:

import javax.inject.Inject; 引入 JSR-330 标准的 @Inject 注解(Dagger 支持)。

public class UserRepository { 仓库层,封装数据访问(网络、本地缓存等)。

private final ApiService apiService; 依赖 ApiService。

@Inject 标记这个构造函数是 Dagger 可用的"注入点": Dagger 知道如果有人需要 UserRepository,要用这个构造函数来创建,同时自动注入 ApiService。

public UserRepository(ApiService apiService) { ... } 构造函数依赖 ApiService,Dagger 会从依赖图中找到对应的提供方式。

public boolean login(...) { ... } 对外暴露业务方法,内部使用 apiService.login(...)。

1.3 LoginViewModel(同样用构造函数注入)

kotlin 复制代码
// LoginViewModel.java
import javax.inject.Inject;

public class LoginViewModel {

    private final UserRepository userRepository;

    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public boolean performLogin(String username, String password) {
        // 可以在这里做一些校验、状态管理等
        if (username == null || username.isEmpty()) {
            System.out.println("Username empty");
            return false;
        }
        return userRepository.login(username, password);
    }
}

逐行说明:

public class LoginViewModel { 表示页面逻辑层(这里不用 AndroidX ViewModel,为了示例简单)。

private final UserRepository userRepository; 依赖仓库层。

@Inject + 构造函数 告诉 Dagger:如果有人需要 LoginViewModel,就用这个构造函数,并从依赖图中注入一个 UserRepository。

performLogin(...) 页面逻辑:进行简单校验后,调用仓库层完成登录。

2. Dagger Module:提供 ApiService

因为 ApiService 构造函数需要 baseUrl,我们希望统一在一个地方配置它,用 Dagger 的 @Module + @Provides。

typescript 复制代码
// NetworkModule.java
import dagger.Module;
import dagger.Provides;

import javax.inject.Singleton;

@Module
public class NetworkModule {

    private final String baseUrl;

    public NetworkModule(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    @Singleton
    @Provides
    ApiService provideApiService() {
        return new ApiService(baseUrl);
    }
}

逐行说明:

@Module 声明这是一个 Dagger Module,里面的方法会用于提供依赖对象。

public class NetworkModule { 定义网络相关的依赖提供模块。

private final String baseUrl; + 构造函数 模块本身也可以有构造函数,这样我们在创建 Component 时可以传入不同的配置(例如测试环境、生产环境用不同 baseUrl)。

@Singleton 声明本方法产生的对象在该 Component 生命周期内为单例。

@Provides 告诉 Dagger:这是一个"提供函数(Provider)",用来创建 ApiService 实例。

ApiService provideApiService() { return new ApiService(baseUrl); } 实际创建 ApiService;Dagger 会在有人需要 ApiService 时调用此方法。

3. Dagger Component:组装整个依赖图

java 复制代码
// AppComponent.java
import javax.inject.Singleton;

import dagger.Component;

@Singleton
@Component(modules = { NetworkModule.class })
public interface AppComponent {

    // 暴露一个方法:用于给 LoginActivity 注入字段
    void inject(LoginActivity activity);

    // 也可以选择暴露某些依赖(可选)
    LoginViewModel loginViewModel();
}

逐行说明:

@Singleton 声明整个 Component 的作用域是单例;配合 Module 中的 @Singleton,可以保证 ApiService 等是在同一图中单例。

@Component(modules = { NetworkModule.class }) 告诉 Dagger:这个 Component 依赖哪些 Module 来提供对象。 这里指定 NetworkModule,让 Dagger 知道 ApiService 怎么创建。

public interface AppComponent { ... } Component 通常是一个接口,Dagger 会在编译期生成实现类 DaggerAppComponent。

void inject(LoginActivity activity); 声明一个"注入方法":表示 Dagger 有能力给 LoginActivity 中标记了 @Inject 的字段进行赋值。

LoginViewModel loginViewModel(); 可选:直接从 Component 获取某个对象实例,此处获取 LoginViewModel。

4. Android 层:LoginActivity 使用 Dagger 注入

scala 复制代码
// LoginActivity.java
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import javax.inject.Inject;

public class LoginActivity extends AppCompatActivity {

    @Inject
    LoginViewModel loginViewModel;

    private AppComponent appComponent;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 1. 构建 AppComponent
        appComponent = DaggerAppComponent
                .builder()
                .networkModule(new NetworkModule("https://api.example.com"))
                .build();

        // 2. 对当前 Activity 进行依赖注入
        appComponent.inject(this);

        // 3. 使用注入好的 loginViewModel
        boolean success = loginViewModel.performLogin("alice", "123456");

        System.out.println("Login result: " + success);
    }
}

逐行说明:

public class LoginActivity extends AppCompatActivity { 标准 Android Activity。

@Inject LoginViewModel loginViewModel; 这里是字段注入:表示 loginViewModel 的实例由 Dagger 来提供。 注意:此时还没有值,只有在执行 appComponent.inject(this); 之后才会被赋值。

private AppComponent appComponent; 保存一个 AppComponent 引用,方便后续使用。

onCreate(...) 是 Activity 生命周期入口。

onCreate 内部:

1.构建 AppComponent
ini 复制代码
appComponent = DaggerAppComponent
        .builder()
        .networkModule(new NetworkModule("https://api.example.com"))
        .build();

DaggerAppComponent 这是 Dagger 编译期自动生成的类名(规则:前面加 Dagger 前缀)。

.builder() 使用 Builder 模式构建 Component。

.networkModule(...) 这里给 Component 设置 NetworkModule,传入 baseUrl。 Dagger 知道 AppComponent 需要一个类型为 NetworkModule 的模块实例。

.build() 完成 Component 实例构建,内部会初始化依赖图。

2.对 Activity 注入
kotlin 复制代码
appComponent.inject(this);

调用我们在 AppComponent 接口中定义的 inject(LoginActivity activity)。

Dagger 会扫描 LoginActivity 中标记了 @Inject 的字段(这里是 loginViewModel),并根据依赖图实例化 LoginViewModel:

为 LoginViewModel 的构造函数注入 UserRepository。

为 UserRepository 的构造函数注入 ApiService。

为 ApiService 调用 NetworkModule.provideApiService() 创建实例。

最终把组装好的 LoginViewModel 赋值给 loginViewModel 字段。

3.使用注入结果
ini 复制代码
boolean success = loginViewModel.performLogin("alice", "123456");

此时 loginViewModel 已经是一个完整的对象,可以直接使用。

后续该 ViewModel 内部调用 Repository,Repository 调用 ApiService,整条依赖链都由 Dagger 管理。

5. 流程总结(从需求到对象的形成)

以 LoginActivity 想要使用 LoginViewModel 为主线:

Activity 声明需要 LoginViewModel(字段 @Inject LoginViewModel loginViewModel;)。

通过 DaggerAppComponent.builder().networkModule(...).build() 构建依赖图。

调用 appComponent.inject(this):

Dagger 发现需要为 LoginActivity.loginViewModel 提供实例;

查看 LoginViewModel 构造函数有 @Inject,需要 UserRepository;

找到 UserRepository 构造函数 @Inject,需要 ApiService;

发现 ApiService 没有 @Inject 构造函数,但有一个 @Provides ApiService provideApiService();

调用 NetworkModule.provideApiService() 得到 ApiService;

组装出 UserRepository、LoginViewModel,最后把 loginViewModel 赋给 Activity 字段。

全程不需要在 Activity 中写:

ini 复制代码
ApiService api = new ApiService("...");
UserRepository repo = new UserRepository(api);
LoginViewModel vm = new LoginViewModel(repo);

而是完全由 Dagger 管理对象创建和依赖关系。

关键就是理清个对象之间的依赖关系。

相关推荐
IT_陈寒2 小时前
Redis性能翻倍的5个关键策略:从慢查询到百万QPS的实战优化
前端·人工智能·后端
码界奇点2 小时前
基于React与TypeScript的后台管理系统设计与实现
前端·c++·react.js·typescript·毕业设计·源代码管理
cehuishi95272 小时前
python和arcgispro的实践(AI辅助编程)
服务器·前端·python
Summer不秃2 小时前
使用 SnapDOM + jsPDF 生成高质量 PDF (含多页分页, 附源码)
前端·javascript·vue.js·pdf·node.js
AC赳赳老秦2 小时前
工业互联网赋能智造:DeepSeek解析产线传感器数据驱动质量管控新范式
前端·数据库·人工智能·zookeeper·json·flume·deepseek
Student_Zhang2 小时前
一个管理项目中所有弹窗的弹窗管理器(PopupManager)
前端·ios·github
网络风云2 小时前
HTML 模块化方案
前端·html
小满zs3 小时前
Next.js第十九章(服务器函数)
前端·next.js
仰望.3 小时前
vxe-table 如何实现分页勾选复选框功能,分页后还能支持多选的选中状态
前端·vue.js·vxe-table