Dagger2 是一个依赖注入的框架,经过编译时生成 Java 代码,来进行依赖注入实现。java
使用前先须要了解依赖倒置原则及依赖注入这两个概念。依赖倒置原则是设计模式六大原则之一,程序要依赖于抽象接口,高层模块不该该依赖低层模块,就好比汽车依赖于引擎,而引擎不该该依赖于汽车。依赖注入实际上是一个很简单且经常使用的东西,就是将一个对象放进另外一个对象里,咱们日常用 setter 或构造方法传参将一个对象注入到这个对象,这就是依赖注入。具体看如下文章便可。android
依赖倒置原则:blog.csdn.net/zhengzhb/ar… 依赖注入:droidyue.com/blog/2015/0…设计模式
若要查看 Dagger2 相关的文档、API,直接访问官网便可:dagger.dev/app
@Inject:做为依赖提供方使用:在构造函数上,经过标记构造函数让 Dagger2 使用(Dagger2 经过 @Inject 能够在须要这个类实例的时候来找到这个构造函数并把相关实例 new 出来)从而提供依赖;做为依赖需求方:标记在须要依赖的变量 Dagger2 会帮助初始化,被咱们使用。框架
@Moudle:依赖提供方,负责提供依赖中所须要的对象ide
@Component:依赖注入组件,负责将依赖注入到依赖需求方。函数
@Provides:会根据返回值类型在有此注解的方法中寻找应调用的方法单元测试
未使用 Dagger2 ,通常是经过如下方式注入依赖:学习
public class Car {
public Car() {
//TODO
}
@Override
public String toString() {
return "汽车";
}
}
复制代码
public class MainActivity extends AppCompatActivity {
private Car mCar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCar = new Car();
Log.i(MainActivity.class.getName(), mCar.toString());
}
}
复制代码
使用 Dagger2:测试
在 Car 类中用 @Inject 修饰须要被调用的构造函数:
public class Car {
@Inject
public Car() {
//
}
@Override
public String toString() {
return "汽车";
}
}
复制代码
添加一个 Module 类,用于提供依赖中所须要的对象,须要用到 @Module 和 @Provider 注解:
@Module
public class CarModule {
@Provides public Car provideCar() {
return new Car();
}
}
复制代码
添加一个 Component,表示给哪些类注入哪些对象,好比 MainActivity 类须要用到 Car 对象,那么就能够新建一个接口,在接口上添加注解 @Component,把 CarModule 赋上去,在接口下新增一个注入方法,把须要使用该对象的类做为参数传入进来,固然还能够将其余对象提供出去:
@Component(modules = CarModule.class)
public interface MainComponent {
void inject(MainActivity mainActivity);
}
复制代码
而后修改 MainActivity,将直接 new 出来的 Car 对象去掉,采用 Dagger2 的方式进行依赖注入。首先在 Car 对象的声明上加上 @inject 注解,注意此处不能声明为 private,由于后面生成代码时要用到:
@Inject
Car mCar;
复制代码
而后进行 build 生成辅助代码,此时 apt 会生成一个 DaggerMainComponent 类,此时在 MainActivity 经过 Dagger2 将相关的依赖注入:
public class MainActivity extends AppCompatActivity {
@Inject
Car mCar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent component = DaggerMainComponent.create();
component.inject(this);
Log.i(MainActivity.class.getName(), mCar.toString());
}
}
复制代码
还有另外一种 Component 建立模式,经过 Builder 来建立,在大型项目中这种形式可能会用得多一些,由于这样会更灵活:
MainComponent component = DaggerMainComponent.builder().carModule(new CarModule()).build();
复制代码
其中,Builder 里的方法来自于 @Component 里的 Module 值,用来指定 Module。
到此,一个简单的使用 Demo 就完成了。其原理就是在 build 的时候生成一系列代码,对依赖进行注入,生成的代码用到了许多设计模式,后续能够深刻学习。
@Named 用于区分获取依赖的方法。从上文可知 Dagger2 寻找依赖是根据返回值进行查找的,当遇到同返回值的不一样依赖便可经过 @Named 注解进行获取:
@Module
public class CarModule {
@Named("normal")
@Provides
public Car provideCar() {
return new Car();
}
@Named("blue")
@Provides
public Car provideBlueCar() {
Car car = new Car();
car.setColor(Color.BLUE);
return car;
}
}
复制代码
在 MainActivity 中在 @Inject 的地方用 @Named 加以区分:
@Inject
@Named("blue")
Car mCar;
复制代码
即:使用 @Named 标记 Module 中生成类实例的方法,使用 @Named 标记目标类中相应类实例
@Qualifier 能够实现与 @Named 同样的做用,其实 @Named 就是 @Qualifier 使用的一个例子。使用 @Qualifier 须要本身定义注解,用 @Qualifier 注解定义一个 @UserThirdQualifier 注解:
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface UserThirdQualifier {
String value() default "";
}
复制代码
@Module
public class CarModule {
@UserThirdQualifier("normal")
@Provides
public Car provideCar() {
return new Car();
}
@UserThirdQualifier("blue")
@Provides
public Car provideBlueCar() {
Car car = new Car();
car.setColor(Color.BLUE);
return car;
}
}
复制代码
在 MainActivity 中用 @UserThirdQualifier 注解区分便可,与 @Named 如出一辙:
@Inject
@UserThirdQualifier("blue")
Car mCar;
复制代码
在 Dagger2 中,用 @Scope 注解的类(也能够注解方法,对应点类)能够在当前 Component 内产生的实例是单例的。并非只有 @Singleton 注解的类才是单例,而是全部 @Scope 注解的类,看 @Singleton 的代码便可知:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
复制代码
**注意,此处的单例是指这个 Component 的生命周期下。**咱们能够自定义做用域的粒度(好比 @PerFragment 指定在 Fragment 的生命周期内等等)。
Component 能够被依赖(在 @Component 注解上面传 dependencies 参数便可),@Subcomponent 也是用于管理 Component 间的依赖的。若是须要父组件的所有 Provider,这时咱们能够用 @Subcomponent 方式而不是用 dependencies 参数方式,相比于 dependencies 参数方式,@Subcomponent 方式不须要父组件显式显露对象,就能够拿到父组件所有对象,只须要在父 Component 接口中声明拿这个 Subcomponent 的方法便可:
@Component(dependencies = AppComponent.class, modules = ActModule.class)
public interface ActivityComponent {
void inject(DependenceTestActivity DependenceTestActivity);
void inject(SubComponentTestActivity subComponentTestActivity);
//包含SubComponent,这样的话该SubComponent也能够拿到ActivityComponent中能提供的依赖。
ActSubComponent getActSubComponent();
}
复制代码
@Subcomponent
public interface ActSubComponent {
void inject(SubFragment subFragment);
}
复制代码
在获取它的地方经过以下方式获取便可:
mActivityComponent = DaggerActivityComponent
.builder()
.appComponent(((App) getApplication()).getAppComponent())
.actModule(new ActModule())
.build();
//获取subcomponent
getActivityComponent().getActSubComponent().inject(this);
复制代码
**具体关于 @Scope、@Subcomponent 的使用可见:juejin.im/entry/581c5… ,上面 Subcomponent 例子来自于此处 **
懒加载 - Lazy:
首先先改写上面的例子
public class MainActivity extends AppCompatActivity {
@Inject
Lazy<Car> mCar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent component = DaggerMainComponent.create();
component.inject(this);
Car car = mCar.get();
Log.i(MainActivity.class.getName(), car.toString());
}
}
复制代码
懒加载很好理解,就是 Component 初始化了一个对象,并将这个对象放在一个地方,啥时候要用的时候才去 get,若是是第一次去调用它的 get 方法,它会进行实例化,然后面的调用就会直接获取到第一次 get 进行实例化的那个实例了。这样作能够提升加载速度,好比在 AOSP 中的 SystemUI 项目,其依赖管理就是利用 Dagger2 的 Lazy 注入进行加载和获取的(代码位于 frameworks/base/packages/SystemUI/src/com/android/systemui/Dependency.java)。
强制加载 - Provider:
有时候不只仅是注入单个实例,而是须要多个实例,那么就能够用 Provider 注入了:
public class MainActivity extends AppCompatActivity {
@Inject
Provider<Car> mCar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent component = DaggerMainComponent.create();
component.inject(this);
for (int i = 0; i < 10; i++) {
Car car = mCar.get();
Log.i(MainActivity.class.getName(), car.toString());
}
}
}
复制代码
使用 Provider 注入的对象,每次 get 都会新实例化一个实例出来,而不像 Lazy 那样都是单例。
若是要注入一个接口的实现,通常咱们会使用以下方式:
@Provides
public XXInterface providesXX(XXImp imp) {
return imp;
}
复制代码
但若是用 @Bind,那么就能省下很多代码:
@Binds
public abstract XXInterface bindXX(XXImp imp);
复制代码
须要注意的是,这个 Module 也须要为 abstract。
Dagger2 是一个对于大型项目来讲很是好的依赖注入组件,其经过编译时去生成辅助代码进行依赖注入,可以减小代码量,下降耦合,同时解耦对单元测试是很是友好的,可以提升应用的质量。可是若是是中小型项目我我的认为使用 Dagger2 是没有必要的,这点见仁见智。最初我看到简单使用的时候以为这个就是多余的,但直到经过了解其余相关的内容以及结合 AOSP 中 SystemUI 的实践,我也慢慢体会到了 Dagger2 的一些魅力。
我的能力有限,如有不合理之处,但愿你们可以指出。联系我:me@york1996.com
参考连接汇总: