Dagger2 使用

Dagger2 使用

Dagger2是一款基于Java注解来实现的完全在编译阶段完成依赖注入的开源库,主要用于模块间解耦、提高代码的健壮性和可维护性。Dagger2在编译阶段通过apt利用Java注解自动生成Java代码,然后结合手写的代码来自动帮我们完成依赖注入的工作。

1. 依赖倒置

依赖倒置原则(Dependence Inversion Principle),程序要依赖抽象,而不是依赖具体实现。 A.高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。 B.抽象不应该依赖于具体,具体应该依赖于抽象。

什么是依赖关系? A类中要用的B类中的部分功能,A类就得持有一个B类对象,即A类依赖B类。

public class A {
    private B mb;
    public void action(){
        mb.show();
    }
}
复制代码

依赖有什么问题? 软件的需求都是不断变动的,当B类发生变动(比如B类删除了show方法),那A类对象要正常工作,就得跟着变动。耦合程度太高。

依赖抽象是什么意思? 高层次的模块不直接持有低层次模块的引用,而是持有一个接口类型的引用,低层次模块实现该接口;一般而言只要定义合理,接口是不怎么变动的,而低层次模块的实现变动不会影响到高层次模块对接口方法的调用。

2. 依赖注入

前面提到让A类依赖接口,但始终要传入一个实现了该接口的实例对象时才能让A类对象正常工作,通常有以下几种形式。

public class A {
    // IB是个接口
    private IB mb;

    public A() {
        // 在A类中生成
        this.mb = new B();
    }

    public A(IB mb) {
        // 构造A时传入B对象
        this.mb = mb;
    }

    public void setMb(IB mb) {
        // 使用setter来传入B对象实例
        this.mb = mb;
    }

    public void action() {
        mb.show();
    }
}
复制代码

在A类外部构造好IB接口的实例对象,传入A对象的方式就是依赖注入。

3. Dagger2基本使用

Dagger2使用编译器注解生成代码来完成依赖注入,使用分为三大步: 引入依赖库

    implementation 'com.google.dagger:dagger:2.21'
    implementation 'com.google.dagger:dagger-android:2.21'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.21'
复制代码
  1. 构建依赖对象,在外部实现依赖对象的创建,Dagger2中负责提供依赖的组件被称为Module。使用@Module标记类,@Provides标记方法,方法返回依赖对象实例。
@Module
public class MainModule {
    @Provides
    Person personProviders() {
        System.out.println("a person created in MainModule ");
        return new Person();
    }
}
复制代码
  1. 创建Injector,用于将依赖对象注入到消费依赖对象的组件中去,Dagger2中称为Component。
@Component(modules = {MainModule.class})
public interface MainComponent {
    void inject(MainActivity mainActivity);
}
复制代码
  1. 完成依赖注入,因为MainActivity依赖Person类,需要在MainActivity中构建Injector对象。
public class MainActivity extends AppCompatActivity {
    @Inject
    Person mPerson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main2);

        MainComponent mainComponent = DaggerMainComponent.builder().mainModule(new MainModule()).build();
        mainComponent.inject(this);
    }
}
复制代码

以上就是最简单的注入,Mudule是需求提供方,在其中创建需要注入的对象;MainActivity是需求方,在Activity中声明所需要的对象。MainComponent是连接彼此的桥梁。

下面解释一下上面用到的几个注解

@Inject 可用在三个地方

  1. 用于类成员变量前,标记该字段需要被注入一个对象。

        @Inject
        Person mPerson;
    复制代码

    Component会找到合适的Module中合适的被@Provides 标记的方法,调用该方法将对象注入。

  2. 用于构造方法前,在dagger2通过Module找不到一个方法来生成该类实例时,会调用该构造方法生成实例对象,并将该实例注入给需求方。

    	   @Inject
        public Person() {
            ID = UUID.randomUUID().toString();
        }
    	或者
    	    @Inject Person(String name){
            ID=UUID.randomUUID().toString();
            this.name=name;
        }
    复制代码

    当然不能同时标记两个构造函数,不然会引起歧义。

  3. 用在普通方法前,当一个Activity中的成员变量都已经注入后,会调自动调用被标记的普通方法。如果该普通方法有参数,则也会从Module中寻找或通过构造函数来创建的方式来创建所需参数对象。

     	@Inject
        public void normalMethod(Person person) {
            Toast.makeText(this, "normalMethod" + person +Toast.LENGTH_LONG).show();
        }
    复制代码

@Module 用在Module类前面,被标记的类包含了一系列被@Provides标记的方法,可以对外提供对象实例。

因为有些对象需要手动创建,不能通过给构造函数加@Inject的方式完成,比如第三方库中的对象。

@Module
public class CcModule {
    private ICommonView mICommonView;
    public CcModule(ICommonView ICommonView) {
        mICommonView = ICommonView;
    }
    @ActivityScope
    @Provides
    public ICommonView provideICommonView() {
        return this.mICommonView;
    }
}
复制代码

@Provides 用于标注Module所标注的类中的方法,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值;

@Component 标记在接口上,是一个容器,指明了该Component包含的Module,以及该容器绑定到哪个组件上。一个Activity或Fragment只能注册一个Component,而该Component要满足能提供该Activity或Fragment中所需的所有要注入的对象才行。

@ActivityScope
@Component(modules = {PersonModule.class})
public interface PersonComponent {
    void inject(MainActivity mainActivity);
}
复制代码

意思是PersonComponent中要能提供Mainactivity中所需的所有要注入的对象才行,不然编译不通过。 而且inject中的类型必须是实际类型。

@Singleton 单例模式,当我们需要创建一个单例对象时,使用该注解即可,方式如下:

// 1. 创建module,用@Singleton标记提供对象的方法。
@Module
public class PersonModule {
    @Provides
    @Singleton
    public Person providePerson() {
        return new Person("姓名","password",18);
    }
}
// 2. 在module所在的Component上添加@Singleton
@Singleton
@Component(modules = {PersonModule.class})
public interface PersonComponent {
    void inject(MainActivity mainActivity);
}

复制代码

如此,MainActivity中的注入的Person对象就是单例对象了,但这个单例模式的仅在MainActivity中有效,当PersonComponent被多个Activity使用时,每个Activity都会有一个Person单例对象,但从全局来看实际上实例化了多个Person对象。 所以,要创建全局单例,就只能在Application对应的Component中创建。

@dependencies 依赖其他Component,一个Component依赖其他Component,则该Component就包含了其他Component可以提供的对象。

@Component(modules = {CarModule.class}, dependencies = {PersonComponent.class})
public interface CarComponent {
    void inject(MainActivity mainActivity);
}
复制代码

CarComponent依赖了PersonComponent,则CarComponent也有了提供Person相关对象的能力。依赖和java中继承很相似,子类“依赖”父类,就持有了父类的非私有成员变量。

@Named 解决注入对象的冲突,假设我们的Activity中需要两个不同类型的Person,有两个provide方法;但注入时Dagger是无法区分谁是谁的。使用@Named注解可以解决该问题。

@Module
public class PersonModule {
    @Provides
    @Named("man")
    public Person provideManPerson() {
        return new Person("男人", "password", 18);
    }

    @Provides
    @Named("woman")
    public Person provideWomanPerson() {
        return new Person("女人", "passwd", 18);
    }
}

// MainActivity中
    @Inject
    @Named("man")
    Person mManPerson;

    @Inject
    @Named("woman")
    Person mWomanPerson;
复制代码

@Qualifier 以上冲突解决方案,需要字符串一致,不够优雅容易出错。我们可以使用@Qualifier定义新的注解来解决这个问题。

// 1. 定义注解
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Man {
}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Woman {
}

// 2. Module中使用类型区分注解
@Module
public class PersonModule {
    @Provides
    @Man
    public Person provideManPerson() {
        return new Person("男人", "password", 18);
    }

    @Provides
    @Woman
    public Person provideWomanPerson() {
        return new Person("女人", "passwd", 18);
    }
}
// 3. MainActivity中使用类型区分注解
@Inject
@Man
Person mManPerson;
@Inject
@Woman
Person mWomanPerson;

复制代码

当然声明的注解也可以用在方法参数中。

@Scope 自定义范围注解,我们可以为不同的组件提供不同的范围注解,比如为Activity、Fragment、Application等不同组件提供不同的范围标签。

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值