上一篇结尾说道如果有参数的调用应该是怎么来写呢?
最后的报错说明一个类的构造好函数只有一个被@Inject标注
前面说过
@Module:@Module用于封装提供依赖的类。为什么需要专门提供这个注解,因为在很多情况下,我们需要注入的类是第三方类库,我们无法再构造函数直接加@Inject注解,还有一个问题就是如果有多个构造函数的问题,Module封装依赖就是为了解决这些问题。
这里我们就需要使用Module了,首先我们先看下如果是第三方库类的注入
@Module
public class DateModule {
int mid;
public DateModule(int id) {
mid = id;
}
@Provides
public Phone providePhone(){
return new Phone(mid);//这里假设Phone是第三方库里面的类
}
@Provides
public Book provideBook(){
return new Book(mid);//这里假设Book是第三方库里面的类
}
}
这里新建一个DataModule类用@Module标注
他有两个方法providePhone和provideBook都用@Provides标注,然后接下来还需要一个步骤继续看BookComponent
@Component(modules = {DateModule.class})
public interface BookComponent {
void inject(MainActivity activity);
}
这里@Component加了参数modules参数,可以表明对外提供的是那个Module,这里可以写多个Module
下面我们就看下调用方式
public class MainActivity extends AppCompatActivity {
@Inject
Book mBook;
@Inject
Phone mPhone;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerBookComponent.builder()
.dateModule(new DateModule(0))
.build()
.inject(this);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("wangyudong", "mBook: " + mBook.toString());
Log.e("wangyudong", "mPhone: " + mPhone.toString());
}
});
}
}
可以看出来区别就在于这里的DaggerBookComponent的Builder需要传入一个DateModule作为参数,来初始化Dagger
到这里就可以完成第三方类的注入了
2.第二个问题,如果有多个构造器呢?
这里就需要用到一个新的注解
@Qulifier:@Qulifier用于自定义注解,也就是说@Qulifier就如同Java提供的几种基本元注解一样用来标记注解类。我们在使用@Module来标注提供依赖的方法时,方法名我们是可以随便定义的(虽然我们定义方法名一般以provide开头,但这并不是强制的,只是为了增加可读性而已)。那么Dagger2怎么知道这个方法是为谁提供依赖呢?答案就是返回值的类型,Dagger2根据返回值的类型来决定为哪个被@Inject标记了的变量赋值。但是问题来了,一旦有多个一样的返回类型Dagger2就懵逼了。@Qulifier的存在正式为了解决这个问题,我们使用@Qulifier来定义自己的注解,然后通过自定义的注解去标注提供依赖的方法和依赖需求方(也就是被@Inject标注的变量),这样Dagger2就知道为谁提供依赖了。----一个更为精简的定义:当类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示;
一句话这个注解是一个原注解,用来生成自定义注解,因为Dagger解析的时候是通过@provide的返回值来匹配的,如果有多个函数返回值一样,dagger就不知道该用那个来生成实例了,因此用不同的自定义注解,把相同返回值类型的方法区别开来
正式用一下,自定义两个注解
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BookQualifierC1 {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BookQualifierC2 {
}
使用自定义注解
@Module
public class DateModule {
int mid;
String mName;
public DateModule(int id, String name) {
mid = id;
mName = name;
}
@Provides
public Phone providePhone(){
return new Phone(mid);
}
@BookQualifierC1
@Provides
public Book provideBookC1(){
return new Book(mid);
}
@BookQualifierC2
@Provides
public Book provideBookC2(){
return new Book(mid, mName);
}
}
用自定义注解标记变量
public class MainActivity extends AppCompatActivity {
@BookQualifierC2 @Inject Book mBook;
@Inject
Phone mPhone;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerBookComponent.builder()
.dateModule(new DateModule(0, "哈利波特"))
.build()
.inject(this);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("wangyudong", "mBook: " + mBook.mName);
Log.e("wangyudong", "mPhone: " + mPhone.toString());
}
});
}
}
这样就mBook的实例就是通过调用两个参数的构造函数来获取的
到此就基本可以满足大多数的对象的注入了
3.最后 还有两个注解@Scope和@SingleTon
Scope中文意思范围的意思,我能可以通过@Scope自定义的注解来限定注解作用域,实现局部的单例;
@Singleton 其实就是一个通过@Scope定义的注解,我们一般通过它来实现全局单例。但实际上它并不能提前全局单例,是否能提供全局单例还要取决于对应的Component是否为一个全局对象。一般都是通过在Application里面通过Builder产品Daggerxxx类,然后其他地方都调用这个Component来作为一个全局的对象来使用,保证了真正的单例