================
提示: 我在学习 Dagger2 时,也阅读了很多文章和官方文档。有些作者会列举出所有注解的用法,有些作者只介绍用法而忽略解释自动生成的代码。我也在寻求一种易于理解 / 接受的讲法,最后我觉得先「基础注解」再「复杂注解」,边介绍用法边解释自动生成代码的方式,或许是更容易理解的方式。期待得到你的反馈~
在讨论的过程中,我们通过一个简单的例子来展开:假设我们有一个用户数据模块,它依赖于两个依赖项:
public class UserRepository {
private final UserLocalDataSource userLocalDataSource;
private final UserRemoteDataSource userRemoteDataSource;
public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
this.userLocalDataSource = userLocalDataSource;
this.userRemoteDataSource = userRemoteDataSource;
}
}
首先,你可以选择不使用依赖注入,那么你可能就会在项目多处重复构建,缺点我们在第一节都讨论过了。
new UserRepository(new UserLocalDataSource(), new UserRemoveDataSource());
后来,有追求的你已经开始使用依赖注入,你写了一个全局的工具方法:
public static UserRepository get() {
return new UserRepository(new UserLocalDataSource(), new UserRemoveDataSource());
}
这确实能满足需求,然而在真实项目中,模块之间的依赖关系往往比这个例子要复杂得多。此时,如果经常手动编写依赖注入的模板代码,不仅耗时耗力,也容易出错。下面,我们开始使用 Dagger2 这个帮手来替我们编写模板代码。
3.1 @Component + @Inject
@Component 和 @Inject 是 Dagger2 最基础的两个注解,仅使用这两个注解就可以实现最简单的依赖注入。
- @Component:创建一个 Dagger 容器,作为获取依赖项的入口
@Component
public interface ApplicationComponent {
UserRepository userRepository();
}
- @Inject:指示 Dagger 如何实例化一个对象
public class UserRepository {
private final UserLocalDataSource userLocalDataSource;
private final UserRemoteDataSource userRemoteDataSource;
@Inject
public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
this.userLocalDataSource = userLocalDataSource;
this.userRemoteDataSource = userRemoteDataSource;
}
}
--------------------------------------------
public class UserLocalDataSource {
@Inject
public UserLocalDataSource() {
}
}
--------------------------------------------
public class UserRemoveDataSource {
@Inject
public UserRemoveDataSource() {
}
}
你需要用 @Inject 注解修饰依赖项的构造方法,同时,它的依赖项 UserLocalDataSource 和 UserRemoteDataSource 也需要增加 @Inject 注解。
以上代码在构建后会自动生成代码:
DaggerApplicationComponent.java
1、实现 ApplicationComponent 接口
public final class DaggerApplicationComponent implements ApplicationComponent {
private DaggerApplicationComponent() {
}
2、创建依赖项实例
@Override
public UserRepository userRepository() {
return new UserRepository(new UserLocalDataSource(), new UserRemoteDataSource());
}
3、构建者模式
public static Builder builder() {
return new Builder();
}
public static ApplicationComponent create() {
return new Builder().build();
}
public static final class Builder {
private Builder() {
}
public ApplicationComponent build() {
return new DaggerApplicationComponent();
}
}
}
可以看到,最简单的依赖注入模板代码已经自动生成了。使用时,你只需要通过 ApplicationComponent 这个入口就可以获得 UserReopsitory 实例:
ApplicationComponent component = DaggerApplicationComponent.create();
UserRepository userRepository = component.userRepository();
3.2 @Inject 字段注入
有些类不是使用构造器初始化的,例如 Android 框架类 Activity 和 Fragment 由系统实例化,此时就不能再使用 3.1 节 中使用的构造器注入,可以改为字段注入,并手动调用方法请求注入。
构造器注入:(X)
public class MyActivity {
@Inject
public MyActivity(LoginViewModel viewModel){
...
}
}
--------------------------------------------
字段注入:
class MainActivity : AppCompatActivity() {
@Inject
lateinit var viewModel: LoginViewModel
override fun onCreate(savedInstanceState: Bundle?) {
DaggerApplicationComponent.create().inject001(this)
super.onCreate(savedInstanceState)
...
}
}
public class LoginViewModel {
private final UserRepository userRepository;
@Inject
public LoginViewModel(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
在 Activity 或 Fragment 中使用时,需要注意组件的生命周期:
-
在 super.onCreate() 中的恢复阶段,Activity 会附加绑定的 Fragment,这些 Fragment 可能需要访问 Activity。为保证数据一致性,应在调用 super.onCreate() 之前在 Activity 的 onCreate() 方法中注入 Dagger。 -
在使用 Fragment 时,应在 Fragment 的 onAttach() 方法中注入 Dagger,此操作可以在调用 super.onAttach() 之前或之后完成。
3.3 @Singleton / @Scope
- @Singleton / @Scope:声明作用域,可以约束依赖项的作用域周期
@Singleton
public class UserRepository {
...
}
--------------------------------------------
@Component
@Singleton
public interface ApplicationComponent {
...
}
在 ApplicationComponent 和 UserRepository 上使用相同的作用域注解,表明两者处于同一个作用域周期。这意味着,同一个 Component 多次提供该依赖项都是同一个实例。你可以直接使用内置的 @Singleton,也可以使用自定义注解:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
--------------------------------------------
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomScope {}
提示: 使用 @Singleton 或 @MyCustomScope,效果是完全一样的。
以上代码在构建后会自动生成代码:
public final class DaggerApplicationComponent implements ApplicationComponent {
private Provider<UserRepository> userRepositoryProvider;
private DaggerApplicationComponent() {
initialize();
}
private void initialize() {
this.userRepositoryProvider = DoubleCheck.provider(UserRepository_Factory.create(UserLocalDataSource_Factory.create(), UserRemoteDataSource_Factory.create()));
}
@Override
public UserRepository userRepository() {
return userRepositoryProvider.get();
}
...
}
作用域注解约束
有几个关于作用域注解的约束,你需要注意下:
- 如果某个组件有作用域注解,那么该组件只能给提供带有该注解的类或者不带任何作用域注解的类;
- 子组件不能使用和某个父组件的相同的作用域注解。
提示: 关于子组件的概念,你可以看 第 3.5 节。
作用域注解规范
只要你满足上面提到的约束规则,Dagger2 框架并不严格限制你定义的作用域语义。你可以按照业务划分作用域,也可以按照生命周期划分作用域。例如:
按照业务划分:
@Singleton
@LoginScope
@RegisterScope
--------------------------------------------
按声明周期划分:
@Singleton
@ActivityScope
@ModuleScope
@FeatureScope
不过,按照生命周期划分作用域是更加理想的做法,作用域不应该明确指明其实现目的。
3.4 @Module + @Providers
- @Module + @Providers:指示 Dagger 如何实例化一个对象,但不是以构造器的方式
public class UserRemoteDataSource {
private final LoginRetrofitService loginRetrofitService;
@Inject
public UserRemoteDataSource(LoginRetrofitService loginRetrofitService) {
this.loginRetrofitService = loginRetrofitService;
}
}
--------------------------------------------
@Module
public class NetworkModule {
@Provides
public LoginRetrofitService provide001(OkHttpClient client) {
return new Retrofit.Builder()
.baseUrl("https://example.com")
.build()
.create(LoginService.class);
}
}
--------------------------------------------
@Singleton
@Component(modules = NetworkModule.class)
public interface ApplicationComponent {
UserRepository userRepository();
void inject001(MainActivity activity);
}
@Module 模块提供了一种与 @Inject 不同的提供对象实例的方式。在 @Module 里,@Provides 方法的返回值是依赖项实例,而参数是进一步依赖的对象。另外,你还需要在 @Component 参数中应用该模块。
目前为止,我们构造的依赖关系图如下所示:
3.5 @Subcomponent
- @Subcomponent:声明子组件,使用子组件的概念可以定义更加细致的作用域
子组件是继承并扩展父组件的对象图的组件,子组件中的对象就可以依赖于父组件中提供的对象,但是父组件不能依赖于子组件依赖的对象(简单的包含关系,对吧?)。
我们继续通过一个简单的例子来展开:假设我们有一个登录模块 LoginActivity,它依赖于 LoginModel。我们的需求是定义一个子组件,它的声明周期只在一次登录流程中存在。在 第 3.2 节 提过,Activity 无法使用构造器注入,所以 LoginActivity 我们采用的是 @Inject 字段注入的语法:
@Subcomponent
public interface LoginComponent {
void inject(LoginActivity activity);
}
但是这样定义的 LoginComponent 还不能真正称为某个组件的子组件,需要增加额外声明:
@Module(subcomponents = LoginComponent.class)
public class SubComponentsModule {
}
--------------------------------------------
@Component(modules = {NetworkModule.class,SubComponentsModule.class})
@Singleton
public interface ApplicationComponent {
UserRepository userRepository();
LoginComponent.Factory loginComponent();
}
--------------------------------------------
@Subcomponent
public interface LoginComponent {
@Subcomponent.Factory
interface Factory{
le.class,SubComponentsModule.class})
@Singleton
public interface ApplicationComponent {
UserRepository userRepository();
LoginComponent.Factory loginComponent();
}
--------------------------------------------
@Subcomponent
public interface LoginComponent {
@Subcomponent.Factory
interface Factory{
|