盒子
盒子
文章目录
  1. Module复用
    1. 共用Module
    2. 使用dependencies
    3. 使用Subcomponent
  2. Scope
  3. Demo地址

Dagger2学习笔记(二)

在上一篇文章我们讲了用于搜索的SearchActivity的实现,这一篇文章我们继续以剩下的两个Activity的实现为例,讲一下Dagger2的其他特性。这两个Activity分别是用了展示SearchActivity搜索的用户的头像和用户名的UserInfoActivity和点击用户头像跳转到的展示用户followers的FollowerActivity。

在我们的Demo中有个叫做UserInfoLoader的类,它是用来向github服务器请求用户信息和follower信息的,会在多个actiity中被使用,例如在FollowerPresenter和UserInfoPresenter中都需要注入UserInfoLoader。最简单的方式是我们可以直接使用@Inject注解标注它的构造方法,使得Dagger2可以直接创建它的实例去注入FollowerPresenter和UserInfoPresenter中。

1
2
3
4
5
6
7
class UserInfoLoader {
...
@Inject
UserInfoLoader() {
}
...
}

Module复用

当然我们也能用复用Module的方式,这种方式虽然比直接用@Inject注解构造方法复杂,但是它还有其他十分有用的功能,接下来我会慢慢分析。

首先我们把它的Module单独抽出来,放到AppModule中:

1
2
3
4
5
6
7
@Module
public class AppModule {
@Provides
UserInfoLoader provideUserInfoLoader() {
return new UserInfoLoader();
}
}

共用Module

我们复用这个Module的方式有几种,一是同时放在FollowerComponent和UserInfoComponent的modules中:

1
2
3
4
5
@Component(modules = {AppModule.class, FollowerPresenterModule.class})
public interface FollowerComponent {
void inject(FollowerPresenter presenter);
void inject(FollowerActivity activity);
}
1
2
3
4
5
@Component(modules = {AppModule.class, UserInfoPresenterModule.class})
public interface UserInfoComponent {
void inject(UserInfoPresenter presenter);
void inject(UserInfoActivity activity);
}

使用dependencies

第二种方式是使用dependencies,首先我们需要声明多一个AppComponent接口

1
2
3
4
@Component(modules = {AppModule.class})
public interface AppComponent {
UserInfoLoader provideUserInfoLoader();
}

这个接口的provideUserInfoLoader()方法就是提供出来给子依赖获取UserInfoLoader的,因为dependencies子依赖是获取不了父依赖的modules里面的Provides的。

之后声明FollowerComponent和UserInfoComponent:

1
2
3
4
5
@Component(dependencies = AppComponent.class, modules = {UserInfoPresenterModule.class})
public interface UserInfoComponent {
void inject(UserInfoPresenter presenter);
void inject(UserInfoActivity activity);
}
1
2
3
4
5
@Component(dependencies = AppComponent.class, modules = {FollowerPresenterModule.class})
public interface FollowerComponent {
void inject(FollowerPresenter presenter);
void inject(FollowerActivity activity);
}

最后就再去实现注入:

1
2
3
4
5
6
FollowerComponent component = DaggerFollowerComponent.builder()
.appComponent(getAppComponent())
.followerPresenterModule(new FollowerPresenterModule(this))
.build();
component.inject(this);
component.inject(mPresenter);

1
2
3
4
5
6
UserInfoComponent component = DaggerUserInfoComponent.builder()
.appComponent(getAppComponent())
.userInfoPresenterModule(new UserInfoPresenterModule(this))
.build();
component.inject(this);
component.inject(mPresenter);

这里的AppComponent是公用的,所以我们放到Application中:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class AppApplication extends Application {
private AppComponent mAppComponent;

public AppApplication() {
super();

mAppComponent = DaggerAppComponent.create();
}

public AppComponent getAppComponent() {
return mAppComponent;
}
}

然后在Activity中这样获取AppComponent:

1
2
3
AppComponent getAppComponent() {
return ((AppApplication)getApplication()).getAppComponent();
}

我们尝试注释掉AppComponent.provideUserInfoLoader,rebuild一下,发现居然没有报错,这是怎么回事?其实是因为UserInfoLoader的构造方法使用@Inject注解标注了,所以可以直接通过构造方法创建UserInfoLoader来注入FollowerPresenter和FollowerActivity。

我们再把UserInfoLoader的构造方法的@Inject注解注释掉,这时候再rebuild就可以发现报错了。

然后再取消掉AppComponent.provideUserInfoLoader的注释,就能顺利编过了。因为我们的AppModule.provideUserInfoLoader是通过new 一个UserInfoLoader出来的,所以可以不依赖构造方法的@Inject注解。

使用Subcomponent

最后一种方法就是使用@Subcomponent注解,这中方法和使用dependencies有点像,他们的区别在于使用@Subcomponent方法AppComponent不需要提供一个provideUserInfoLoader方法,子依赖可以直接使用AppComponent中的modules。首先我们要这样声明AppComponent:

1
2
3
4
5
@Component(modules = {AppModule.class})
public interface AppComponent {
FollowerComponent plus(FollowerPresenterModule module);
UserInfoComponent plus(UserInfoPresenterModule module);
}

然后FollowerComponent和UserInfoComponent的定义如下:

1
2
3
4
5
@Subcomponent(modules = {FollowerPresenterModule.class})
public interface FollowerComponent {
void inject(FollowerPresenter presenter);
void inject(FollowerActivity activity);
}
1
2
3
4
5
@Subcomponent(modules = {UserInfoPresenterModule.class})
public interface UserInfoComponent {
void inject(UserInfoPresenter presenter);
void inject(UserInfoActivity activity);
}

注入的实现代码如下:

1
2
3
4
FollowerComponent component = getAppComponent().plus(new FollowerPresenterModule(this));

component.inject(this);
component.inject(mPresenter);

Scope

现在还有一个问题,现在FollowerComponent和UserInfoComponent虽然都往Presenter注入了UserInfoLoader,但他们是不同的实例:

D/UserInfoPresenter: mUserInfoLoader : com.example.linjw.dagger2demo.model.UserInfoLoader@31e117c
D/FollowerPresenter: mUserInfoLoader : com.example.linjw.dagger2demo.model.UserInfoLoader@c9ad63b

如果我想他们使用的就是同一个UserInfoLoader实例呢?需要怎么做?

Dagger2中有作用域的概念,可以规定几个Component在同一个作用域,在同一个作用域注入的依赖就是同一个实例。

首先需要声明我们的Scope:

1
2
3
4
@Scope
@Retention(RUNTIME)
public @interface AppScope {
}

然后就只需要将Module的Provides方法和Component用同一个Scope注解标注一下,就能让他们处于同一个作用域了。

比如我们需要在AppModule.provideUserInfoLoader标注:

1
2
3
4
5
6
7
8
@Module
public class AppModule {
@AppScope
@Provides
UserInfoLoader provideUserInfoLoader() {
return new UserInfoLoader();
}
}

像我们使用Subcomponent去实现依赖继承,我们就只需要在AppComponent中标注就好了,这样他们的子依赖也会处于AppScope中:

1
2
3
4
5
6
@AppScope
@Component(modules = {AppModule.class})
public interface AppComponent {
FollowerComponent plus(FollowerPresenterModule module);
UserInfoComponent plus(UserInfoPresenterModule module);
}

D/UserInfoPresenter: mUserInfoLoader : com.example.linjw.dagger2demo.model.UserInfoLoader@31e117c
D/FollowerPresenter: mUserInfoLoader : com.example.linjw.dagger2demo.model.UserInfoLoader@31e117c

Demo地址

可以在这里查看完整代码