您的位置:首页 > 其它

Dagger2入门

2016-10-18 13:08 330 查看

一、为什么要使用Dagger2

Dagger2是一个Android端的依赖注入框架,如果你不了解什么是依赖注入的话,我想你看这篇文章也没有必要,当然如果是为了学习新技术,推荐去google一下什么叫依赖注入,有很多好文章,这篇介绍的是dagger2的使用,就不介绍什么是依赖注入了和依赖注入的好处了。

二、基本使用

使用时我们要有一个基本思想,所谓的依赖注入是事先把要用到的对象实例化,然后利用注入器注入到要用这个对象的地方。所以我们要做两件事,第一,实例化对象。第二,写一个构造器。

1、利用@Inject直接定义构造方法

现在有这样一个类

public class Animal {
private String name;
public Animal(){
this.name ="puppy";
}
}

我们要注入这样一个类,可以分一下几步走

1)利用@Inject注解构造方法,这一步就是直接宣告这个类可以通过这个构造方法实例化。

public class Animal {
private String name;

@Inject
public Animal(){
this.name ="puppy";
}

public String getName(){
return name;
}
}

2)利用@Component声明一个注入器

@Component()
public interface AnimalMainComponet {
void inject( MainActivity mainActivity);
}
如果想要声明一个注入器,那么我们要创建一个接口,利用@Component声明,然后写一个inject方法,传入的必须是要注入的类的强类型,上面这个例子说明我们想注入到MainActivity中,注意,这里的方法可以随意命名,不会影响。当然,除了使用接口,我们也可以使用抽象类:

@Component()
public  abstract class AnimalMainComponet {
abstract void inject( MainActivity mainActivity);
}

3)开始注入

因为dagger2是基于编译期生成代码的,所以先编译一下,就会自动生成一个以Dagger开头的类,利用这个类来注入

public class MainActivity extends AppCompatActivity {

@Inject Animal animal;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.tv);
DaggerAnimalMainComponet.builder().build().inject(this);
tv.setText(animal.getName());

}
}
利用@Inject直接声明下Animal,然后利用DaggerAnimalMainCompomet注入下就可以使用animal了。运行结果



利用@Inject直接注入构造方法非常简单,但是有缺点

public class User {
private String name;
private String age;

public User(Car car){
this.name =car.getName();
this.age ="345";
}

public User(String name,String age){
this.name =name;
this.age = age;
}

public String getInfo(){
return name+" "+age;
}
}
这里有两个构造方法,但是,@Inject只能用来指明使用一个构造方法注入,而且这个方法还必须是无参的,在这里就不能这么用了。另外,我们经常用到第三方的包,是不可能修改第三方包中的构造方法的。所以这种注入方式虽然简单,但是不怎么用。我们接着介绍第二种方法:

2、利用@Module,@Provides注入。

还是以这个Animal为例,假设我么无法修改这个Animal类:

public class Animal {
private String name;
public Animal(){
this.name ="puppy";
}
}


我们按照以下步骤来

1)利用@Module声明一个类:这步是为了宣告这个类可以用来提供实例化好了的对象

@Module
public class AnimalModule {

}

2)在@Module类中利用@Provides来预先生成要用的对象。

@Module
public class AnimalModule {
@Provides
Animal provideAnimal(){
return new Animal();
}
}


这里真正提供Animal对象的是利用@Provides注解的provideAnimal方法,需要注意的是提供对象的方法必须以provide开头。

3)利用@Component声明注入器

@Component(modules = AnimalModule.class)
public  interface AnimalMainComponet {
void inject( MainActivity mainActivity);
}


这里和上面不同的是要指明我们从哪里获取需要的对象,用的就是
@Component(modules = AnimalModule.class)
如果这里要从多个地方获取不同的对象,可以这样写 

@Component(modules = {AnimalModule.class,CarModule.class,UserModule.class})


4)开始注入

public class MainActivity extends AppCompatActivity {

@Inject Animal animal;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.tv);
DaggerAnimalMainComponet.builder().build().inject(this);
tv.setText(animal.getName());

}
}
真正注入的实现和第一种方法中提到的是一模一样的。

三、复杂应用

 1. 构造方法带参数怎么办

现在有这样一个类

public class Car {
private String name;
public Car(String name){
this.name = name;
}

public String getName(){
return name;
}
}
用上面的第一种方法肯定不行,那让我们用第二种方法来写

写一个@Module,并且提供一个@Provides方法

@Module
public class CarModule {
@Provides
Car provideCar(){
return new Car("宝马");
}
}


这里问题来了,我们只能在这部把传入的参数写死了,但是我想在用的时候再传入参数怎么办呢?可以利用CarModule传入

@Module
public class CarModule {
private String inputCarName;
public CarModule(String name){
inputCarName = name;
}
@Provides
Car provideCar(){
return new Car(inputCarName);
}
}


这里我们在CarModule中定义了一个参数用来接收传入的参数,然后在provideCar中使用,在MainActivity中这样使用

public class MainActivity extends AppCompatActivity {

@Inject Car car;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.tv);
DaggerCarMainComponent.builder().carModule(new CarModule("奔驰")).build().inject(this);
tv.setText(car.getName());

}
}


和之前的比起来,注入的时候多了一个carModule方法,这里我们可以实例化自己的CarModule,随意传入参数。看下运行结果



2. 有多个构造方法怎么办

有这样一个类要注入:

public class User {
private String name;
private int age;
public User(int age){
this.age =age;
name = "defaultName";
}

public User(String name){
this.name =name;
this.age =99;
}

public String getInfo(){
return name+" "+age;
}
}
这个类中有两个构造方法,那么我们在@Module中提供实例时也应该提供两个不同的方法,分别用不同的构造方法来生成对象,比如这样

@Provides
public User provideUserByName(){
return new User(name);
}

@Provides
public User provideUserByAge(){
return new User(age);
}


但是,很不幸,这样会报错,因为Dagger2框架不知道应该选哪个方法去生成User对象。知道原因就好解决了,我们可以用@Name注解表明下

@Module
public class UserModule {

private String name;
private  int age;
public UserModule(String name,int age){
this.name =name;
this.age =age;
}

@Named("ByName")
@Provides
public User provideUserByName(){
return new User(name);
}

@Named("ByAge")
@Provides
public User provideUserByAge(){
return new User(age);
}

}


在使用的时候可以这样用

public class MainActivity extends AppCompatActivity {

@Named("ByName")
@Inject
User user1;

@Named("ByAge")
@Inject
User user2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.tv);
DaggerUserMainActivityComponent.builder().userModule(new UserModule("hello",100)).build().inject(this);
tv.setText("ByName: "+user1.getInfo()+"  ByAge: "+user2.getInfo());

}
}
这样就知道user1 是用了provideUserByName()这个方法生成,user2是用了provideUserByAge()这个方法生成的。这样问题确实解决了,但是字符串很容易出错啊,一不小心拼错了怎么办?  为了更方便的使用,我们可以使用@Qualifier 注解来自定义一个注解,达到上面的字符串一样的效果。

首先自定义两个注解来代替上面的两个字符串:

用来代替ByName字符串的注解@ByName

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ByName {
}
以及用来代替ByAge字符串的注解@ByAge

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ByAge {
}


用法这样的,在@Module中

@ByName
@Provides
public User provideUserByName(){
return new User(name);
}

@ByAge
@Provides
public User provideUserByAge(){
return new User(age);
}


在使用时

@ByName
@Inject
User user1;

@ByAge
@Inject
User user2;

3. 构造方法的参数中有要靠依赖注入的对象



先看下我们的项目结构,我们现在有三个类可以实现依赖注入了,现在我们有这样一个类

public class InfoUtil {
public Animal animal;
public Car car;
public User user;

public InfoUtil(Animal animal) {
this.animal = animal;

}

public InfoUtil(Car car) {
this.car = car;
}

public InfoUtil(User user) {
this.user = user;
}

public String getAnimalInfo(){
return "the name of animal is : "+animal.getName();
}

public String getCarInfo(){
return "the name of car is :"+car.getName();
}

public String getUserInfo(){
return ""+user.getInfo();
}

}


我们来看看这个类的module怎么写

@Module
public class InfoUtilModule {

@ByAnimal
@Provides
public InfoUtil provideInfoUtilByAnimal(Animal animal){
return new InfoUtil(animal);
}

@ByUser
@Provides
public InfoUtil provideInfoUtilByUser(@ByName User user){
return new InfoUtil(user);
}

@ByCar
@Provides
public InfoUtil provideInfoUtilByCar(Car car){
return new InfoUtil(car);
}
}


可以看到,这里定义了三个不同的注解来区分用不同的参数构造InfoUtil这个类,注意provideInfoUtilByUser这个方法,它的参数需要显示制定用那种方式注入,这里用的是@ByName。那么这些方法中的Animal ,User,Car从哪里来呢? 这里框架会自动从建立的依赖关系图中去获取,所以我们在写Component的时候就要指定提供这些对象的Module类,如下

@Component(modules = {InfoUtilModule.class, AnimalModule.class, CarModule.class, UserModule.class})
public interface InfoUtilComponent {
public void inject(MainActivity mainActivity);
}

四、巨坑

同一个Activity只能在一个Component中注入,否则会报错,就是说如果在不同Component中的 inject方法中传入了同一个Activity的引用会报错。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  依赖注入 Dagger2