依赖注入概念和Dagger2框架使用介绍

一、 依赖注入基本概念

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。

依赖注入(Dependency Injection)是指程序运行时,若需要调用另一个对象,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序,依赖注入是目前最佳的解耦方式。

如果一个类A的功能实现需要借助于类B,那么就称类B是类A的依赖。如果在类A的内部去实例化类B,那么两者之间会出现较高的耦合,一旦类B出现了问题,类A也需要进行改造,如果这样的情况较多,每个类之间都有很多依赖,那么就会出现牵一发而动全身的情况,程序会极难维护,并且很容易出现问题。

要解决这个问题,就要把A类对B类的控制权抽离出来,交给一个第三方去做,把控制权反转给第三方就称作控制反转。控制反转是一种思想,是能够解决问题的一种可能的结果。
依赖注入(Dependency Injection)就是其最典型的实现方法。由第三方(我们称作IOC容器)来控制依赖,把他通过构造函数、属性或者工厂模式等方法,注入到类A内,这样就极大程度的对类A和类B进行了解耦。

依赖注入概念和Dagger2框架使用介绍_第1张图片

二、 如何使用Dagger2

Dagger2的使用比较复杂,先来看最简单的用法,主要分成三步:

  • 使用@Inject注解需要生成的目标对象和其构造方法;
  • 创建Component接口,作用是将产生的对象注入到需要对象的容器中;
  • 容器调用Component 的creat()和inject()方法将产生的对象注入到自己体内。
public class Company {

    @Inject
    public Company() {
    }
}

注意:构造方法需要使用@Inject注解修饰

MainActivity使用@Inject注解myCompany:

1.  public class MainActivity extends AppCompatActivity {  
2.    
3.      private static final String TAG = "Dagger2_Test";  
4.      @Inject  
5.      public Company myCompany;  
6.    
7.      @Override  
8.      protected void onCreate(Bundle savedInstanceState) {  
9.          super.onCreate(savedInstanceState);  
10.         setContentView(R.layout.activity_main);  
11.         //onCreate()中添加如下语句:  
12.         DaggerDagger2TestComponent.create()  
13.                 .inject(this);  
14.         Log.d(TAG,"myCompany = " + myCompany.toString());  
15.     }  
16. }

添加Component注解:

@Component
public interface Dagger2TestComponent {
    void inject(MainActivitymainActivity);
}

运行结果,看到myCompany已经实例化成功:

Dagger2_Test: myCompany = com.example.xxx.dagger2ver.Company@50e3e70

三、Dagger2 的原理介绍

Dagger2的原理主要分析自动生成代码的逻辑,通过介绍,已经了解注解的基本使用,Dagger2注解处理器会通过@Inject和@Component会自动生成如下三个java文件:
Company_Factory.java

1.  public final class Company_Factory implements Factory {  
2.    private static final Company_Factory INSTANCE = new Company_Factory();  
3.    
4.    @Override  
5.    public Company get() {  
6.      return provideInstance();  
7.    }  
8.    
9.    public static Company provideInstance() {  
10.     return new Company();  
11.   }  
12.   
13.   public static Company_Factory create() {  
14.     return INSTANCE;  
15.   }  
16.   
17.   public static Company newCompany() {  
18.     return new Company();  
19.   }  
20. }  

MainActivity_MembersInjector.java:

1.  public final class MainActivity_MembersInjector implements MembersInjector {  
2.    private final Provider myCompanyProvider;  
3.    
4.    public MainActivity_MembersInjector(Provider myCompanyProvider) {  
5.      this.myCompanyProvider = myCompanyProvider;  
6.    }  
7.    
8.    public static MembersInjector create(Provider myCompanyProvider) {  
9.      return new MainActivity_MembersInjector(myCompanyProvider);  
10.   }  
11.   
12.   @Override  
13.   public void injectMembers(MainActivity instance) {  
14.     injectMyCompany(instance, myCompanyProvider.get());  
15.   }  
16.   
17.   public static void injectMyCompany(MainActivity instance, Company myCompany) {  
18.     instance.myCompany = myCompany;  
19.   }  
20. }  

DaggerDagger2TestComponent.java:

1.  public final class DaggerDagger2TestComponent implements Dagger2TestComponent {  
2.    private DaggerDagger2TestComponent(Builder builder) {}  
3.    
4.    public static Builder builder() {  
5.      return new Builder();  
6.    }  
7.    
8.    public static Dagger2TestComponent create() {  
9.      return new Builder().build();  
10.   }  
11.   
12.   @Override  
13.   public void inject(MainActivity mainActivity) {  
14.     injectMainActivity(mainActivity);  
15.   }  
16.   
17.   private MainActivity injectMainActivity(MainActivity instance) {  
18.     MainActivity_MembersInjector.injectMyCompany(instance, new Company());  
19.     return instance;  
20.   }  
21.   
22.   public static final class Builder {  
23.     private Builder() {}  
24.   
25.     public Dagger2TestComponent build() {  
26.       return new DaggerDagger2TestComponent(this);  
27.     }  
28.   }  
29. }  

四、Dagger2 详细使用介绍

目标对象构造方法带参数

上面是介绍通过Daggers框架的@Inject和@Component 注解,目标对象的简单方法。
现在如果Company构造方法需要传参,该如何实现呢?

1.  public class Company {  
2.      private String mName = null;  
3.    
4.      public Company(String name) {  
5.          this.mName = name;  
6.      }
7.  }

答案是通过Dagger2提供的@Module注解,@Module注解的作用是提供Component需要的依赖。
创建Dagger2TestModule01.java

1.  @Module  
2.  public class Dagger2TestModule01 {  
3.    
4.      @Provides  
5.      public Company provideCompany(){  
6.          return  new Company("google");  
7.      }  
8.  }

此时查看generated文件夹中自动生成的类DaggerDagger2TestComponent.java:

1.  DaggerDagger2TestComponent.java  
2.    @Override  
3.    public void inject(MainActivity activity) {  
4.      injectMainActivity(activity);  
5.    }  
6.    
7.    private MainActivity injectMainActivity(MainActivity instance) {  
8.      MainActivity_MembersInjector.injectMyCompany(  
9.          instance,    /*MainActivity对象*/  
10.         /*需要注入的company对象*/  
11.        Dagger2TestModule01_ProvideCompanyFactory.proxyProvideCompany(dagger2TestModule01));  
12.     return instance;  
13.   }  
14.   
15.   public static final class Builder {  
16.     private Dagger2TestModule01 dagger2TestModule01;  
17.   
18.     private Builder() {}  
19.   
20.     public Dagger2TestComponent build() {  
21.       if (dagger2TestModule01 == null) {  
22.         this.dagger2TestModule01 = new Dagger2TestModule01();  
23.       }  
24.       return new DaggerDagger2TestComponent(this);  
25.     }  
26.   
27.     public Builder dagger2TestModule01(Dagger2TestModule01 dagger2TestModule01) {  
28.       this.dagger2TestModule01 = Preconditions.checkNotNull(dagger2TestModule01);  
29.       return this;  
30.     }  
31.   } 

Dagger2TestModule01_ProvideCompanyFactory.java

1.  public final class Dagger2TestModule01_ProvideCompanyFactory implements Factory {  
2.    private final Dagger2TestModule01 module;  
3.    
4.    public Dagger2TestModule01_ProvideCompanyFactory(Dagger2TestModule01 module) {  
5.      this.module = module;  
6.    }  
7.    
8.    @Override  
9.    public Company get() {  
10.     return provideInstance(module);  
11.   }  
12.   
13.   public static Company provideInstance(Dagger2TestModule01 module) {  
14.     return proxyProvideCompany(module);  
15.   }  
16.   
17.   public static Dagger2TestModule01_ProvideCompanyFactory create(Dagger2TestModule01 module) {  
18.     return new Dagger2TestModule01_ProvideCompanyFactory(module);  
19.   }  
20.   
21.   public static Company proxyProvideCompany(Dagger2TestModule01 instance) {  
22.     return Preconditions.checkNotNull(  
23.         //这里就会调用到Dagger2TestModule01添加的provideCompany()方法  
24.         instance.provideCompany(), "Cannot return null from a non-@Nullable @Provides method");  
25.   }  
26. }  

Module中方法带参数

上一节是构造方法带参数,如果是Module中提供目标对象的方法需要传参,该如何处理?
如下情形:

1.  @Module  
2.  public class Dagger2TestModule01 {  
3.  @Provides  
4.  //provideCompany()方法需要传入company name的参数  
5.  public Company provideCompany(String name){  
6.      return  new Company(name);  
7.  }  
8.  
9.  @Provides  
10. //在Module中提供一个返回值为String的方法  
11. public String companyName(){  
12.     return "facebook";  
13. }  
14. } 

Dagger在Module中执行provideCompany()时,发现需要一个String类型的参数,接着Dagger就会在Module中搜索返回值为String的方法并执行。

@Named注解的使用

如果现在Company中有两个构造方法,MainActivity需要生成两个不同的对象,该如何实现呢?

1.  public Company(String name) {  
2.      this.mName = name;  
3.  }  
4.    
5.  public Company(String name, int number) {  
6.      this.mName = name;  
7.      this.mNumber = number;  
8.  }  

方法是通过@Named注解,在Dagger2TestModule01中定义两个不同的方法,来返回不同的Company对象,并且加上@Named("TypeX")区分:

1.  @Module  
2.  public class Dagger2TestModule01 {  
3.    
4.      @Named("Type1")  
5.      @Provides  
6.      public Company provideCompany(){  
7.          return  new Company("Google");  
8.      }  
9.        
10.     @Named("Type2")  
11.     @Provides  
12.     public Company provideCompany2(){  
13.         return  new Company("Facebook",20000);  
14.     }  
15. } 

MainActivity中也通过@Named("TypeX")来声明不同的Company对象:

1.  @Named("Type1")  
2.  @Inject  
3.  public Company myCompany;  
4.    
5.  @Named("Type2")  
6.  @Inject  
7.  public Company myCompany2;  

局部单例和全局单例

开发中,经常会遇到创建Activity/Fragment级单例或者Application级单例的情形,Activity/Fragment级单例一般称为局部单例,Application级单例称为全局单例。
通过@Scope注解,就可以实现此功能,@Scope可以管理所创建对象的「生命周期」,这里的「生命周期」是与Component 相关联,与Activity等任何Android组件没有任何关系。

局部单例

  1. 首先,声明一个自定义@Scope注解:
1.  ActivityScope.java  
2.  @Scope  
3.  @Retention(RUNTIME)  
4.  public @interface ActivityScope {  
5.  } 
  1. MainActivity中新增两departmen对象,并用@ActivityScope 来修饰,用来和Company对象做对比验证:
    MainActivity.java
1.  @Inject public Company myCompany;  
2.  @Inject public Company myCompany3;  
3.    
4.  @Inject public Department de1;  
5.  @Inject public Department de2;  
6.  @Provides  
7.  public Company provideCompany(){  
8.      return  new Company();  
9.  }  
10.   
11. @ActivityScope  
12. @Provides  
13. public Department provideDepartment(){  
14.     return  new Department();  
15. } 
  1. Dagger2TestComponent 也添加@ActivityScope 修饰:
1.  @ActivityScope  
2.  @Component(modules = Dagger2TestModule01.class)  
3.  public interface Dagger2TestComponent {  
4.      void inject(MainActivity activity);  
5.  }

查看运行结果:

可以看到,两个company对象不同,但两个department是同一对象,通过上面的代码发现,只是增添了@ActivityScope注解就实现了Activity中的局部单例。

全局单例

新增SubActivity,以及其Module和Component,SubActivity中也声明Department对象,用来验证是否全局单例:
SubActivity.java

1.  @Inject public Department de1;

通过log打印发现SubActivity和MainActivity的department并不是同一对象,原因是因为两者的Component不同,现在要实现全局单例应该怎么做呢?

如上文提到,因为「生命周期」是与Component相关联,所以如果要实现全局单例,就应该创建全局的Component,一个应用中只有Application对象是全局唯一的,我们从这里入手。
先定义全局Module和Component:

1.  @Module  
2.  public class ApplicaionModule {  
3.    
4.      private MyApplication application;  
5.    
6.      public ApplicaionModule(MyApplication application) {  
7.          this.application = application;  
8.      }  
9.    
10.     @Singleton  
11.     @Provides  
12.     Company provideCompany(){  
13.         return new Company();  
14.     }  
15. }
1.  @Singleton //Dagger2提供的单例注解
2.  @Component (modules = ApplicaionModule.class)  
3.  public interface ApplicationComponent {  
4.      //这里告知需要返回的全局单例对象,不然会报错  
5.      Company getCompany();  
6.  } 

新建ComponentHolder.java,给各个Activity/Fragment提供全局唯一ApplicationComponent:

1.  public class ComponentHolder {  
2.      private static ApplicationComponent myAppComponent;  
3.    
4.      public static void setAppComponent(ApplicationComponent component) {  
5.          myAppComponent = component;  
6.      }  
7.    
8.      public static ApplicationComponent getAppComponent() {  
9.          return myAppComponent;  
10.     }  
11. }
12.   
13. public class MyApplication extends Application {  
14.     private static final String TAG = "Dagger2_Test";  
15.   
16.     @Override  
17.     public void onCreate() {  
18.         super.onCreate();  
19.         //生成ApplicationComponent对象,并设置到ComponentHolder中,供Activity获取  
20.         ApplicationComponent appComponent = DaggerApplicationComponent.builder()  
21.                 .applicaionModule(new ApplicaionModule(this))  
22.                 .build();  
23.         ComponentHolder.setAppComponent(appComponent);  
24.     }  
25. }

修改Dagger2TestModule01.java和Dagger2TestModule02.java,注释掉其中返回company对象的方法,因为已经通过ApplicaionModule.java来提供:

1.  @Module  
2.  public class Dagger2TestModule01 {  
3.  //    @ActivityScope  
4.  //    @Provides  
5.  //    public Company provideCompany(){  
6.  //        return  new Company();  
7.  //    }  
8.  }  
9.  @Module  
10. public class ModuleSubActivity {  
11.   //同上  
12. } 

因为现在依赖ApplicationComponent提供目标对象,所以要在MainActivity和SubActivity对应的Component中添加@ActivityScope和dependencies = ApplicationComponent.class:

1.  @ActivityScope  
2.  @Component(modules = Dagger2TestModule01.class, dependencies = ApplicationComponent.class) 
3.  public interface Dagger2TestComponent {  
4.      void inject(MainActivity activity);  
5.  } 

MainActivity.java 和SubActivity.java 的添加一句:

1.  DaggerDagger2TestComponent.builder()  
2.           // 添加如下语句,把ApplicationComponent传入到自己的Component中  
3.          .applicationComponent(ComponentHolder.getAppComponent())  
4.          .build()  
5.          .inject(this); 

查看运行结果:

依赖注入概念和Dagger2框架使用介绍_第2张图片

可以看到,两个Activity中的company都是同一对象,实现了全局单例。
现在来分析@Singleton注解的实现和含义:
Singleton.java

1.  @Scope  
2.  @Documented  
3.  @Retention(RUNTIME)  
4.  public @interface Singleton {}  

从实现看@Singleton也是一个Scope,并且和上面我们自定义的ActivityScope.java并无区别,只是两者名字不一样,有助于理解代码而已。

如果说有差异的话,就是@Singleton修饰的Component不能再依赖其它Component,如上面我们定义的Dagger2TestComponent依赖于ApplicationComponent,但是ApplicationComponent不能依赖Dagger2TestComponent

再来看MainActivity的Component:Dagger2TestComponent,和之前相比,多了dependencies = ApplicationComponent.class,这一句的作用就是告知Dagger2框架,MainActivity的component需要依赖ApplicationComponent,在自动生成的DaggerDagger2TestComponent.java中就会多出一个applicationComponent()的方法。

1.  @ActivityScope  
2.  @Component(modules = Dagger2TestModule01.class, dependenci2es = ApplicationComponent.class)  public interface Dagger2TestComponent {  
3.      void inject(MainActivity activity);  

至此,Daggers的基本用法已经介绍完成。

你可能感兴趣的:(依赖注入概念和Dagger2框架使用介绍)