Dagger2 使用介绍

Dagger2

概要及前提

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 或 @Qulifier 区分同类型依赖

@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;
复制代码

单例(@Singleton, @Scope)

在 Dagger2 中,用 @Scope 注解的类(也能够注解方法,对应点类)能够在当前 Component 内产生的实例是单例的。并非只有 @Singleton 注解的类才是单例,而是全部 @Scope 注解的类,看 @Singleton 的代码便可知:

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
复制代码

**注意,此处的单例是指这个 Component 的生命周期下。**咱们能够自定义做用域的粒度(好比 @PerFragment 指定在 Fragment 的生命周期内等等)。

@Subcomponent

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, Provide)

懒加载 - 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 那样都是单例。

@Bind 注入接口实现

若是要注入一个接口的实现,通常咱们会使用以下方式:

@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

参考连接汇总:

设计模式六大原则(3):依赖倒置原则

说说依赖注入

都是套路——Dagger2 没有想象的那么难

Dagger 官网

相关文章
相关标签/搜索