看一个简单dagger2的Demo1
目标类B
public class B { @Inject public B() { } public String getName(){ return "BBBBBBB"; }}页面Activitypublic class MainActivity extends BaseActivity { @Inject B b; @Override PRotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //实例化component对象,注意DaggerBComponent是生成的,所以打这行代码时,先编译一下 BComponent component = DaggerBComponent.builder().build(); //注入 component.injectB(this); Log.e("-------","通过inject来实现======"+b.getName()); }}@Componentpublic interface BComponent { void injectB(Main2Activity activity);}运行,打印的结果为"通过...======BBBBBBB"
其中Inject注解(Annotation)来标注目标类中所依赖的其他类(MainActivity中的B),同样用注解来标注所依赖的其他类的构造函数(B的构造方法)
1:用Inject注解标注目标类中的其他类(MainActivity中的属性B)
2:用Inject注解标注其他类的构造方法(B类的构造方法)
3:用Inject注解标注属性时,属性不能用private修饰,不然会报错,如下:
Error:(13, 20) 错误: Dagger does not support injection into private fields
原因是Dagger2是采用编译时产生注入代码(一个注入工具类)来为变量注入值,注入过程如下所示:
public void injectMembers(MainActivity instance) { if (instance == null) { throw new NullPointerException("Cannot inject members into a null reference"); } supertypeInjector.injectMembers(instance); instance.str = strProvider.get();//为变量str注入值,若变量str为private,此处就无法引用到str}补充:inject还可以标注在方法里,
使用
public class LoginActivityPresenter { private LoginActivity loginActivity; @Inject public LoginActivityPresenter(LoginActivity loginActivity) { this.loginActivity = loginActivity; } @Inject public void enableWatches(Watches watches) { watches.register(this); //Watches instance required fully constructed LoginActivityPresenter }}@Inject
注解提供依赖的方式是在这个类的public
方法中作注解:方法的所有参数都是通过依赖图表提供的。但是为什么我们需要方法注入呢?在某些情况下会用到,如当我们希望传入类的当前实例(
this
引用)到被注入的依赖中。方法注入会在构造器调用后马上被调用,所以这表示我们可以传入完全被构造的this
。因为enableWatches方法里需要用到LoginActivityPresenter对象实例,而用inject标注这个方法的作用在于,当这个构造方法被调用时,会把这个实例传给watches.register(this)中去.其中Component也是一个注解类,一个类要想是Component,
1:必须用Component注解来标注该类
2:该类是接口或抽象类
3:方法名必须是injectXXX(Object),参数object是表示你要注入到哪里去,这里不能写父类BaseActivity,因为具体写了哪个类,它就到哪个类中找Inject标注的属性
上文中提到Component在目标类中所依赖的其他类与其他类的构造函数之间可以起到一个桥梁的作用。
这个demo中component的工作原理是:
Component会查找MainActivity中用Inject注解标注的属性(B),查找到相应的属性后会接着查找该属性(B)对应的用Inject标注的构造函数(这时候就发生联系了),剩下的工作就是初始化该属性的实例并把实例进行赋值。因此我们也可以给Component叫另外一个名字注入器(Injector)
Demo1还有种是对象B的构造方法是由参数的,那么又应该怎么弄呢?后面会有说明
引出Module类
项目中使用到了第三方的类库,第三方类库又不能修改,所以根本不可能把Inject注解加入这些类中,这时我们的Inject就失效了。
那么就需要封装第三方的类库,封装的代码怎么管理呢,总不能让这些封装的代码散落在项目中的任何地方,总得有个好的管理机制,那Module就可以担当此任。可以把封装第三方类库的代码放入Module中,像Demo2:
目标类A
public class A { String aa = "小花";//注意这里没有用inject标注 public A() { }}Moudle类(类名命名木有要求)
@Modulepublic class AModule { @Provides A providesA(){ return new A(); }}Component类(类名命名木有要求)
@Component(modules = {AModule.class})public interface AComponent { void inject(NewActivity act);}页面NewActivity
public class NewActivity extends AppCompatActivity { @Inject A a1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_new); //实例化component对象 AComponent component = DaggerAComponent.builder().aModule(new AModule()).build(); //注入 component.inject(this); Log.e("---------",a1.aa); }}打印结果:小花
其中,Module类其实是简单工厂模式,类里面基本都是创建类实例的方法
这时Component是怎样的工作原理:
Component是注入器,它一端连接目标类,另一端连接目标类依赖实例,它把目标类依赖实例注入到目标类中。上文中的Module是一个提供类实例的类,所以Module应该是属于Component的实例端的(连接各种目标类依赖实例的端),Component的新职责就是管理好Module,Component中的modules属性可以把Module加入Component,modules可以加入多个Module。
Module中的创建类实例方法用Provides进行标注,Component在搜索到目标类中用Inject注解标注的属性后,Component就会去Module中去查找用Provides标注的对应的创建类实例方法,这样就可以解决第三方类库用dagger2实现依赖注入了。
那么问题来了,如果实例D的构造也用@Inject标注的话,这时Component应该怎么作为桥梁的呢?
首先Component会搜索目标类中用Inject标注的属性(B),然后如果有Module类,就会先从Module类中找,看有没有用@Provides标注的方法,是否提供了这个实例,有则创建返回;没有的话,就去看这个实例(B)的构造方法是否Inject标注,有则,创建返回;也就是说,Component会先去从Module中找,没有的话,才去构造中找
注意:Component可以包含一个或多个Module类,也可以不包含,Demo1即使不包含
如果构造是有参数的,Component应该怎么连接?
public class D { String name = "卡卡卡"; public D(B b) { }}以下是初始化目标类的具体步骤
步骤1:查找Module中是否存在创建该类的方法。步骤2:若存在创建类方法,查看该方法是否存在参数 步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每个参数 步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束步骤3:若不存在创建类方法,则查找Inject注解的构造函数, 看构造函数是否存在参数 步骤3.1:若存在参数,则从**步骤1**开始依次初始化每个参数 步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束注意:如果参数没有从module中找到,其构造又未被inject标注,则会报错
所以:在module中,不要出现参数和返回值一样,如上所述的话,会造成死循环
总结:
Inject,Component,Module,Provides是dagger2中的最基础最核心的知识点。奠定了dagger2的整个依赖注入框架。
Inject主要是用来标注目标类的依赖和依赖的构造函数Component它是一个桥梁,一端是目标类,另一端是目标类所依赖类的实例,它也是注入器(Injector)负责把目标类所依赖类的实例注入到目标类中,同时它也管理Module。Module和Provides是为解决第三方类库而生的,Module是一个简单工厂模式,Module可以包含创建类实例的方法,这些方法用Provides来标注
新闻热点
疑难解答