Dagger2初探(三)

参考:
Dagger2 使用(二)
Dagger 2 : Component.Builder注解有什么用?

在前面两篇
Dagger2 初探 (一)
Dagger2 初探 (二)
的基础上继续.

在前面学习了dagger2的基础之后就要正式开始在Android项目中使用dagger2了, 但是在https://github.com/google/dagger我还看到有一个叫做dagger.android的扩展库, 是为了方便在Android中使dagger而生的, 但是当我在网上找资料去了解如何使用时, 却发现那么多博客讲的是云里雾里, 让人一脸懵逼, 更让人搞不懂的是它的原理.本以为学完前面的几个概念就可以直接上手dagger.android了, 好吧, 我还是一步一步来吧.这篇依然是讲几个概念.后面再专门介绍dagger.android

@Component.Builder和@BindsInstance

先写一个简单的例子
AppComponent.java

1
2
3
4
5
6
7
8
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {

void inject(MainActivity mainActivity);

SharedPreferences getSharedPrefs();
}

AppModule.java

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

private static final String DATA_STORE = "DATA_STORE";

Application application;

public AppModule(Application application) {
this.application = application;
}

@Provides
Application providesApplication() {
return application;
}

@Provides
@Singleton
public SharedPreferences providePreferences() {
return application.getSharedPreferences(DATA_STORE, Context.MODE_PRIVATE);
}

}

在MyApplication中实例化Component

1
2
3
4
5
6
7
8
9
10
11
12
public class MyApplication extends Application {
private static final String TAG = "MyApplication";

@Override
public void onCreate() {
super.onCreate();
AppComponent appComponent =
DaggerAppComponent.builder().appModule(new AppModule(this)).build();
SharedPreferences sharedPrefs = appComponent.getSharedPrefs();
Log.d(TAG, "sharedPrefs = " + sharedPrefs);
}
}

把程序跑起来, 打印结果:

1
D/MyApplication: sharedPrefs = android.app.SharedPreferencesImpl@716e85e

查看自动生成的类DaggerAppComponent.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
36
37
38
39
40
41
42
43
public final class DaggerAppComponent implements AppComponent {
private Provider<SharedPreferences> providePreferencesProvider;

private DaggerAppComponent(Builder builder) {
initialize(builder);
}

public static Builder builder() {
return new Builder();
}

@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.providePreferencesProvider =
DoubleCheck.provider(AppModule_ProvidePreferencesFactory.create(builder.appModule));
}

@Override
public void inject(MainActivity mainActivity) {}

@Override
public SharedPreferences getSharedPrefs() {
return providePreferencesProvider.get();
}

public static final class Builder {
private AppModule appModule;

private Builder() {}

public AppComponent build() {
if (appModule == null) {
throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}

public Builder appModule(AppModule appModule) {
this.appModule = Preconditions.checkNotNull(appModule);
return this;
}
}
}

特别注意里面这个内部类DaggerAppComponent.Builder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static final class Builder {
private AppModule appModule;

private Builder() {}

public AppComponent build() {
if (appModule == null) {
throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}

public Builder appModule(AppModule appModule) {
this.appModule = Preconditions.checkNotNull(appModule);
return this;
}
}

可以看到这个内部类现在有一个成员变量appModule, 另外注意它这个appModule(AppModule appModule)这个方法.

好的, 注意完了.现在让我们使用@Component.Builder@BindsInstance来改写上面的代码

我们看到之前AppModule的构造函数中需要传入一个Application参数, 而使用@BindsInstance和@Component.Builder就会方便一些了.

改写AppModule.java

1
2
3
4
5
6
7
8
9
10
11
@Module
public class AppModule {

private static final String DATA_STORE = "DATA_STORE";

@Provides
@Singleton
public SharedPreferences providePreferences(Application application) {
return application.getSharedPreferences(DATA_STORE, Context.MODE_PRIVATE);
}
}

可以看到providePreferences方法需要一个Application参数.

现在再改写AppComponent.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {

void inject(MainActivity mainActivity);

SharedPreferences getSharedPrefs();

@Component.Builder
interface Builder {

AppComponent build();

@BindsInstance
Builder application(Application application);
}
}

再修改MyApplication中的代码:

1
2
3
4
5
6
7
8
9
10
11
public class MyApplication extends Application {
private static final String TAG = "MyApplication";

@Override
public void onCreate() {
super.onCreate();
AppComponent appComponent = DaggerAppComponent.builder().application(this).build();
SharedPreferences sharedPrefs = appComponent.getSharedPrefs();
Log.d(TAG, "sharedPrefs = " + sharedPrefs);
}
}

可以看到相比之前, 少了appModule(AppModule appModule)方法, 多了application(Application application)方法

查看修改后生成的DaggerAppComponent.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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public final class DaggerAppComponent implements AppComponent {
private Provider<Application> applicationProvider;

private Provider<SharedPreferences> providePreferencesProvider;

private DaggerAppComponent(Builder builder) {
initialize(builder);
}

public static AppComponent.Builder builder() {
return new Builder();
}

@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.applicationProvider = InstanceFactory.create(builder.application);
this.providePreferencesProvider =
DoubleCheck.provider(
AppModule_ProvidePreferencesFactory.create(builder.appModule, applicationProvider));
}

@Override
public void inject(MainActivity mainActivity) {}

@Override
public SharedPreferences getSharedPrefs() {
return providePreferencesProvider.get();
}

private static final class Builder implements AppComponent.Builder {
private AppModule appModule;

private Application application;

@Override
public AppComponent build() {
if (appModule == null) {
this.appModule = new AppModule();
}
if (application == null) {
throw new IllegalStateException(Application.class.getCanonicalName() + " must be set");
}
return new DaggerAppComponent(this);
}

@Override
public Builder application(Application application) {
this.application = Preconditions.checkNotNull(application);
return this;
}
}
}

我们发现DaggerAppComponent.Builder有两个成员变量了, appMoudle 通过默认构造函数生成, 另一个成员变量 application是并通过 @BindsInstance 注解过的方法public Builder application(Application application)来初始化的。

这里面具体的原理我就不深究了
总结:

  1. 如果使用@Component.Builder@BindsInstance来自定义 Builder 类,那么被@BindsInstance注解方法里面的参数在 Builder 类中都有对应的成员变量。

使用场景:

  1. 如果我们的 Moudle 某个被 @Provides 注解的 provide**(xxx obj) 函数使用了某种类型的对象作为参数 (比如 application),而这个参数如果不想通过 Moudle 构造函数传递进来,那么就可以使用 @Component.Builder 来自定义 Builder 类,并通过 @BindsInstance 注解的方法来提供这个参数 obj。
  2. 或者直接给 Component 直接添加一个对象依赖的提供函数。

@Subcomponent.Builder

关于@Subcomponent.Builder, 几篇博客都说和@Component.Builder类似,不过它是在subcomponents中使用, 但是都没有明确说明使用方法.
在开始讲@Subcomponent.Builder之前我们需要先回顾一下@Subcomponent的知识, 直接上例子吧.
FatherComponent.java

1
2
3
4
@Component(modules = FatherModule.class)
public interface FatherComponent {
SonComponent getSonComponent();
}

FatherComponent需要提供一个方法返回SonComponent, 这是为了与SonComponent建立联系.

FatherModule用于提供Father对象, 代码如下:

1
2
3
4
5
6
7
8
@Module
public class FatherModule {

@Provides
Father provideFather() {
return new Father();
}
}

SonComponent.java, 在这里就用到了@Subcomponent

1
2
3
4
@Subcomponent(modules = SonModule.class)
public interface SonComponent {
void inject(SubcomponentActivity activity);
}

SonModule.java用于提供Son对象

1
2
3
4
5
6
7
8
@Module
public class SonModule {

@Provides
Son provideSon() {
return new Son();
}
}

现在目标是要在SubcomponentActivity中同时注入Father对象和Son对象, 需要这样使用:

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

private static final String TAG = "SubcomponentActivity";

@Inject
Father mFather;

@Inject
Son mSon;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_subcomponent);
DaggerFatherComponent.create().getSonComponent().inject(this);
Log.d(TAG, "father = " + mFather);
Log.d(TAG, "son = " + mSon);

}
}

首先获取FatherComponent, 然后通过FatherComponent获取SonComponent, 然后注入activity中

打印结果:

1
2
D/SubcomponentActivity: father = me.mundane.componentbuilder.bean.Father@e847cc9
D/SubcomponentActivity: son = me.mundane.componentbuilder.bean.Son@8e48fce

可以看到注入成功.

现在开始使用@Subcomponent.Builder, 改写上面的代码.

首先是, Son的构造函数变了, 需要一个String类型的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Son {

private static final String TAG = "Son";

private String name;

public Son(String name) {
this.name = name;
}

public void say() {
Log.d(TAG, "my name is " + name);
}
}

然后SonModule.java的代码也变了

1
2
3
4
5
6
7
8
@Module
public class SonModule {

@Provides
Son provideSon(String name) {
return new Son(name);
}
}

接着是SonComponent.java变了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Subcomponent(modules = SonModule.class)
public interface SonComponent {
void inject(SubcomponentActivity activity);

@Subcomponent.Builder
interface Builder {

SonComponent build();

@BindsInstance
SonComponent.Builder setSonName(String name);

}
}

可以看到, SonModule中需要的String参数是从这里传入的.
接着改变FatherComponent

1
2
3
4
@Component(modules = FatherModule.class)
public interface FatherComponent {
SonComponent.Builder getSonComponentBuilder();
}

之前这里返回的类型是子Component, 但是现在, 子Component在构建的时候我们是需要设置参数的, 如果直接返回的就是子Component, 我们还怎么给它设置参数呢?所以我猜测这里返回的应该是一个子Component的builder对象, 然后继续使用这个builder对象传入参数然后最后构建成子Component.
最后是Activity中的代码:

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
public class SubcomponentActivity extends AppCompatActivity {

private static final String TAG = "SubcomponentActivity";

@Inject
Father mFather;

@Inject
Son mSon;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_subcomponent);
DaggerFatherComponent.create()
.getSonComponentBuilder()
.setSonName("mundane")
.build()
.inject(this);
//DaggerFatherComponent.create().getSonComponent().inject(this);
Log.d(TAG, "father = " + mFather);
Log.d(TAG, "son = " + mSon);
mSon.say();

}
}

打印结果:

1
2
3
D/SubcomponentActivity: father = me.mundane.componentbuilder.bean.Father@e847cc9
D/SubcomponentActivity: son = me.mundane.componentbuilder.bean.Son@8e48fce
D/Son: my name is mundane

以上均只是我的个人猜测用法.
唯一找到一篇关于@Subcomponent.Builder的用法的博客是这篇Android_Dagger2 篇——从小白最易上手的角度 + 最新 dagger.android
可以自己写个demo试一下

0%