目录
- Dagger简介
- Dagger1.x,Dagger2.x简单对比
-
- Dagger中的注解
- @Inject
- @Provide
- @Module
- @Component
- @Binds
- multibinds
- 其他关键注解
- 4.使用
- 5.Dagger & Android
- Activity的注入
- Fragment的注入
Dagger官方文档
Dagger&Android
Dagger分享(这一篇笔记也很好,内容全面易懂,排版也很清晰,可以参考学习)
DaggerDamo(文中的Dagger&Android的Demo都是参考这里写的)
1. Dagger简介:
依赖注入的框架。依赖注入的目的——解耦。
依赖注入的常规方式
如果class A中有class B的实例,就可以说A依赖于B,如MVP中,V(Activity)持有P(Presenter),V依赖于P,这个例子存在一个问题,如果有一天,B的构造方法变了,那我们就需要修改A中创建B的代码,而且所有创建B实例的地方,都需要修改。
———— 如果使用Dagger来进行依赖注入,只需要用@Inject
和其他注解就能实现。
依赖注入的几种方式
- 构造函数注入
// Constructor
Client(Service service) {
// Save the reference to the passed-in
// service inside this client
this.service = service;
}
- setter方法注入
// Setter method
public void setService(Service service) {
// Save the reference to the passed-in
// service inside this client
this.service = service;
}
- 接口注入
// Service setter interface.
public interface ServiceSetter {
public void setService(Service service);
}
// Client class
public class Client implements ServiceSetter {
// Internal reference to the service used by this
// client.
private Service service;
// Set the service that this client is to use.
@Override
public void setService(Service service) {
this.service = service;
}
}
2. Dagger1.x -> Dagger2.x
更好的性能:相较于Dagger1,它使用的预编译期间生成代码来完成依赖注入,而不是用的反射。大家知道反射对手机应用开发影响是比较大的,因为反射是在程序运行时加载类来进行处理所以会比较耗时,而手机硬件资源有限,所以相对来说会对性能产生一定的影响。
容易跟踪调试:因为dagger2是使用生成代码来实现完整依赖注入,所以完全可以在相关代码处下断点进行运行调试.
3. Dagger中的注解
@Inject
@Inject,两个作用,其一:用来标记构造函数,Dagger可以调用它创建该类的实例,对外提供依赖。其二:标记依赖字段,表示该字段由Dagger提供实例。也就是标记依赖源和依赖宿主。
如果一个类有@Inject注解的字段,但是没有@Inject注解的构造器(注入jar包中的类),可以使用方法注入——@Provide注解。
@Inject不生效的地方:
- 注解接口不生效,因为接口不能被实例化。
- 注入第三方jar包里的类不生效,因为这样的类不能被@Inject注解。
- Configurable objects must be configured!
对于这些不能使用@Inject进行注入的地方,可以使用@Provider注解的方法提供依赖。
@Provide
@Provide用来注解方法,该方法用于提供依赖。当需要Dagger提供依赖注入的时候,这个方法会被调用——创建依赖对象。
@Module
@Module用于注解类,该类用于提供依赖。
@Component
@Component一般用来标注接口,被标注了Component的接口,在编译时会由Dagger生成接口的实例,作为提供依赖方(Module)和依赖宿主的桥梁,或者说注入器,把相关依赖注入到宿主容器中。
@Component(modules = DripCoffeeModule.class)
public interface CoffeeShop {
CoffeeMaker maker();
}
Component中有两个方法:
-
Provision:Provision方法如上,不接收参数,返回值是被注入的依赖类型,这个class必须有
@Inject
注解的构造器,或者有module创建这个依赖的实例。 - Member-injection: Member-injection方法有一个参数,用于传入依赖的宿主,如果依赖宿主不能由Dagger实例化(如Activity),就可以使用这种方式进行注入。
void injectMembers(T)
@Binds
如果我们需要一个一般化的类型,而Dagger的object graph中已经一个该类型的子类,那么用Provide是这样做的:
@Provides
static Pump providePump(Thermosiphon pump) {
return pump;
}
用 Binds 注解可以这么做:
@Binds
static abstract Pump providePump(Thermosiphon pump);
@Binds 用于注解方法声明,如果 module 是接口,那么 binds 方法就是一个方法声明,如果 module 是类,那么 binds 方法就是一个abstract方法。 binds 方法有且只有一个参数,这个参数可以赋值给返回值类型。也就是说参数是返回接口的子类。
注意 Module 不能同时用 Binds 方法和 Provide 非static方法,因为 Binds 方法只是一个方法声明没有实现,一旦 Module 有了 Provide 方法(非static),意味着这个 Module 必须实例化,所以方法声明就必须得有实现,这便起了冲突。当然,如果 Provide 方法是static的,那也是可以的。
multibinds
multibinds不是一个注解,而是用于multibinds的一组注解。用于将对象绑定到集合中。
- 注解 Provider 方法,表示提供的对象会被注入 Set/Map。
Set:
- @IntoSet 将一个元素注入 Set
- @ElementsIntoSet 将一个集合注入 Set
Map
@IntoMap 表示该方法的返回值作为 Map 的 value 注入 Map 中,另外 Key 由下面的注解提供:
- @Stringkey 提供字符串作为 key
- @IntKey 提供 int
- @ClassKey 提供 class
- 也可用 @MapKey 自定义 key 的类型
key 都是常量
在dagger-android中我们会看到 @ActivityKey 注解,就是通过@MapKey 定义的。
- 注解集合Set/Map,@Multibinds
对于 @Inject 注解的集合,找不到元素提供方的话,dagger 会在编译期报错:
java.util.Map<java.lang.String,java.lang.String> cannot be provided without an @Provides- or @Produces-annotated method.
如果有在 Module 中声明了 @Multibinds,便不会报错,而是得到一个空集合。具体例子可以看dagger-android中的Activity注入。
注意:Module注解的优先级高于构造器注解。
其他关键注解
@Scope: Dagger2可以通过自定义注解限定注解作用域,来管理每个对象实例的生命周期,在它的生命周期范围内,提供单例。
@Qualifier: 有时只靠类型,不足以识别一个依赖,可能无法找到依赖提供方。比如我们注入的是一个特殊的接口实现,与默认实现有区别(可能是构造参数不同),这时,我们需要@Qualifiers声明一个额外的注解,用于区分依赖。
例如:在Android中,我们会需要不同类型的context,所以我们就可以定义 Qualifier注解@perApp
和@perActivity
,这样当注入一个context的时候,我们就可以告诉 Dagger我们想要哪种类型的context。
@Singleton:可以在@Providers方法或依赖类声明中使用该注解。可在全局范围内提供注入类的单例。
@Provides
@Singleton
static Heater provideHeater() {
return new ElectricHeater();
}
@Singleton
class CoffeeMaker {
...
}
Component需要和Scope关联起来,一个Component不能既有@Singleton的依赖,又有@RequestScoped的依赖,因为二者的作用域不同,生命周期也不同。
@Component(modules = DripCoffeeModule.class)
@Singleton
interface CoffeeShop {
CoffeeMaker maker();
}
4.使用
背景:假设我们有一个咖啡机(CoffeeMaker),可以加热咖啡(Heater),有泵可以抽出咖啡(Pump)。现在,我们需要在CoffeeMaker中注入这两个装置,完成功能组合。
1. 定义依赖接口
//加热装置
public interface Heater {
void on();
void off();
boolean isHot();
}
public interface Pump {
void pump();
}
2.定义依赖宿主CoffeeMaker
,使用@Inject
注入依赖。
注意:我们注入的依赖,除了有@Inject注解的字段之外,还需要通过构造函数参数传入。
public class CoffeeMaker {
@Inject Heater heater;
@Inject Pump pump;
CoffeeMaker(Heater heater, Pump pump) {
this.heater = heater;
this.pump = pump;
}
public void brew() {
heater.on();
pump.pump();
System.out.println(" [_]P coffee! [_]P ");
heater.off();
}
}
3.使用@Module,定义依赖提供方。
ElectricHeater是Heater的实现类。Thermosiphon是Pump的实现类。
@Module
public class DripCoffeeModule {
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
}
4.使用@Component定义注入器接口,完成Module和依赖宿主的关联。
@Component(modules = DripCoffeeModule.class)
public interface CoffeeShop {
CoffeeMaker maker();
}
返回的CoffeeMaker对象也由Dagger提供。修改CoffeeMaker的构造函数,添加@Inject。
@Inject
CoffeeMaker(Heater heater, Pump pump) {
this.heater = heater;
this.pump = pump;
}
5.在Activity或Main方法中通过Component获取CoffeeMaker实例。
可以发现,我们并没有实现DaggerCoffeeShop
,这里为什么可以调用呢?因为在编译期间,Dagger自动为我们的Component接口生成实现类,DaggerCoffeeShop
就是CoffeeShop
接口的实例.
dripCoffeeModule ()
,该方法根据Component绑定的Module自动生成,传入Module实例。
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule())
.build();
coffeeShop.maker().brew();
以上,就完成了依赖注入。
但是存在一个问题:Module对应一个默认的构造器,Dagger在编译期间生成的DaggerCoffeeShop
,可以访问Module,获取依赖实例。Module的Provide方法是静态的,不需要我们手动构造Module实例就可以访问其Provide方法。—— 所以,我们可以使用create()
代替build()
。
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
我们使用Component关联依赖提供方法(Module)和依赖宿主。并返回CoffeeMaker对象,这个CoffeeMaker对象如何获取?
- 会通过Dagger。先找有没有Module提供CoffeeMaker对象,如果没有,再找有没有@Inject注解的构造器。
- 找到构造器之后,发现
CoffeeMaker
构造器有两个参数,那个这两个参数也是注入的。 - 再找有没有提供参数的Provider方法,因为之前声明了Component接口,把Module和
CoffeeMaker
关联了,这里会去DripCoffeeModule
找Provider方法。这里也就解释了为什么还需要在CoffeeMaker
中传入依赖,最为构造函数的参数。 - 最终完成注入
5.Dagger & Android
Dagger相比于其他依赖注入框架,优点在于它完全静态生成依赖。
Dagger在Android中使用的特点在于,Android中很多组件是由Framework进行实例化的。常规的注入方式,是通过构造函数参数传递依赖。但是在Android中,显然不能由Dagger创建Activity/Fragment,无法访问到其构造函数。所以,我们必须在组件的生命周期方法内进行成员的注入。
常规的注入方式,通过构造函数传递依赖
public class CoffeeMaker {
//通过Module提供注入——DripCoffeeModule
@Inject Heater heater;
@Inject Pump pump;
@Inject
CoffeeMaker(Heater heater, Pump pump) {
this.heater = heater;
this.pump = pump;
}
}
Android中的注入
/**
* 给Application用的component,可以作为一个全局的component,如果其他的Activity需要使用,从Appliction中获取,以此达到复用的目的。
* 将依赖提供方AppModule和依赖宿主FeatureActivity关联起来
*/
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
//需要我们自己声明builder,通常builder都是Dagger自动生成的,但是在Android中注入Activity,需要我们自己声明,提供component
@Component.Builder
interface Builder {
@BindsInstance
Builder application(App application);
AppComponent build();//提供获取组件的方法,这个组件bind到了Application上
}
/**
* Member-injection方法,用于传入依赖的宿主,对于Activity这样的组件,不能由Dagger进行实例化,无法在其构造器上添加注入的参数,需要中这种方式进行注入
*/
void inject(FeatureActivity featureActivity);
}
@Module
public class AppModule {
@Provides
Context provideContext(App application) {
return application.getApplicationContext();
}
@Singleton
@Provides
SomeClientApi provideSomeClientApi() {
return new SomeClientApiImpl();
}
}
public class App extends Application {
//将Component绑定到Application,Activity需要使用时,通过Application获取该组件,达到复用的目的
private AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent
.builder()
.application(this)
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
在Android中,framework为我们初始化了Activity,除了使用@Inject
注解注入的变量之外,我们需要调用component的members injection方法完成注入。
public class FeatureActivity extends AppCompatActivity {
//在Activity中注入api,通过Application获取
@Inject
SomeClientApi mSomeClientApi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在Activity的生命周期方法内完成注入
((App) getApplication())
.getAppComponent()
.inject(this);
//调用注入的依赖
mSomeClientApi.report();
}
}
以上是在Activity注入一个依赖的全部步骤,但是这样的注入方式带来的问题:
- 在其他需要注入这个依赖的Activity中,会在
onCreate()
写这样的重复代码。 - 被注入方——Activity,它知道我们是如何注入一个依赖的,用什么注入器(Component),注入器怎么调用。这样违反了依赖注入的原则:一个类不需要知道它如何被注入依赖的。
使用dagger-android module进行依赖注入
1. 添加dagger-android依赖
// Dagger core dependencies
implementation 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
// Dagger Android dependencies
implementation 'com.google.dagger:dagger-android:2.16'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16' // if you use the support libraries
2. 定义一个@Subcomponent
接口
该component实现 AndroidInjector<YourActivity>
接口,并声明其内部抽象类Builder,继承AndroidInjector.Builder<YourActivity>
@Subcomponent
public interface FeatureSubComponent extends AndroidInjector<FeatureActivity> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<FeatureActivity> {}
}
3. 定义一个Module,用来bind subComponent的Builder
@Module(subcomponents = FeatureSubComponent.class)
public abstract class BuildersModule {
@Binds
@IntoMap
@ActivityKey(FeatureActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindFeatureActivityInjectorFactory(FeatureSubComponent.Builder builder);
// Add more bindings here for other sub components
}
把这个Module绑定到Application的注入容器(Component)中.此外还需要注入AndroidSupportInjectionModule
。
@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, BuildersModule.class})
public interface AppComponent {
// void inject(FeatureActivity featureActivity);
//这里不再注入Activity,而是注入到Application中
void inject(App app);
}
4.改造Application
实现HasActivityInjector
接口,实现接口的activityInjector()
方法返回注入的 DispatchingAndroidInjector<Activity>
,
最后在onCreate()
中进行注入。
public class App extends Application implements HasActivityInjector{
@Inject
DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create()
.inject(this);
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingActivityInjector;
}
}
5. 在Activity中注入依赖
移除原来的注入代码,改为在super.onCreate()
前调用AndroidInjection.inject(this)
。
public class FeatureActivity extends AppCompatActivity {
@Inject
SomeClientApi mSomeClientApi;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//调用依赖的方法
mSomeClientApi.report();
}
}
注入的依赖,需要宿主提供参数
有时我们注入的依赖,它的实例化参数需要宿主方提供,在没有dagger-android 的时候,需要我们在Activity
中调用inject()
进行注入之前,就将Activity
作为参数传给module。如:在MVP模式中,实例化P的时候,需要传入V的实例,需要我们在把Activity
作为Module的构造函数的参数进行传递,再添加provider函数提供Activity。
@Module
class FeatureModule {
private FeatureView view;
//构造module的时候传入view
public FeatureModule(FeatureView view) {
this.view = view;
}
//再把这个view提供出去
@Provides FeatureView provideView() {
return view;
}
}
//presenter中使用构造函数注入
class FeaturePresenter {
private final FeatureView view;
@Inject
Presenter(FeatureView view) {
this.view = view;
}
public void doSomething() {
}
}
public class FeatureActivity extends AppCompatActivity implements FeatureView {
@Inject FeaturePresenter presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//在Activity中实例化FeatureModule,并把Activity作为参数
DaggerFeatureComponent.builder()
.featureModule(FeatureModule(this)).build()
.inject(this)
// presenter ready to be used
presenter.doNothing();
}
}
但是在dagger-android中,Activity只调用AndroidInjection.inject(this)
进行注入,不在Activity中初始化Module。应该怎么进行注入呢?使用dagger-android的时候,activity实例已经存在于dagger的对象图中了,使用@Binds
,我们可以从对象图中,取出activity作为参数,具体代码如下:
@Module
public abstract class FeatureModule {
//使用Binds表示从Dagger的对象图中获取FeatureActivity实例,然后将其赋值给FeatureConstrant.View,参数是返回值的子类
@Binds
abstract FeatureConstrant.View provideFutureView(FeatureActivity featureActivity);
}
在presenter中添加inject构造器,view作为参数。
public class FeaturePresenter implements FeatureConstrant.Presenter {
private FeatureConstrant.View mView;
@Inject
public FeaturePresenter(FeatureConstrant.View view) {
this.mView = view;
}
//业务代码
}
在component中加入这个module,最后在view中添加@Inject注解的presenter字段就OK。
@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, BuildersModule.class, FeatureModule.class})
public interface AppComponent {
//...
}
注入Fragment
Fragment的注入和Activity的注入流程差不多。步骤如下:
- Activity实现
HasFragmentInjector
接口。提供DispatchingAndroidInjector<Fragment>
对象。和Activity的注入有区别,Fragment是在Activity中加载的,所以在Activity中实现接口。
public class FeatureActivity extends AppCompatActivity HasFragmentInjector{
@Inject
DispatchingAndroidInjector<Fragment> fragmentInjector;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
@Override
public AndroidInjector<Fragment> fragmentInjector() {
return fragmentInjector;
}
//other function
}
- 在Fragment中添加依赖,并调用
AndroidInjection.inject(this)
进行依赖注入。注意,inject()
需要在onAttach()
的super.onAttach(activity)
前调用。
public class TextFragment extends Fragment {
//注入的依赖
@Inject
SomeClientApi mSomeClientApi;
@Override
public void onAttach(Activity activity) {
AndroidInjection.inject(this);
super.onAttach(activity);
}
//...
}
- 定义subComponent,实现
AndroidInjector<YourFragment>
接口,定义内部类Builder。
@Subcomponent
public interface FragmentSubComponent extends AndroidInjector<TextFragment> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<TextFragment> {}
}
4.把SubComponent的Builder绑定到Module。使用FragmentKey注解内部方法。
@Module(subcomponents = {OtherSubComponent.class, FragmentSubComponent.class})
public abstract class BuildersModule {
//add fragment sub component
@Binds
@IntoMap
@FragmentKey(TextFragment.class)
abstract AndroidInjector.Factory<? extends Fragment> bindFragmentInjectorFratory(FragmentSubComponent.Builder builder);
// Add more bindings here for other sub components
}
5.把Module绑定到Component。
@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, BuildersModule.class, OtherModule.class})
public interface AppComponent {
//这里不再注入Activity,而是注入到Application中
void inject(App app);
}
6.总结
这一部分先到这里,后续想到再补充。