Android 常用开源框架源码解析 系列 (九)dagger2 呆哥兔 依赖注入库

一、前言

依赖注入定义

目标类中所依赖的其他的类的初始化过程,不是通过手动编码的方式创建的。

是将其他的类已经初始化好的实例自动注入的目标类中。

“依赖注入”也是面向对象编程的 设计模式 ————-组合的配套使用

作用 :降低程序的耦合,耦合就是因为类之间的依赖关系所引起的

产生场景:在一个对象里去创建另一个对象的实例

问题:过多的类,对象之间的依赖会造成代码难以维护。

不符合开闭原则的对象的引用写法:错误示例:

public class ClassA {

classB b ;

public ClassA (ClassB b ){

this.b = b;

}

}

通过依赖注入的方式 ,将ClassB对象注入到ClassA中。从而灵活配置ClassA 的属性

public class ClassA{

@Inject

ClassB b;

public ClassA(){...}

}

常见的四种注入方式

1、接口注入

定义ClassBInterface接口,在其内定义setB()函数.ClassAInterface去实现ClassBInterface 接口,在ClassAInterface类中定义了一个ClassBInterface成员变量,并复写setB()函数,最终通过setB()函数完成接口注入操作。

public interface ClassBInterface {

void setB(ClassB b);

}

public class ClassA implements ClassBInterface {

ClassB classB;

@Override

public void setB(ClassB b) {

classB = b;

}

}

2、set注入——依赖注入的(核心:外部传递而来)重要方法之一

public class ClassAset {

ClassB classb;//定义成员变量

//通过set方法完成注入(对其成员变量的赋值),更加剥离了各个部分的耦合性

public void setCalssB(ClassB b){

classb =b;

}

}

ps:很多时候都是通过

3、通过构造方法注入-常用

public class ClassAConstructor {

//在classA中定义一个ClassB 成员变量

ClassB classB;

//将ClassB作为参数,传递至ClassA的构造函数中,进行成员变量ClassB的赋值

public ClassAConstructor(ClassB b){

classB = b;

}

}

4、通过Java依赖注解完成方法注入-重点

/**

* 对象的组合-在一个类中引用其他对象,容器依赖持有的类的实现

* */

public class FruitContainer {

//在FruitContainer类中持有了Banana的引用,从而调用引用类的方法完成某些功能

Banana banana;

//在FruitContainer的构造方法中通过banana构造方法新建了一个banana对象

public FruitContainer(){

banana =new Banana();

}

}

涉及场景:业务需求改变,被持有的类的构造方法进行了改变,那么持有这个类的很多类都有可能需要进行不同长度的改变,造成了代码的很大的耦合性增加,降低了扩展性,是种比较严重的不符合开闭原则的写法

思考:在FruitContainer依赖Banana的实现的情况下,用什么办法在不修改FruitContainer的类情况下,满足Banana或是其内在的构造函数进行了修改。

答: 通过注解的方式 ,注入到宿主类:FruitContainer 类中

public class FruitConainerInject{//类不用关心具体(水果)引用类的实现

添加@Inject,自动完成注入功能————> 这时候并不会完成注入,需要引入注解库并在目标类中调用才完成注入

Fruit f;

public FruitContainerInject(){

//动态注入

}

}

小结:

不要在需要依赖的类中通过new 类创建依赖而是通过方法提供的参数注入进来

二、dagger2的使用流程

1、将需要注入的对象的类的构造参数使用@Inject标注,在编译的时候编译器会通知dagger2 实例化该类

2、新建component接口,并在开始使用@Component注解进行注释,自定义void inject()函数,并传入需要依赖注入的对象的类型

3、使用编译器的make project 功能 ,进行编译,在build文件夹内目录下生成Component所对应的类,生成的类的名字格式为“Dagger+自定义的Component名字”

4、在需要注入的目标类(一般是Activity or Fragment之类)中,使用@Inject标注要注入的变量,在初始化时候调用自动生成的Component类的builder().xxModule(xxx).build().自定义inject()方法,完成构建

三、dagger2的基本用法

0、导入

compileOnly ‘org.glassfish:javax.annotation:10.0-b28‘

annotationProcessor ‘com.google.dagger:dagger-compiler:2.5‘

implementation ‘com.google.dagger:dagger:2.5’

导入可能存在的问题:

Error:Could not get unknown property ‘classpath‘ for task ‘:app:transformJackWithJackForDebug‘ of type com.android.build…..

  • 1. 删除project根目录build.gradle中的: classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’ (一般现在都没有)
  • 2.app的build.gradle中,删除apply plugin ‘android-apt‘
  • 3.在app的build.gradle中,dependencies中的 apt ‘com.google.dagger:dagger-compiler:2.1.0‘ 改为 
  • annotationProcessor ‘com.google.dagger:dagger-compiler:2.1.0‘

1、@Inject 注解

1、标记在需要依赖的变量,dagger2为其提供依赖,并实例化有此注解的类

2、使用在构造函数上,构造函数工厂,通过标记构造函数让dagger2 使用,从而提供相关的依赖

ps:dagger2 通过@Inject标记可以在需要这个类实例的时候,来找到这个构造方法,将相关的实例new出来

(1)、@Inject标记的变量

通过@Inject 标记的变量 ,在make project后会在其build下的source文件夹内的debug,包名下生成:

"类名_MembersInjector”文件

@Override

public void injectMembers(Car instance) {

if (instance == null) {

throw new NullPointerException("Cannot inject members into a null reference");

}

//通过调用typeProvider.get()函数获取到.type ,在这里type 是通过inject标注的

instance.tyre = tyreProvider.get();

}

ps:通过@Inject 标记的成员变量不能被命名为private 属性,否则无法获取到

(2)、@Inject标记的构造方法

通过@Inject 标记的构造方法 ,在make project后会在其build下的source文件夹内的debug,包名下生成:

"类名_Factory”文件

INSTANCE;

@Override

public Tyre get() {

return new Tyre();

}

public static Factory<Tyre> create() {

return INSTANCE;

}

依赖注入是依赖的对象实例————>需要注入的实例属性

新建工厂实例并调用成员属性注入类完成Tyre的实例注入

2、@Component 注解——注入者与被注入者之间的墙梁,more Improtant

用于标注接口 或是 抽象类,可以完成依赖注入的过程。

将@Inject 和@Module 联系起来的墙梁,从@Module中获取依赖并将依赖注入给 @Inject

@Component标记的接口或是抽象类,在make project后会在其build下的source文件夹内的debug,包名下生成:

"Dagger接口名称”文件

@Component

public interfaceCarComponent {

/**1、参数必须是需要注入依赖的类型,不能是其父类 or 子类

*2、注入接口的方法的返回值 必须是void,命名可以任意但是最好是inject+需要注入依赖的类名,便于识别

**/

voidinjectCar(Car car);  <——————将依赖注入到的目标位置

}

private MembersInjector<Car> carMembersInjector;

private void initialize(final Builder builder) {

//通过carMembersInjector查找目标类对应的成员属性注入类,将依赖的属性的工厂实例传入给注入类,完成依赖注入

this.carMembersInjector = Car_MembersInjector.create(Tyre_Factory.create());

}

public Car() {

//通过Component以下方法注入car 属性,整个流程

DaggerCarComponent.builder().build().injectCar(this);

}

标记了@Inject 的变量,需要配套对其对象的构造方法 进行@Inject的构造方法标记 来确定用何种构造方法完成对象的构建,才能创建完成。最后在构建好的使用了@Component修饰的接口中,在目标类的构造方法中使用Component的builder()方法调用接口中的方法完成整个方法的构建

四、dagger2的源码

DaggerCarComponent.builder().build().injectCar(this); 通过构建者模式完成创建

在build() 方法中完成了DaggerCarComponent 对象的创建:

public CarComponent build() {

return new DaggerCarComponent(this);

}

private void initialize(final Builder builder) {

//调用成员注入器Car_MembersInjector 通过create方法完成carMembersInjector对象的构建

this.carMembersInjector = Car_MembersInjector.create(Tyre_Factory.create());

}

injectCar():完成实际的成员变量的注入

@Override

public void injectCar(Car car) {

carMembersInjector.injectMembers(car);

}

injectMembers():在 xx_Factory下完成对象的创建

@Override

public void injectMembers(Car instance) {

if (instance == null) {

throw new NullPointerException("Cannot inject members into a null reference");

}

//最后通过tyreProvider.get()函数完成注入

instance.tyre = tyreProvider.get();

}

五、dagger2的常用注解-区别于@Inject

思考:如何需要提供的类构造函数无法修改怎么办?比如引用库 里面的类 无法修改如何解决?

@Module 注解

  • 可以给不能修改的类提供依赖,dagger2 会在该类中寻找实例化某个类所需要的依赖
  • 但需要配合@Provide 注解一起使用

@Provides

  • 标注一个Module中的方法,可以使需要提供依赖的时候被调用。
  • 同时,被@Provides注解修饰的方法他的返回值就是依赖对象的实例。

ps:@Provides注解只能使用在Module当中

1、创建一个用@Module修饰的目标类,和一个@Provides修饰的构建类

@Module

public class CarModule {

//在需要提供依赖的时候被调用

@Provides

static Car provideCar(){

return new Car();

}

}

2、在component 桥梁类里指明modules对应的类字节码对象,是具体哪个module

@Component(modules = CarModule.class)

public interface CarComponent {

void injectCar(Car car);

}

3、make program 后生成 :“CarModule_ProvideCarFactory”

在其内部调用了Module的内部方法函数 创建car对象

@Override

public Car get() {

return Preconditions.checkNotNull(

CarModule.provideCar(), "Cannot return null from a [email protected] @Provides method");

}

一般依赖注入的初始化使用方法:

xxxComponent

        .builder()

        .xxxModule(new xxxModule(

                view:this, new xxx实现InteractorImpl()) )

        .build()

        .inject(activity:this);

ps:

  • InteractorImpl()是P层 被@Inject的标注方法;
  • 同时 P层的对象 也是被@Inject 标记注解
  • 将Module层 标记为@Module注解
  • 根据需要标注 M层内的方法 @Provieds
  • 添加@Component(modules = xx.class)注解,并在其内定义一个inject(传入xxActivity的Activity对象)函数

ps:通过provide注解 ,dagger2 会找到被标记了@Provides注解的方法,并调用其构造方法来完成对象的创建

六、dagger2的补充知识

同ButterKnife 依赖注入框架一样 dagger2也是采用了apt代码自动生成技术,注解停留在编译时,不影响性能。

在编译器build 过程中,APT 也就是dagger-compiler 扫描到注解生成对应的class字节码文件比如扫描到@Provide

就会生成对应的 provide_factory ,来再需要被初始化的时候查找响应的构造方法。

(一) 一些常用的dagger2的使用

1、带一个参数的构造参数

示例:

public class Product(){

@Inject

public product(){}

}

public class Factory{

Product product;

@Inject

public Factory(Product product){

this.product = product;

}

}

解析:在 xxActivity 中进行 inject() 的时候,发现 Factory 的构造函数被 @Inject 标注了且带有一个参数,然后 dagger2 就去寻找 Product 发现它的构造函数也被 @Inject 标注并且无参数,于是 dagger2 把 Product 的实例注入给 xxActivity,然后再去实例化 Factory 的时候用的是已经注入给 xxActivity 的那个 Product 实例。也就是说我们可以这样理解:并不是 Factory 直接实例化 Product,而是 xxActivity 实例化 Product 后交给 Factory 使用的。

2、带Module的Inject方式

以第三方库为OkHttp为例:直接上修改后的代码

@Module

public class HttpActivityModule{

@Provides

OkHttpClient provideOkHttpClient(){

return new OkHttpClient();

}

}

@Component (modules =HttpActivityModule.class)

public interface HttpActivityComponent{

void inject(HttpActivity httpActivity);

}

3、带复杂 Module的Inject方式

使用场景:

在使用dagger2的时候传入一些配置,直接使用module的构造参数传入即可。实例化的时候使用的是builder内部类传入需要的值。

Module中其中一个依赖又要依赖另外一个依赖。如果被@Provides标注的方法带有参数,dagger2会自动寻找本Module中其他返回值类型为参数的类型的且被@Provides标注的方法。

@Module

public class HttpActivityModule{

private int txtSize;

public HttpActivityModule(int txtSize){

this.txtSize = txtSize;

}

@Provides

OkHttpClient provideOkHttpClient(){

OkHttpClient client = new OkHttpClient();

client.setTxtSize(this.txtSize);

return client;

}

@Provides

RetrofitManager provideRetrofitManager( OkHttpClient client){

return new RetrofitManager(client);————return new 谁就在Activity中 @Inject谁

}

}

如果本Module中找不到就会去看这个类的构造参数是否被@Inject标注:

public class OkHttpClient {

private int txtSize;

@Inject

public OkHttpClient(){}

}

@Module

public class HttpActivityModule{

private int txtSize;

public HttpActivityModule(int txtSize){

this.txtSize = txtSize;

}

...

@Provides

RetrofitManager provideRetrofitManager( OkHttpClient client){

return new RetrofitManager(client);————return new 谁就在Activity中 @Inject谁

}

4、Component 依赖Component 模式

有时候一个Component 跟另外一个Component 所提供的依赖有重复的时候,这时候可以像操作extends关键字一样进行依赖关系的注入

a、dependence 方式

@Component(modules = HttpActivityModule.class)

public interface HttpActivityComponent{

RetrofitManager provideRetrofitManager();

}

@Component(dependences =HttpActivityComponent.class)

public interface HttpFragmentComponent{

void inject(HttpFragment httpFragment);

}

在Activity中的关键代码:

private HttpActivityComponent httpActivityComponent;//实例化component对象

在onCreate()中:

httpActivityComponent = DaggerHttpActivityComponent.builder().

.httpActivityModule( new HttpActivityModule (this) )

.build();

public HttpActivityComponent getHttpActivityComponent(){

return httpActivityComponent;

}

在对应的fragment中的关键代码:

@Inject

RetrofitManager retrofitManager;

在onViewCreated()中关键代码:

HttpActivityComponent activityComponent=((HttpActivity) getActivity()).getHttpActivityComponent();

DaggerHttpFragmentCompent

.builder()

.httpActivityComponent(activityComponent)

.build()

.inject(this);

小结:

1、在父Component中要显示的写出需要暴露可提供给子Component的依赖

2、在子Component的注解中使用 :(dependences =父类Component.class)

3、在子Component的实例化   void inject(xxx)

b、subComponent 方式

部分代码同a方式一样进行省略操作,展示不同地方:

@Component(modules = HttpActivityModule.class)

public interface HttpActivityComponent{

HttpFragmentComponent httpFragmentComponent();

}

@SubComponent -真实实现inject()

public interface HttpFragmentComponent{

void inject(HttpFragment httpFragment);

}

在对应的fragment中的关键代码:

HttpActivityComponent activityComponent=

((HttpActivity) getActivity()).getHttpActivityComponent()

.httpFragmentComponent().inject(this);

subComponent 实现方式总结

1、先定义子Component,使用@SubComponent标注

2、定义父Component,在其中定义获取子Component的方法

3、注意子Component 实例化方式

思考:如果子Component构建时候不是无参的而是有参数的情况,又该如何进行处理呢?

解析重点:子Component构建时候传入参数的话,就需要在Component中使用@Subcomponent.Builder注解(接口or抽象类)

示例:

父Component:

@Component( modules = {AppModule.class})

public interface AppComponent{

XxActivityComponent.XxBuilderxxBuiler();

}

子Component:

@Subcomponent ( modules = {XxActivityModule.class})

public interface XxActivityComponent{

void inject( XxActivity activity);

@Subcomponent.Builder

interface XxBuilder{

XxBuilder xxActivityModule(XxActivityModile module);

XxActivityComponent build();

}

}

Module 类:

@Module

public class AppModule{

@Provides

AppBen provideAppBean(){

return new AppBean():

}

@Module

public class XxActivityModule{

private ActivityBean activityBean;

public XxActivityModule (ActivityBean bean){

this.activityBean = bean;

}

@Provides

ActivityBean provideActivityBean(){

return this.activityBean;

}

}

然后看一下在其对应的Application 和Activity 里的实现

App extends Application{ //applicaation 对应了父Component

private AppComponent appComponent;

@Override

public void onCreate(){

appcComponent= DaggerAppComponent.create();

}

public AppComponent getAppComponent(){

return appComponent;

}

}

XxActivity extends AppCompatActivity{

@Inject

AppBean appBean;

@Inject

ActivityBean activityBean;

...  onCreate(){

((App)getApplication() ) .getAppComponent()

.xxBuilder()

.xxActivityModule (new XxActivityModule (new ActivityBean() ) )

.build()

.inject (this);

}

小结:

subComponent 实现方式小结:

1、在子Compoonent ,定义一个借口或是抽象类(通常定义为xxBuilder),使用@Subcomponent.Builder() 标注

2、编写返回值 为XxBuilder,方法的参数需要传入参数的XxModule类型

3、编写返回值为当前子Component 的无参方法

4、 父Component 中定义获取子 Component.Builder的方法

(二) dagger2 中有关Scope 作用域

1、无module 的状态

只需要提供依赖的类,以及Component 都添加@Singleton标注

@Singleton

public class Product {

@Inject

public Product(){}

}

@Singleton

@Component

public interface Factory{

void inject(xxActivity activity);

}

在使用的时候,在对应的Activity里进行声明:

@Inject

Product product;

DaggerXxActivityComponent.create().inject)the);

ps:

如果使用@Singleton 标注了构造参数 ,或是只标注了提供依赖的类 并没有标注Component类 那么 系统会报以下两种错误:

@Scope annotations are not allowed on @Inject  constructors.Annotate the class instead

@包名 .xxComponent (unscoped) may not reference…@Singleton class ….

2、有module 的状态

Component 必须添加@Singleton 标注,再根据需要给Module中的@Provides标注的方法再标注上@Singleton

//无参数构造方法

public class Product{

public Product(){}

}

//Component 接口类:

@Singleton

@Component (modules = XxActivityModule.class)

public interface XxActivityComponent{

void inject(XxActivity activity);

}

@Module

public class XxActivityModule{

@Singleton

@Provides

Product provideProduct(){

return new Product():

}

}

有关Scope 的注意事项:

1、把Scope简单的解释为单例是不科学的。正确的理解应该是:

在某个范围里他是单例(何为作用域呢?可以看作是程序中实例化的Component 的生命周期的长短;

如果在Application里 build的那它的作用域就是整个App的生命周期;

如果在Activity中build的那它的作用域就随着Activity的生命周期保持一致;

2、Scope 只是一个标注 ,只跟它使用的地方及Component实例化的地方有关

3、在Component 依赖Component 的时候,Scope的名字必须不同。

七、dagger2 结合 MVP 模式的小示例片段详解

在普通模式下 使用MVP 模型,P层 和V层 也就是Activity会相互持有对方的引用,如下:

View层 实例代码:

public class PopActivity extends AppCompatActivity implements xxViewImpl {

private xxPresenter xxxPresenter;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

...

//实例化p层对象,将View传递给P对象,并调用P层方法

xxxPresenter = new xxPresenter(this);

xxxPresenter.xxx();

}

}

Presenter层实例代码:

public class xxPresenter {

//View层的引用

private xxViewImpl mView;

public xxPresenter(xxViewImpl view) {

mView = view;

}

public void xxx(){

//调用model数据层方法,加载数据

...

//回调方法在成功 or 失败时候

mView.updateUi();

}

}

以上实例代码就存在Activity和Presenter 存在一定的耦合程度,如果使用依赖注入的形式的话:

View层伪代码:

public class xxActivity extends  AppCompatActivity implements xxViewImpl{

@Inject

xxPresenter xxxPresenter;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

...

//依赖注入框架初始化

DaggerXXComponent.builder()

.XXModule(new XXModule(this))

.build()

.inject(this);

//调用p层方法

xxxPresenter.xx*();

}

}

tips:表明xxPresenter是需要注入到这个Activity中,即表明这个Activity依赖于xxPresenter,但是p对象不能为private修饰符。这里通过new XXModule(this)将view传递到XXModule里,然后XXModule里的provideXXView()方法返回这个View,当去实例化XXPresenter时,发现构造函数有个参数,此时会在Module里查找提供这个依赖的方法,将该View传递进去,这样就完成了presenter里View的注入。

P层伪代码:

public class xxPresenter {

//View层的引用

xxViewImpl mView;

@Inject

public xxPresenter(xxViewImpl view) {

mView = view;

}

public void xxx(){

//调用model数据层方法,加载数据

...

//回调方法在成功 or 失败时候

mView.updateUi();

}

}

tips:在P层构造方法添加@Inject注解,表明这个构造方法与Activity中的p层对象建立联系。也就说档某个类被@Inject,就会到这个类中的构造方法中,查找从而完成依赖注入。

注意??:google不推荐直接将P层的构造参数添加注解,更加推荐将P层放到Module里进行管理,因为这样代码更加容易管理。

新建用于链接的墙梁接口

@Component(modules=XXModule.class )

public interface XXComponent{

void inject(xxActivity activity);

}

tips:根据Component将前面两项建立联系,并自定义void 返回类型的方法传入需要被注入的类Activity。并进行build项目

@Module注解的伪代码:

@Module

public class XXModule {

final xxViewImpl mView;

public XXModule(xxViewImpl view) {

mview = view;

}

@Provides

XXView provideXXVIew(){

return mView;

}

}

tips:用来提供依赖,来使得部分没有构造函数的类的依赖,也就是无法手动@Inject的类比如引用库,系统库;

在module类里,在构造方法里将外界传进来的view赋值给声明的成员对象,并通过@Provides注解标注,以providexx开头名称,并返回声明的view对象,该方法是为了提供依赖,可以创建多个不同的方法提供不同依赖

下面通过伪代码实例再研究下其原理性的内容:

在对已经@Inject 修饰过的构造方法进行makeProgram 过程后,会生成xxx_Factory类:

public XXPresenter_Factory(Provider<xxViewImpl> viewProvider) {

assert viewProvider != null;

this.viewProvider = viewProvider;

}

@Override

public xxPresenter get() {

return new xxPresenter(viewProvider.get());

}

public static Factory<XXPresenter> create(Provider<XXViewImplw> viewProvider) {

return new XXPresenter_Factory(viewProvider);

}

可以看到参数是一个Provider类型,范型参数是View层 ,通过构造方法对viewProvider进行实例化。成员参数没有直接用xxViewImpl 而通过Provider类型修饰是因为前者是一个依赖,而依赖提供者是XXModule,因此这个viewProvider也是由XXModule提供的

下面的get()函数内,也可以看到,在这里实例化了xxPresenter对象 通过new xxPresenter(依赖的xxViewImpl,只是是通过get()函数获取的)

最后通过create()函数,将含有一个参数viewProvider用来创建 XXPresenter_Factory类

接下来再来看一下,XXModule类对应的注入类

public final class XXModule_ProvideMainViewFactory implements Factory<xxViewImpl> {

private final XXModule module;

public XXModule_ProvideXXViewFactory(XXModule module) {

assert module != null;

this.module = module;

}

@Override

public xxViewImpl get() {

return Preconditions.checkNotNull(

module.provideXXView(), "Cannot return null from a [email protected] @Provides method");

}

public static Factory<xxViewImpl> create(XXModule module) {

return new XXModule_ProvideXXViewFactory(module);

}

}

在Module类中被@Provides修饰的方法都会对应的生成一个工厂类。这里是XXModule_ProvideXXViewFactory,

我们看到这个类里有一个get()方法,其中调用了XXModule里的provideXXView()方法来返回我们所需要的依赖xxViewImpl。还记得在xxPresenter_Factory里的get()方法中,实例化XXPresenter时候的参数viewProvider.get()吗?到这里我们就明白了,原来那个viewProvider就是生成的XXModule_ProvideXXViewFactory,然后调用了其get()方法,将我们需要的xxViewImpl注入到XXPresenter里。

思考:xxPresenter_Factory的创建是由create()完成的,但是这个create是在什么位置调用的?

答案显而易见肯定是在桥梁类Component 注入类中完成的。

示例伪代码:

public final class DaggerXXComponent implements XXComponent {

private Provider<xxViewImpl> provideXXViewProvider;

private Provider<XXPresenter> XXPresenterProvider;

private MembersInjector<XXActivity> XXActivityMembersInjector;

private DaggerXXComponent(Builder builder) {

assert builder != null;

initialize(builder);

}

public static Builder builder() {

return new Builder();

}

在initialize()函数中,可以看到三个create()方法,每一个provider工厂都对应一个providerFactory.craete()实例,将providerXXViewProvider作为参数传递给XXPresenterProvider,后又又作为参数传递给Activity的注入类:XXActivityMembersInjector对象进行实例化。

@SuppressWarnings("unchecked")

private void initialize(final Builder builder) {

this.provideXXViewProvider = XXModule_ProvideXXViewFactory.create(builder.XXModule);

this.XXPresenterProvider = XXPresenter_Factory.create(provideXXViewProvider);

this.XXActivityMembersInjector = XXActivity_MembersInjector.create(XXPresenterProvider);

}

从伪代码中可以看出 ,定义的xxComponent会生成一个对应的DaggerXXComponent类,并且该类实现了XXComponent接口里的方法。

@Override

public void inject(XXActivity activity) {

XXActivityMembersInjector.injectMembers(activity);//将Activity注入到该类中

}

public static final class Builder {

private XXModule XXModule;

private Builder() {}

public XXComponent build() {

if (xxModule == null) {

throw new IllegalStateException(XXModule.class.getCanonicalName() + " must be set");

}

return new DaggerXXComponent(this);

}

public Builder XXModule(XXModule XXModule) {

this.XXModule = Preconditions.checkNotNull(XXModule);

return this;

}

}

}

从伪代码中发现,Builder内部类就是用来创建module以及自身实例的,所以放在了DaggerXXComponent里进行初始化依赖,而真正让依赖关联起来的就是xxActivityMembersInjector对象。

public final class XXActivity_MembersInjector implements MembersInjector<XXActivity> {

private final Provider<XXPresenter> XXPresenterProvider;

public XXActivity_MembersInjector(Provider<XXPresenter> XXPresenterProvider) {

assert XXPresenterProvider != null;

this.XXPresenterProvider = XXPresenterProvider;

}

public static MembersInjector<XXActivity> create(

Provider<XXPresenter> XXPresenterProvider) {

return new XXActivity_MembersInjector(XXPresenterProvider);

}

@Override

public void injectMembers(XXActivity instance) {

if (instance == null) {

throw new NullPointerException("Cannot inject members into a null reference");

}

instance.XXPresenter = XXPresenterProvider.get();

}

public static void injectXXPresenter(

XXActivity instance, Provider<XXPresenter> XXPresenterProvider) {

instance.XXPresenter = XXPresenterProvider.get();

}

}

从这里发现,将xxPresenterProvider中创建好的xxPresenter实例赋值给instance的成员xxPresenter。这样用@Inject标注的xxPresenter就得到了实例化了,进而在代码中进行实际使用。

小结:

  • Module 并不是必须品,但是Component 是必不可少的
  • 编译后生成的Component实现类的名称必然是:Dagger+自定义Component接口名称
  • 在使用dagger2 时候,定义类或是方法名字的时候要遵守google的固定标准方便代码维护
  • 定义的Component 和Module结尾一样要以此结尾命名
  • Module中@Provides 标注的方法要遵循规则以provide开头命名
  •   Component中 返回值为void且有参数的方法,参数是最重要的代表的是要注入的目标类,方法名一般使用inject
  • Component 中返回值不为void 且无参数,返回值是最重要的代表的是暴露给子Component 使用的依赖 或是获取的子Component的类型

本章部分内容摘自:

https://www.jianshu.com/p/39d1df6c877d

https://juejin.im/entry/59e55646f265da43111f4548

感谢分享 respect

结语:

尽可能的达到高内聚低耦合的目标,正是呆哥兔追求的意义。

八、dagger2的扩展延伸-dagger.android

用于android的注入方式:

@Component( modules = {AndroidInjectionModule.class,AppModule.class})

public interface AppComponent {

void inject(App app);

}

@Subcomponent (modules = {MainActivityModule.class})

public interface MainActivityComponent extends AndroidInjector<MainActivity>{

void inject(MainActivity activity);

@Subcomponent.Builder

public abstrace class Builder extends AndroidInjector.Builder<MainActivity>{}

}

注意:

  • 在父Module中添加 @Module(subcomponents ={子Component})方式进行关联
  • 在这样的情况下,子Component中必须存在被@Subcomponent.Builder标注的抽象类或是接口,否则会报异常

@Module

public class MainActivityModule{

@Provides

ActivityBean provideActivityBean(){

return new ActivityBean():

}

}

@Module (subcomponents = {MainActivityComponent.class});

public abstract class AppModule{

@Provides

static AppBean provideAppBean(){

return new AppBean():

}

@Binds

@IntoMap

@ActivityKey( MainActivity.class)

abstract AndroidInjector.Factory

<? extends Activity>bindFactory(MainActivityComponent).inject(this);

在Activity中的实现:

public class App extends Application implements HasActivityInjector{

@Inject

DispatchingAndroidInjector<Activity> activityInjector;

@Override

public void onCreate(){

DaggerAppComponent.create().inject(htis):

}

@Override

public AndroidInjector<Activity> activityInjector(){

return activityInjector;

}

}

public class MainActivity extends AppCompatActivity {

@Inject

AppBean appBean;

@Inject

ActivityBean activityBean;

@Override

protected void onCreate(Bundle saveInstanceState){

AndroidInjection.inject(this);

super.onCreate(saveInstanceState);

….

}

}

dagger.android 小结

1、在AppComponent中将dagger2库里的AndroidInjectionModule注入到Applicaation中,并将Application实现响应的接口,并返回响应的方法;

2、子Component继承自AndroidInjector,内部BUilder使用抽象类并继承AndroidInjector.Builder;

3、父 Module 使用 @Module ( subcomponents = {} ) 的方式关联子 Component,并在父 Module 中编写返回值为 AndroidInjector.Factory、参数为子 Component.Builder 的抽象方法(如果有其他被 @Provides 标注的方法,应将方法改为 static,否则报错);

4、最后在 Acitivity 的 onCreate() 中第一行代码的位置使用 AndroidInjection 注入,如果是 Fragment 则是在 onAttach() 方法中,其他的请自行查阅。

5、dagger.android 库也提供了其他实现方式,诸如DaggerApplication、DaggerActivity、DaggerFragment、DaggerService、DaggerBroadcastReceiver 等实现类,有兴趣的小伙伴自己研究一下吧

原文地址:https://www.cnblogs.com/cold-ice/p/9466866.html

时间: 2024-11-04 19:41:17

Android 常用开源框架源码解析 系列 (九)dagger2 呆哥兔 依赖注入库的相关文章

Android 常用开源框架源码解析 系列 (十)Rxjava 异步框架

一.Rxjava的产生背景 一.进行耗时任务 传统解决办法: 传统手动开启子线程,听过接口回调的方式获取结果 传统解决办法的缺陷: 随着项目的深入.扩展.代码量的增大会产生回调之中套回调的,耦合度高度增加的不利场景.对代码维护和扩展是很严重的问题. RxJava本质上是一个异步操作库 优点: 使用简单的逻辑,处理复杂 ,困难的异步操作事件库;在一定程度上替代handler.AsyncTask等等 二.传统的观察者模式 使用场景 1.一个方面的操作依赖于另一个方面的状态变化 2.如果在更改一个对象

Android 常用开源框架源码解析 系列 (十一)picasso 图片框架

一.前言 Picasso 强大的图片加载缓存框架 api加载方式和Glide 类似,均是通过链式调用的方式进行调用 1.1.作用 Picasso 管理整个图片加载.转换.缓存等策略 1.2.简单调用: Picasso .with(this 传入一个单例,上下文).load("url"/file文件/资源路径) .into() 1.2.1 .一些简单的链式调用参数 .placeholder(R.drawable.xx)  //网络未加载完成的时候显示的本地资源图片 .error(R.dr

Android 10大开源常用框架源码解析 系列 (二)网络框架之一 OkHttp杂题

1.Android基础网络编程:socket.HttpClient.HttpURLConnection     1.1 Socket 定义 是一个对TCP/IP协议进行封装的编程调用接口,本身不是一种协议是接口Api!!     成堆出现,一对套接字:包括ip地址和端口号   基于应用层和传输层抽象出来的一个层.App可以通过该层发送.接收数据,并通过Socket将App添加到网络当中 简单来说就是应用与外部通信的端口,提供了两端数据的传输的通道     1.2 Socket通信模型 基于TCP

【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 Android热更新开源项目Tinker源码解析系类之三:so文件热更新 转载请标明本文来源:http://www.cnblogs.com/yyangblog/p/6252855.html更多内容欢迎star作者的github:https://github.com/LaurenceYang/artic

【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源热更新 Android热更新开源项目Tinker源码解析系类之三:so热更新 转载请标明本文来源:http://www.cnblogs

Android 开源项目源码解析(第二期)

Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations 源码解析 SlidingMenu 源码解析 Cling 源码解析 BaseAdapterHelper 源码分析 Side Menu.Android 源码解析 DiscreteSeekBar 源码解析 CalendarListView 源码解析 PagerSlidingTabStrip 源码解析 公共

iOS开源库源码解析之Mantle

来自Leo的原创博客,转载请著名出处 我的StackOverflow 这个源码解析系列的文章 AsnycDispalyKit SDWebImage Mantle(本文) AFNetworking(3.0) MBProgressHud SwiftJSON MagicRecord Alamofire 前言 iOS开发中,不管是哪种设计模式,Model层都是不可或缺的.而Model层的第三方库常用的库有以下几个 JSONModel Mantle MJExtension JSON data到对象的转换原

Android LayoutInflater.from().inflate()源码解析

我们知道,在Activity#setContentView()中会调用PhoneWindow#setContentView().而在PhoneWindow#setContentView()中有这么一句mLayoutInflater.inflate(layoutResID, mContentParent).这行代码的作用是将我们的activity_main.xml填充到mContentParent中去.详见:setContentView源码解析.在写adapter的时候,也经常写mInflater

iOS开源库源码解析之SDWebImage

来自Leo的原创博客,转载请著名出处 我的stackoverflow 这个源码解析系列的文章 AsnycDispalyKit SDWebImage(本文) 前言 SDWebImage是iOS开发中十分流行的库,大多数的开发者在下载图片或者加载网络图片并且本地缓存的时候,都会用这个框架.这个框架相对来说,源代码还是比较少的.本文会详细的讲解这些类的架构关系和原理. 本文会先介绍类的整体架构关系,先有一个宏观的认识.然后讲解sd_setImageWithURL的加载逻辑,因为这是SDWebImage