上一篇介绍了Dagger2简单的使用,这次我们接着介绍稍微复杂一点的使用场景。
首先我需要注入一个书名,代码如下:
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注解。
说到这里,那么问题来了,如果我的两个方法返回值一样的情况下怎么办呢?看代码:
@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依赖另一个,有点类似于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/
