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的引用会报错。相关文章推荐
- Android开发Dagger2入门
- Dagger2入门,以初学者角度来观察
- Android Dagger2入门详解(一)
- Dagger2入门
- Dagger2入门,以初学者角度来观察
- Dagger 2: Step To Step 入门
- Android -- 带你从源码角度领悟Dagger2入门到放弃(二)
- Dagger 2 入门
- Dagger2入门,以初学者角度来观察
- Dagger2 入门解析
- “一盘沙拉”带你入门Dagger2(二)之带参数怎么办
- Dagger2 这次入门就不用放弃了
- “一盘沙拉”带你入门Dagger2(六)之Component依赖
- Android Dagger2入门详解(六)
- Dagger2入门详解
- Dagger2实现依赖注入之一步一步带你入门
- Dagger2入门,以初学者角度来观察
- Dagger2入门
- Android之dagger2的简单运用和详细解读(入门)
- Dagger2入门,以初学者角度来观察