上一篇介绍了Dagger2简单的使用,这次我们接着介绍稍微复杂一点的使用场景。
module的provide方法带参数
首先我需要注入一个书名,代码如下:
public class MainActivity extends AppCompatActivity {
@Inject
String bookName;
}
然后我们接着编写module的代码
@Module
public class ActivityModule {
@Provides
String provideName(BookBean book){
return book.getTitle();
}
@Provides
BookBean provideBook(){
BookBean bookBean = new BookBean();
bookBean.setTitle("c++从入门到放弃");
return bookBean;
}
@Provides
PeopleBean providePeople(){
People peopleBean = new PeopleBean();
peopleBean.setTitle("mike");
return peopleBean;
}
}
我们可以看到provideName(BookBean book)
方法是需要有一个BookBean的参数,当我们需要注入书名时,是通过传入的BookBean获取的。那这个BookBean怎么来的呢?大家可能已经注意到了下面还有一个provideBook()
方法的返回类型是BookBean。没错,Dagger会自动寻找返回值为BookBean的方法,并将其返回的参数传入到provideName(BookBean book)
方法中。这就是为什么不是调用providePeople()
方法了,因为返回值不匹配。但是必须有一个条件,就是provideBook()
必须被@Provides注解。
@Qualifier注解
说到这里,那么问题来了,如果我的两个方法返回值一样的情况下怎么办呢?看代码:
@Module
public class ActivityModule {
@Provides
String provideName(BookBean book){
return book.getTitle();
}
@Provides
BookBean provideBook(){
BookBean bookBean = new BookBean();
bookBean.setTitle("c++从入门到放弃");
return bookBean;
}
@Provides
BookBean provideBook2(){
BookBean bookBean = new BookBean();
bookBean.setTitle("android从入门到放弃");
return bookBean;
}
}
这里就要介绍一个新的注解了@Qualifier。
首先我们先自定义两个注解,并用@Qualifier修饰
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Android {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Cpp {
}
然后修改module代码
@Provides
@Singleton
@Cpp
BookBean provideBook() {
BookBean bookBean = new BookBean();
bookBean.setTitle("c++从入门到放弃");
return bookBean;
}
@Provides
@Singleton
@Android
BookBean provideBook2() {
BookBean bookBean = new BookBean();
bookBean.setTitle("Android从入门到放弃");
return bookBean;
}
可以看到,只是添加了刚才自定义的注解。这就相当于给他们取了一个别名。
最后修改module的provideName
@Provides
String provideName(@Android BookBean book){
return book.getTitle();
}
注意看,参数前面增加了一个@Android注解。
至此,@Qualifier的用法就介绍完了。值得注意的是接下来讲的Component依赖有一点不同,会在下文介绍。
Component添加依赖
Component依赖就是指一个Component依赖另一个,有点类似于java的继承。
首先,我们先来写要被依赖的Component
这个例子里,我们模拟网络请求。所以需要依赖注入一个Retrofit,同时要使注入的Retrofit的生命周期和app保持一致,还好是一个单例。这时候我们要使用到两个新的注解@Singleton、@Scope。
先看MainActivity的代码
public class MainActivity extends AppCompatActivity {
@Inject
Retrofit retrofit;
@Inject
String bookName;
...
}
这里我们就是向服务器请求数据并打印出来。还有就是把注入的书名显示在textview上,这个主要作用后面再说。
接下来看被依赖的Component和Module的代码:
@Module
public class AppModule {
private Retrofit retrofit;
@Provides
@Singleton
OkHttpClient provideOkHttpClient() {
return new OkHttpClient.Builder().build();
}
@Provides
@Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient) {
return retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("http://api.douban.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
// 演示@Qualifier在Component依赖时的使用
@Provides
@Singleton
@Cpp
BookBean provideBook() {
BookBean bookBean = new BookBean();
bookBean.setTitle("c++从入门到放弃");
return bookBean;
}
@Provides
@Singleton
@Android
BookBean provideBook2() {
BookBean bookBean = new BookBean();
bookBean.setTitle("Android从入门到放弃");
return bookBean;
}
}
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
OkHttpClient getClient();
Retrofit getRetrofit();
@Android
BookBean getBook();
}
module中有三个参数的生成,都是单例。然后在Component中提供方法将module中的生成暴露给子图(这正是Dagger中 components工作的重要性质:如果你不想把modules的类型暴露出来,那么你就只能显示地使用它们。在这个例子中,我把这些元素暴露给子图, 如果你把他们删掉,编译的时候就会报错。这里不理解就先记着要这么写就行了)。
(BookBean getBook();
被@Android修饰了,这就是前面提到的有所不同的就是如果你给方法取了别名,这里暴露的方法也需要被相同的注解修饰。)
然后来写真正的Component:
@ActivityScope
@Component(modules = ActivityModule.class, dependencies = AppComponent.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}
这里多了一个@ActivityScope
注解,这是一个我自定义的注解,看代码:
@Scope
public @interface ActivityScope {
}
它的作用是为了注入的Retrofit和app的生命周期保持一致,与Component的依赖无关。(至于@Scope我也没理解)
这里看到多加了一条dependencies = AppComponent.class
,这就是将需要依赖的Component添加进去。
module的代码:
@Module
public class ActivityModule {
@Provides
String provideName(@Android BookBean book){
return book.getTitle();
}
}
然后module就没什么变化了,但是这里我加了provideName(BookBean book)
方法。这是给MainActivity
中的String bookName
生成注入。而provideName(BookBean book)
中的BookBean则来自AppModule
中的BookBean provideBook()
方法返回的值。也就是说带参的provide方法不一定要在同一个module中,也可以在被依赖的module中。
最后,在MainActivity中使用
public class MainActivity extends AppCompatActivity {
@Inject
Retrofit retrofit;
@Inject
String bookName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
inject();
Github github = retrofit.create(Github.class);
github.htmls("1220562")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<BookBean>() {
@Override
public void call(BookBean bookBean) {
System.out.println(bookBean);
}
});
TextView tv = (TextView) findViewById(R.id.textview);
tv.setText(bookName);
}
private void inject() {
DaggerActivityComponent.builder()
.appComponent(DaggerAppComponent.create())
.activityModule(new ActivityModule())
.build().inject(this);
}
}
主要看inject()
方法的代码,可以看到我们先生成了一个DaggerActivityComponent.builder()
然后调用了appComponent()
方法,此时传入的就是要依赖的Component,这里就是DaggerAppComponent.create()
,其他的就和以前一样了。
参考文章:http://www.jianshu.com/p/05ad9ad8a3e8
https://dreamerhome.github.io/2016/07/11/dagger%20for%20code/