Dagger2初探(二)

参考:
Dagger2 使用(二)

接上一篇Dagger2初探(一)继续讲Dagger2中的一些注解。

@Qualifier和@Named

关于这里有这么一个场景,有一个测试接口和一个线上接口,调用哪个接口一般通过一个标志位来区分, 上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class QualifierActivity extends AppCompatActivity {
private static final String TAG = "QualifierActivity";

private boolean isTest = true;

@Inject
ApiServer mApiServerTest;

@Inject
ApiServer mApiServerRelease;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qualifier);
DaggerQualifierComponent.builder()
.apiServerModule(getApiServerModule())
.build()
.inject(this);
Log.d(TAG, "mApiServerTest = " + mApiServerTest);
Log.d(TAG, "mApiServerRelease = " + mApiServerRelease);
if (isTest) {
mApiServerTest.register();
} else {
mApiServerRelease.register();
}
}

@NonNull
private ApiServerModule getApiServerModule() {
return new ApiServerModule(getApplicationContext());
}
}

ApiServerModule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Module
public class ApiServerModule {
private static final String TAG = "ApiServerModule";

private Context mContext;

public ApiServerModule(Context context){
mContext = context;
}

@Provides
public ApiServer provideTestApiServer(OkHttpClient okHttpClient){
Log.d(TAG,"provideTestApiServer");
return new ApiServer(okHttpClient);
}

@Provides
public ApiServer provideReleaseApiServer(OkHttpClient okHttpClient){
Log.d(TAG,"provideReleaseApiServer");
return new ApiServer(okHttpClient);
}
}

NetModule.java

1
2
3
4
5
6
7
8
9
@Module
public class NetModule {
private static final String TAG = "NetModule";
@Provides
public OkHttpClient provideOkhttpClient(){
Log.d(TAG, "provideOkhttpClient");
return new OkHttpClient().newBuilder().build();
}
}

QualifierComponent.java

1
2
3
4
@Component(modules = {ApiServerModule.class, NetModule.class})
public interface QualifierComponent {
void inject(QualifierActivity activity);
}

然后Make Project, 发现报错了

因为ApiServerModule里有两个方法的返回值都是ApiServer,Component一脸懵逼,愣是搞不懂把哪两个”绑定”起来,直接罢工了。所以这里需要用@Named注解来标注,从而区分这两个对象。

现在修改代码:

ApiServerModule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Module
public class ApiServerModule {
private static final String TAG = "ApiServerModule";

private Context mContext;

public ApiServerModule(Context context){
mContext = context;
}

@Named("test")
@Provides
public ApiServer provideTestApiServer(OkHttpClient okHttpClient){
Log.d(TAG,"provideTestApiServer");
return new ApiServer(okHttpClient);
}

@Named("release")
@Provides
public ApiServer provideReleaseApiServer(OkHttpClient okHttpClient){
Log.d(TAG,"provideReleaseApiServer");
return new ApiServer(okHttpClient);
}
}

QualifierActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class QualifierActivity extends AppCompatActivity {
private static final String TAG = "QualifierActivity";

private boolean isTest = true;

@Named("test")
@Inject
ApiServer mApiServerTest;

@Named("release")
@Inject
ApiServer mApiServerRelease;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qualifier);
DaggerQualifierComponent.builder()
.apiServerModule(getApiServerModule())
.build()
.inject(this);
Log.d(TAG, "mApiServerTest = " + mApiServerTest);
Log.d(TAG, "mApiServerRelease = " + mApiServerRelease);
if (isTest) {
mApiServerTest.register();
} else {
mApiServerRelease.register();
}
}

@NonNull
private ApiServerModule getApiServerModule() {
return new ApiServerModule(getApplicationContext());
}
}

打印结果:

1
2
3
4
5
6
7
8
9
05-17 10:19:27.307 13288-13288/me.mundane.dagger2learning D/NetModule: provideOkhttpClient
05-17 10:19:27.382 13288-13288/me.mundane.dagger2learning D/ApiServerModule: provideTestApiServer
05-17 10:19:27.383 13288-13288/me.mundane.dagger2learning D/ApiServer: ApiServer构造函数
05-17 10:19:27.384 13288-13288/me.mundane.dagger2learning D/NetModule: provideOkhttpClient
05-17 10:19:27.386 13288-13288/me.mundane.dagger2learning D/ApiServerModule: provideReleaseApiServer
05-17 10:19:27.386 13288-13288/me.mundane.dagger2learning D/ApiServer: ApiServer构造函数
05-17 10:19:27.386 13288-13288/me.mundane.dagger2learning D/QualifierActivity: mApiServerTest = me.mundane.dagger2learning.bean.ApiServer@8381dde
05-17 10:19:27.386 13288-13288/me.mundane.dagger2learning D/QualifierActivity: mApiServerRelease = me.mundane.dagger2learning.bean.ApiServer@b28bbf
05-17 10:19:27.386 13288-13288/me.mundane.dagger2learning D/ApiServer: 注册信息

可以看到成功注入了两个ApiServer对象

其实这里的@Named注解也只是@Qualifier中的一种而已,我们直接看@Named的源码

1
2
3
4
5
6
7
8
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

/** The name. */
String value() default "";
}

我们可以不使用@Named而使用@Qualifier来自定义两个注解。

Release.java

1
2
3
4
5
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Release {

}

Test.java

1
2
3
4
5
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {

}

使用这两个注解替换@Named(“test”)和@Named(“release”)
查看打印结果:

1
2
3
4
5
6
7
8
9
05-17 10:51:09.746 15573-15573/me.mundane.dagger2learning D/NetModule: provideOkhttpClient
05-17 10:51:09.832 15573-15573/me.mundane.dagger2learning D/ApiServer: ApiServer构造函数
05-17 10:51:09.833 15573-15573/me.mundane.dagger2learning D/ApiServerModule: provideTestApiServer me.mundane.dagger2learning.bean.ApiServer@753e28c
05-17 10:51:09.834 15573-15573/me.mundane.dagger2learning D/NetModule: provideOkhttpClient
05-17 10:51:09.835 15573-15573/me.mundane.dagger2learning D/ApiServer: ApiServer构造函数
05-17 10:51:09.836 15573-15573/me.mundane.dagger2learning D/ApiServerModule: provideReleaseApiServer me.mundane.dagger2learning.bean.ApiServer@2064dd5
05-17 10:51:09.836 15573-15573/me.mundane.dagger2learning D/QualifierActivity: mApiServerTest = me.mundane.dagger2learning.bean.ApiServer@753e28c
05-17 10:51:09.836 15573-15573/me.mundane.dagger2learning D/QualifierActivity: mApiServerRelease = me.mundane.dagger2learning.bean.ApiServer@2064dd5
05-17 10:51:09.836 15573-15573/me.mundane.dagger2learning D/ApiServer: 注册信息

可以看到效果和@Named是一样的。

Provides, Lazy

还是直接上代码
Dog.java

1
2
3
4
5
6
public class Dog {
private static final String TAG = "Dog";
public Dog() {
Log.d(TAG, "Dog构造函数初始化");
}
}

DogComponent.java

1
2
3
4
@Component(modules = DogModule.class)
public interface DogComponent {
void inject(LazyActivity activity);
}

LazyActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class LazyActivity extends AppCompatActivity {
private static final String TAG = "LazyActivity";


@Inject
Lazy<Dog> mDogLazy;//注入Lazy元素

@Inject
Provider<Dog> mDogProvider;//注入Provider元素

@Inject
Dog mDog;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lazy);
DaggerDogComponent.create().inject(this);
Log.d(TAG, "mDog = " + mDog);
Log.d(TAG, "------------------ 我是分割线 ------------------");

// 在这时才创建Dog实例,以后每次调用get会得到同一个Dog对象
Log.d(TAG, "dogLazy1 = " + mDogLazy.get());
Log.d(TAG, "dogLazy2 = " + mDogLazy.get());
Log.d(TAG, "dogLazy3 = " + mDogLazy.get());
Log.d(TAG, "------------------ 我是分割线 ------------------");

// 在这时才创建对象,以后每次调用get会再强制调用对应Module层的Provides方法一次
// 根据Provides方法具体实现不同, 可能返回跟之前是同一个对象,也可能不同。
Log.d(TAG, "dogProvider1 = " + mDogProvider.get());
Log.d(TAG, "dogProvider2 = " + mDogProvider.get());
Log.d(TAG, "dogProvider3 = " + mDogProvider.get());

}
}

打印结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
05-17 11:48:31.858 19046-19046/me.mundane.dagger2learning D/DogModule: provideDog
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/Dog: Dog构造函数初始化
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/LazyActivity: mDog = me.mundane.dagger2learning.bean.Dog@36b1df
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/LazyActivity: ------------------ 我是分割线 ------------------
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/DogModule: provideDog
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/Dog: Dog构造函数初始化
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/LazyActivity: dogLazy1 = me.mundane.dagger2learning.bean.Dog@2f5de2c
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/LazyActivity: dogLazy2 = me.mundane.dagger2learning.bean.Dog@2f5de2c
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/LazyActivity: dogLazy3 = me.mundane.dagger2learning.bean.Dog@2f5de2c
05-17 11:48:31.859 19046-19046/me.mundane.dagger2learning D/LazyActivity: ------------------ 我是分割线 ------------------
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/DogModule: provideDog
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/Dog: Dog构造函数初始化
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/LazyActivity: dogProvider1 = me.mundane.dagger2learning.bean.Dog@e6930f5
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/DogModule: provideDog
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/Dog: Dog构造函数初始化
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/LazyActivity: dogProvider2 = me.mundane.dagger2learning.bean.Dog@232ae8a
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/DogModule: provideDog
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/Dog: Dog构造函数初始化
05-17 11:48:31.860 19046-19046/me.mundane.dagger2learning D/LazyActivity: dogProvider3 = me.mundane.dagger2learning.bean.Dog@97e8cfb
05-17 11:48:31.867 19046-19046/me.mundane.dagger2learning D/ActivityThreadInjector: clearCachedDrawables.
05-17 11:48:31.967 19046-19102/me.mundane.dagger2learning D/OpenGLRenderer: endAllStagingAnimators on 0xa955fb00 (RippleDrawable) with handle 0x9fbbf2c0

可以发现,和普通的@Inject不同,普通的@Inject是在注入后就开始创建对象了,而Lazy和Provider对象只有在调用get()方法的时候才会去调用Module里的provide方法。
区别在于:
Lazy是懒加载,以后每次调用get会得到同一个对象。
Provider不是懒加载,以后每次调用get会再强制调用对应module层的Provides方法一次,根据Provides方法具体实现不同。

@Binds

@Binds类似于@Provides, 区别是@Binds用于修饰抽象类中的抽象方法, 看一下使用实例吧。
Presenter接口和PresenterImpl

Presenter.java

1
2
3
public interface Presenter {
void print();
}

PresenterImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
public class PresenterImpl implements Presenter {
private static final String TAG = "PresenterImpl";

public PresenterImpl() {

}

@Override
public void print() {
Log.d(TAG, "This is PresenterImpl");
}
}

PresenterModule是这里的关键,@Binds也是用在这里的

1
2
3
4
5
6
7
8
9
10
11
// @Module may not contain both non-static @Provides methods
// and abstract @Binds or @Multibinds declarations
// 注意也要用抽象类和抽象方法
@Module
public abstract class PresenterModule {
// 这个方法必须返回接口或抽象类,比如Presenter,不能直接返回PresenterImpl
// 方法的参数类型就是这个方法返回的注入对象的类型,类似@Provides修饰的方法的返回类型
@Binds
abstract Presenter bindPresenter(PresenterImpl presenter);

}

注意到abstract。module类和这个bind方法都是抽象的。参数类型为PresenterImpl,意思就是说,想要注入Presenter, 请找PresenterImpl。
然后我们再写一个PresenterImplModule提供具体的Presenter实现类

1
2
3
4
5
6
7
@Module
public class PresenterImplModule {
@Provides
PresenterImpl providePresenterImpl() {
return new PresenterImpl();
}
}

BindsComponent.java

1
2
3
4
@Component(modules = {PresenterModule.class, PresenterImplModule.class})
public interface BindsComponent {
void inject(BindsActivity activity);
}

BindsActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BindsActivity extends AppCompatActivity {
private static final String TAG = "BindsActivity";

@Inject
Presenter mPresenterImpl;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binds);
DaggerBindsComponent.create().inject(this);
Log.d(TAG, "mPresenterImpl = " + mPresenterImpl);
mPresenterImpl.print();
}
}

打印结果:

1
2
05-17 15:15:06.821 13285-13285/me.mundane.dagger2learning D/BindsActivity: mPresenterImpl = me.mundane.dagger2learning.bean.PresenterImpl@79efdd7
05-17 15:15:06.821 13285-13285/me.mundane.dagger2learning D/PresenterImpl: This is PresenterImpl

可以看到成功注入了一个Presenter的实现类的对象。

需要注意的地方:
@Provides和@Binds这两个不能共存于一个module。

@IntoSet

之前介绍的内容都是单个对象的注入,那么我们是否能将多个对象注入到容器中呢?首先是Set。

IntoSetModule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Module
public class IntoSetModule {

@Provides
@IntoSet
public Player providePlayer2() {
return new Player("2");
}

@Provides
@IntoSet
public Player providePlayer1() {
return new Player("1");
}

}

IntoSetComponent.java

1
2
3
4
@Component(modules = IntoSetModule.class)
public interface IntoSetComponent {
void inject(IntoSetActivity activity);
}

IntoSetActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class IntoSetActivity extends AppCompatActivity {


@Inject
Set<Player> mPlayers;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_into_set);
DaggerIntoSetComponent.create().inject(this);
for (Player player : mPlayers) {
player.play();
}
}
}

打印结果:

1
2
05-17 17:05:42.247 18727-18727/me.mundane.dagger2learning D/Player: 1 is playing 
05-17 17:05:42.247 18727-18727/me.mundane.dagger2learning D/Player: 2 is playing

可以看到这个Set被成功注入了,并且里面包含了两个对象。
当然因为Set是无序的,所以在这个Set中play1在前面还是play2在前面也是不确定的。

进阶用法:
可以同时向 Set 注入多个对象。修改IntoSetModule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Module
public class IntoSetModule {

@Provides
@IntoSet
public Player providePlayer2() {
return new Player("2");
}

@Provides
@IntoSet
public Player providePlayer1() {
return new Player("1");
}

@Provides
@ElementsIntoSet
public Set<Player> providePlayers(){
return new HashSet<>(Arrays.asList(new Player("3"), new Player("4")));
}

}

打印结果:

1
2
3
4
05-17 17:11:57.565 19836-19836/me.mundane.dagger2learning D/Player: 2 is playing 
05-17 17:11:57.565 19836-19836/me.mundane.dagger2learning D/Player: 4 is playing
05-17 17:11:57.565 19836-19836/me.mundane.dagger2learning D/Player: 1 is playing
05-17 17:11:57.565 19836-19836/me.mundane.dagger2learning D/Player: 3 is playing

当然也是无序的。

@IntoMap

@IntoMap其实和@IntoSet类似
IntoMapModule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Module
public class IntoMapModule {

@Provides
@IntoMap
@StringKey(value = "1")
public Player providePlayer1(){
return new Player("1");
}

@Provides
@IntoMap
@StringKey(value = "2")
public Player providePlayer2(){
return new Player("2");
}
}

IntoMapComponent.java

1
2
3
4
@Component(modules = IntoMapModule.class)
public interface IntoMapComponent {
void inject(IntoMapActivity activity);
}

IntoMapActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class IntoMapActivity extends AppCompatActivity {

private static final String TAG = "IntoMapActivity";

@Inject
Map<String, Player> mPlayers;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_into_map);
DaggerIntoMapComponent.create().inject(this);
Log.d(TAG, "mPlayers = " + mPlayers);
for (String s : mPlayers.keySet()) {
Player player = mPlayers.get(s);
Log.d(TAG, "key = " + s + ", value = " + player);
player.play();
}

}
}

打印结果:

1
2
3
4
5
05-17 19:10:04.299 26236-26236/me.mundane.dagger2learning D/IntoMapActivity: mPlayers = {1=me.mundane.dagger2learning.bean.Player@2afd2a8, 2=me.mundane.dagger2learning.bean.Player@cfc11c1}
05-17 19:10:04.300 26236-26236/me.mundane.dagger2learning D/IntoMapActivity: key = 1, value = me.mundane.dagger2learning.bean.Player@2afd2a8
05-17 19:10:04.301 26236-26236/me.mundane.dagger2learning D/Player: 1 is playing
05-17 19:10:04.301 26236-26236/me.mundane.dagger2learning D/IntoMapActivity: key = 2, value = me.mundane.dagger2learning.bean.Player@cfc11c1
05-17 19:10:04.301 26236-26236/me.mundane.dagger2learning D/Player: 2 is playing

可以看到也成功将多个对象注入到了Map容器当中
除了@StringKey以外,目前dagger2预定义的有:

  • @ClassKey
  • @IntKey
  • @LongKey

@MapKey:

我们观察@StringKey的源码可以发现是这样的:

1
2
3
4
5
6
7
@Documented
@Target(METHOD)
@Retention(RUNTIME)
@MapKey
public @interface StringKey {
String value();
}

如果我们要使用@MapKey自定义一个@xxxKey的话,返回类型必须是以下几类:

  • 基本数据类型
  • String
  • Class
  • 枚举类型
  • 注解类型
  • 以上数据类型的数组

但是还可以指定 Enum 类型,或者特定的类的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
enum MyEnum {
ABC, DEF;
}

@MapKey
@interface MyEnumKey {
MyEnum value();
}

@MapKey
@interface MyNumberClassKey {
Class<? extends Number> value();
}

@Module
class MyModule {

@Provides
@IntoMap
@MyEnumKey(MyEnum.ABC)
static String provideABCValue() {
return "value for ABC";
}

@Provides
@IntoMap
@MyNumberClassKey(BigDecimal.class)
static String provideBigDecimalValue() {
return "value for BigDecimal";
}
}

还有个复合键值,但是就暂时写到这里吧。

0%