Dagger2已经出来好久,之前也接触过,但是用的不多,一些没有理解到位的地方,其实Dagger2学习的成本还是蛮高的(对我这个学渣来说).
现在从最基础的来慢慢 的了解.这个神兵利器.
1.什么是Dagger2.
Dagger2是一个依赖注入框架,第一代由大名鼎鼎的Square公司共享出来,第二代则是由谷歌接手后推出的,现在由Google接手维护.
2.什么是依赖注入
依赖注入是一种面向对象的编程模式,它的出现是为了降低耦合性,所谓耦合就是类之间依赖关系,所谓降低耦合就是降低类和类之间依赖关系。可能有的人说自己之前并没有使用过依赖注入,其实真的没有使用过吗?当我们在一个类的构造函数中通过参数引入另一个类的对象,或者通过set方法设置一个类的对象其实就是使用的依赖注入.
伪代码:
//简单的依赖注入,构造方法或者set()方法都属于依赖注入
public class ClassA {
private ClassB classB;
public void ClassA(ClassB b) {
classB = b;
}
}
发现这里ClassA的构造函数里传了一个参数ClassB, 这个Class就依赖于 classA . 其实这样写代码 有问题的,当你的业务变大了, 发现你要传的东西也慢慢的变多, 当要需要修改的里面的东西的时候,发现里面要改的东西太多了.这样根本不符合开闭原则. 所以Dagger2来解决这个问题.
3.基本配置
1.方式: 通过annotationProcessor就是APT工具中的一种,他是google开发的内置框架,不需要引入
//dagger2
implementation 'com.google.dagger:dagger:2.15'
annotationProcessor 'com.google.dagger:dagger-compiler:2.15'
}
2.方式:annotationProcessor和android-apt的功能是一样的.APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件.不懂 APT 可以看这博客:https://blog.csdn.net/xx326664162/article/details/68490059
<1>首先在你的Project build.gradle中添加如下代码
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
//添加apt插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
<2>然后添加依赖(在module的build.gradle中添加如下代码)
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'com.google.dagger:dagger:2.15'
apt 'com.google.dagger:dagger-compiler:2.15'
}
4.使用方式;
Dagger2 是要配合MVP 模式来写是最好的. mvp 模式后面在慢慢的写.先看一下注解干嘛的.
先创建一类,
public class ClassA {
@Inject
public ClassA() {
}
}
我们只是使用 @Inject 这注解 . 编译代码后,看一下. 发现什么效果都咩有.
但是在这build 路径下面发现了帮我们生成一个 ClassA_Factory 这个类. 可以看一下这类的代码
// Generated by Dagger (https://google.github.io/dagger).
package view.dome.com.mvp_dragger2_dome;
import dagger.internal.Factory;
public final class ClassA_Factory implements Factory {
private static final ClassA_Factory INSTANCE = new ClassA_Factory(); //在new ClassA_Factory 对象
@Override
public ClassA get() {
return new ClassA(); //通过get 方法 来创建 classA 这个类
}
public static ClassA_Factory create() {
return INSTANCE;
}
}
通过代码可以看到 ClassA_Factory这个类 似乎是一个工厂类,在通过create()创建后,每次调用get()方法都能获得一个ClassA对象。其实通过@Inject注解了一个类的构造方法后,可以让编译器帮助我们产生一个对应的Factory类,通过这个工厂类我们可以通过简单的get()方法获取到ClassA对象!
接下来做一个实验看看Dagger2是不是给我们创建对象. 创建一个Activity类,在这个类中创建一个成员变量ClassA,按照Dagger2给我们的指示,当我们需要一个ClassA,我们只需要在这个成员变量上方加一个@Inject注解,编译器会自动帮我们产生对应的代码,我们就可以直接使用这个ClassA对象了!
public class MainActivity extends AppCompatActivity {
@Inject
ClassA classA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button_dragger2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,classA.toString(), Toast.LENGTH_SHORT).show();
}
});
}
}
本案例中我们设置一个Button,点击Button后我们打印出这个ClassA对象.
但是看结果给我报一空指针异常,发现.@Inject并没有帮助我们初始化对应的ClassA 对象,或者说,我们的Activity并没有使用刚才我们看到的ClassA_Factory类,不过也可以理解,我们并没有建立Activity和ClassA_Factory类之间的关系嘛。看一下应该怎么样建立联系.
创建Module类以及一个Component接口
public class Dragger2_SimpleModule {
private MainActivity mainActivity;
public Dragger2_SimpleModule(MainActivity mainActivity) {
this.mainActivity =mainActivity;
}
}
import view.dome.com.mvp_dragger2_dome.MainActivity;
@Component(modules = Dragger2_SimpleModule.class)
public interface Dragger2_SimpleComponent {
void inject(MainActivity activity);
}
请注意,Module类上方的@Module注解意味着这是一个提供数据的【模块】,而Component接口上方的@Component(modules = Dragger2_SimpleModule.class)说明这是一个【组件】.
现在 我们在Aactivity 里面添加下面的代码看一下. 看是否能创建连接.(写完,代码先要编译一下.)
@Inject
ClassA classA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerDragger2_SimpleComponent.builder()
//此时dragger2_SimpleModule法是过时方法,因为我们没有使用到任何一个module中提供的对象
.dragger2_SimpleModule( new Dragger2_SimpleModule(this))
.build().inject(this);
findViewById(R.id.button_dragger2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,classA.toString(),Toast.LENGTH_SHORT).show();
}
});
}
现在 发现能运行,也能拿到 classA的对象了.
Module和Component作用详解
下面是一个大神举的栗子 ,感觉是蛮好懂的
我们假设案例中的Activity代表家庭住址,classA代表某个商品,现在我们需要在家(Activity)中使用商品(classA),我们网购下单,商家(代表着案例中自动生成的classA_Factory工厂类)将商品出厂,这时我们能够在家直接获得并使用商品吗?
当然不可能,虽然商品(classA)已经从工厂(Factory)生产出来,但是并没有和家(Activity)建立连接,我们还需要一个新的对象将商品送货上门,这种英雄级的人物叫做——快递员(Component,注入器)。
没错,我们需要这样的一种注入器,将已经生产的classA对象传递到需要使用该classA的容器Activity中,于是我们需要在Activity中增加这样几行代码:
DaggerDragger2_SimpleComponent.builder()
//此时dragger2_SimpleModule法是过时方法,因为我们没有使用到任何一个module中提供的对象
.dragger2_SimpleModule( new Dragger2_SimpleModule(this))
.build()
.inject(this);
这就说明快递员Component已经将对象Inject(注入)到了this(Activity)中了,既然快递到家,我们当然可以直接使用classA啦!
Module 可以理解成快递的箱子,里面装载的是我们想要的商品,我们在Module中放入什么商品,快递员(Component)将箱子送到我们家(Activity容器),我们就可以直接使用里面的商品啦!
总结
- @Inject : 注入,被注解的构造方法会自动编译生成一个Factory工厂类提供该类对象。
2.@Component: 注入器,类似快递员,作用是将产生的对象注入到需要对象的容器中,供容器使用。
3.@Module: 模块,类似快递箱子,在Component接口中通过@Component(modules =
xxxx.class),将容器需要的商品封装起来,统一交给快递员(Component),让快递员统一送到目标容器中。
Module跟Component源码分析
-
看一下 dagger2 帮我们生成了什么代码.
发现里面有三个类. 看一下主要干嘛的.
2.先来看一下 ClassA_Factory 这个类, 其实这个类在上面已经说过了,
当我们@Inject注解一个类的构造方法时,编译器会自动帮我们生成一个工厂类,负责生产该类的对象,类似于商品的厂家.
3.DaggerDragger2_SimpleComponent 这个类,
// Generated by Dagger (https://google.github.io/dagger).
package view.dome.com.mvp_dragger2_dome.module;
import dagger.internal.Preconditions;
import view.dome.com.mvp_dragger2_dome.ClassA;
import view.dome.com.mvp_dragger2_dome.MainActivity;
import view.dome.com.mvp_dragger2_dome.MainActivity_MembersInjector;
public final class DaggerDragger2_SimpleComponent implements Dragger2_SimpleComponent {
private DaggerDragger2_SimpleComponent(Builder builder) {}
public static Builder builder() {
return new Builder();
}
public static Dragger2_SimpleComponent create() {
return new Builder().build();
}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectClassA(instance, new ClassA());
return instance;
}
public static final class Builder {
private Builder() {}
public Dragger2_SimpleComponent build() {
return new DaggerDragger2_SimpleComponent(this);
}
/**
* @deprecated This module is declared, but an instance is not used in the component. This
* method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
@Deprecated
public Builder dragger2_SimpleModule(Dragger2_SimpleModule dragger2_SimpleModule) {
Preconditions.checkNotNull(dragger2_SimpleModule);
return this;
}
}
}
在MainActivity 里面就使用到这个类, 这个类的类名就是Dagger 加上Component接口名,在类上面使用也简单build 调用.
DaggerDragger2_SimpleComponent.builder()
//此时dragger2_SimpleModule法是过时方法,因为我们没有使用到任何一个module中提供的对象
.dragger2_SimpleModule( new Dragger2_SimpleModule(this))
.build()
.inject(this);
在mainActivty 代码里 发现 首先是调用 DaggerDragger2_SimpleComponent.builder()再调用 build 这个方法.实际上是通过建造者模式创建了一个新的new Builder(); 而我们在使用的时候 再调用了 .build() 方法 , 这个方法里面就是 new DaggerDragger2_SimpleComponent(this); 这个对象.
其实这里面可以这样用,
DaggerDragger2_SimpleComponent
// .builder()
// //此时dragger2_SimpleModule法是过时方法,因为我们没有使用到任何一个module中提供的对象
// .dragger2_SimpleModule( new Dragger2_SimpleModule(this))
// .build()
.create()
.inject(this);
直接用 .create() 这个方法, 发现这个方法里面已经帮我new 好了.
再看一下 inject( ) 这个方法, 发现把activity 传进injectMainActivity 这个方法里面,
发现 这里使用了 MainActivity_MembersInjector 这个类的方法了,
DaggerDragger2_SimpleComponent 类
// Generated by Dagger (https://google.github.io/dagger).
package view.dome.com.mvp_dragger2_dome.module;
import dagger.internal.Preconditions;
import view.dome.com.mvp_dragger2_dome.ClassA;
import view.dome.com.mvp_dragger2_dome.MainActivity;
import view.dome.com.mvp_dragger2_dome.MainActivity_MembersInjector;
public final class DaggerDragger2_SimpleComponent implements Dragger2_SimpleComponent {
private DaggerDragger2_SimpleComponent(Builder builder) {}
public static Builder builder() {
return new Builder();
}
public static Dragger2_SimpleComponent create() {
return new Builder().build();
}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectClassA(instance, new ClassA());
return instance;
}
public static final class Builder {
private Builder() {}
public Dragger2_SimpleComponent build() {
return new DaggerDragger2_SimpleComponent(this);
}
/**
* @deprecated This module is declared, but an instance is not used in the component. This
* method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
@Deprecated
public Builder dragger2_SimpleModule(Dragger2_SimpleModule dragger2_SimpleModule) {
Preconditions.checkNotNull(dragger2_SimpleModule);
return this;
}
}
}
MainActivity_MembersInjector 这个类
// Generated by Dagger (https://google.github.io/dagger).
package view.dome.com.mvp_dragger2_dome;
import dagger.MembersInjector;
import javax.inject.Provider;
public final class MainActivity_MembersInjector implements MembersInjector {
private final Provider classAProvider;
public MainActivity_MembersInjector(Provider classAProvider) {
this.classAProvider = classAProvider;
}
public static MembersInjector create(Provider classAProvider) {
return new MainActivity_MembersInjector(classAProvider);
}
@Override
public void injectMembers(MainActivity instance) {
injectClassA(instance, classAProvider.get());
}
public static void injectClassA(MainActivity instance, ClassA classA) {
instance.classA = classA;
}
}
其实已经很简单了,在该Injector的injectMembers()方法中,已经将Student对象通过ClassA_Factory的get()方法获得,然后直接赋值给Activity的ClassA对象了!
里面就是这个方法 injectClassA(instance, classAProvider.get());
上面是没有带Module的源码解析,现在带上Module 看一下.
- 在ClassA 这个类里面 去掉@Inject 这个注解.
package view.dome.com.mvp_dragger2_dome;
import javax.inject.Inject;
public class ClassA {
public ClassA() {
}
}
2.Module类(增加一个Provide注解方法)
package view.dome.com.mvp_dragger2_dome.module;
import dagger.Module;
import dagger.Provides;
import view.dome.com.mvp_dragger2_dome.MainActivity;
@Module
public class Dragger2_SimpleModule {
private MainActivity mainActivity;
public Dragger2_SimpleModule(MainActivity mainActivity) {
this.mainActivity =mainActivity;
}
@Provides
ClassA provideClassA(){
return new ClassA();
}
}
再看一下 MainActivity 里面是怎么用的
DaggerDragger2_SimpleComponent
// .builder()
// //此时dragger2_SimpleModule法是过时方法,因为我们没有使用到任何一个module中提供的对象
// .dragger2_SimpleModule( new Dragger2_SimpleModule(this))
// .build()
.builder()
.dragger2_SimpleModule(new Dragger2_SimpleModule(this))
.build()
.inject(this);
发现 这里面dragger2_SimpleModule 这方法不再是过时的.
现在 argger 帮我生成什么样的代码.
发现 Factory 这类 发生变化了,
// Generated by Dagger (https://google.github.io/dagger).
package view.dome.com.mvp_dragger2_dome.module;
import dagger.internal.Factory;
import dagger.internal.Preconditions;
import view.dome.com.mvp_dragger2_dome.ClassA;
public final class Dragger2_SimpleModule_ProvideClassAFactory implements Factory {
private final Dragger2_SimpleModule module;
public Dragger2_SimpleModule_ProvideClassAFactory(Dragger2_SimpleModule module) {
this.module = module;
}
@Override
public ClassA get() {
return Preconditions.checkNotNull(
module.provideClassA(), "Cannot return null from a non-@Nullable @Provides method");
}
public static Dragger2_SimpleModule_ProvideClassAFactory create(Dragger2_SimpleModule module) {
return new Dragger2_SimpleModule_ProvideClassAFactory(module);
}
public static ClassA proxyProvideClassA(Dragger2_SimpleModule instance) {
return Preconditions.checkNotNull(
instance.provideClassA(), "Cannot return null from a non-@Nullable @Provides method");
}
}
Dragger2_SimpleModule_ProvideClassAFactory 这里必须传了 Model .通过@Providers注解后,产生的对象就经过Module包装,通过Component快递员送到需要的容器Activity中。
相比@Inject简单粗暴的注解生成的“万能工厂”ClassA_Factory类,似乎这个更“安全”一些~.
经过两次分析 我们基本理解了Dagger2的使用方式,原理基本如下:
@Inject 注解构造 生成“大众”工厂类
或者
@Module +@Providers 提供注入“私有”工厂类
然后
通过Component 创建获得Activity,获得工厂类Provider,统一交给Injector
最后
Injector将Provider的get()方法提供的对象,注入到Activity容器对应的成员变量中,我们就可以直接使用Activity容器中对应的成员变量了!