Android应用框架之Dagger依赖注入框架初步

主要注解

javax.inject包

  1. @Inject
  2. @Scope和@Singleton
  3. @Named和@Qualifier

dagger包

  1. @Module和@Provides
  2. @Component
  3. @Subcomponent
  4. Lazy:非注解类,用于延迟注入

注解使用

@Inject注解

用于标记需要注入的依赖,或者标记用于提供依赖的构造方法。

在Dagger2中@Inject有3种使用形式:

1、构造器注入,@Inject标注在构造器上:
  • 告诉Dagger2可以使用这个构造器构建对象。
  • 注入构造器所需要的参数的依赖。

构造器注入的局限:

  • 如果有多个构造器,我们只能标注其中一个,无法标注多个。
  • 如果需要注入第三方类库中类对象,无法完成注入。

例如:

public class Engine {

    @Inject
    public Engine() {}

}

public class Car {

    private Engine engine;

    @Inject
    public Car(Engine engine) {
        this.engine = engine;
    }

}
2、属性注入

如MainActivity类,标注在属性上。注意,被标注的属性不能使用private修饰,否则无法注入。

public class MainActivity extends Activity {

    @Inject
    Car car;

}
3、方法注入

标注在public方法上,Dagger2会在构造器执行之后立即调用这个方法。
方法注入和属性注入基本上没有区别, 那么什么时候应该使用方法注入呢?
比如该依赖需要this对象的时候,使用方法注入可以提供安全的this对象,因为方法注入是在构造器之后执行的。

public class MainActivity extends Activity {

    private Car car;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerMainActivityComponent.create().inject(this);
    }

    @Inject
    public void setCar(Car car) {
        this.car = car;
    }
}

@Module和@Provides

使用@Inject标记构造器提供依赖是有局限性的,比如需要注入的对象是第三方库提供的,我们无法在第三方库的构造器上加上@Inject注解。
或者,我们使用依赖倒置的时候,因为需要注入的对象是抽象的,@Inject也无法使用,因为抽象的类并不能实例化,比如:

public interface Vehicle {
    void drive();
}

public class Car implements Vehicle {

    @Inject
    public Car() {}

    public void drive() {
        Log.d("dagger", "drive car!");
    }
}

public class Driver {

    private Vehicle mVehicle;

	@Inject
    public Driver(Vehicle vehicle) {
        mVehicle = vehicle;
    }

    public void drive() {
        mVehicle.drive();
    }
}

编译的时候会报错,Vehicle是接口类,不能实现对象实例化,这时候,就需要用到@Module和@Provides来提供依赖了。

@Module
public class VehicleModule {

    @Provides
    public Vehicle provideCar() {
        return new Car();
    }

}

@Component(modules = VehicleModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

@Module需要和@Provide一起使用才具有作用,并且@Component也需要指定Module。
@Module是告诉Component,可以从这里获取依赖对象。Component就会去找被@Provides标注的方法,相当于构造器的@Inject,可以提供依赖。

依赖多个modules:

@Module
public class DriverModule {

    @Provides
    public Driver providerDriver(Vehicle vehicle) {
        return new Driver(vehicle);
    }
}

@Module
public class VehicleModule {

    @Provides
    public Vehicle provideCar() {
        return new Car();
    }

}

@Component(modules = {VehicleModule.class, DriverModule.class})
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

@Named和@Qualifier

当我有两个相同的依赖(都继承某一个父类或者都实现某一个接口)可以提供给高层时,那么程序就不知道我们到底要提供哪一个依赖,因为它找到了两个。
这时候我们就可以通过限定符为两个依赖分别打上标记,指定提供某个依赖。

@Module
public class VehicleModule {

	@Named("car")
    @Provides
    public Vehicle provideCar() {
        return new Car();
    }

	@Named("truck")
    @Provides
    public Vehicle provideTruck() {
        return new Truck();
    }
}

public class Driver {

    private Vehicle mVehicle;

	@Inject
    public Driver(@Named("car") Vehicle vehicle) {
        mVehicle = vehicle;
    }

    public void drive() {
        mVehicle.drive();
    }
}

@Qualifier的作用和@Named是完全一样的,不过更推荐使用@Qualifier,因为@Named需要手写字符串,容易出错。
@Qualifier不是直接注解在属性上的,而是用来自定义注解的。

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

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

@Module
public class VehicleModule {

    @CarVehicle
    @Provides
    public Vehicle provideCar() {
        return new Car();
    }

    @TruckVehicle
    @Provides
    public Vehicle provideTruck() {
        return new Truck();
    }
}

public class Driver {

    private Vehicle mVehicle;

    public Driver(@CarVehicle Vehicle vehicle) {
        mVehicle = vehicle;
    }

    public void drive() {
        mVehicle.drive();
    }
}

@Component

可以理解为注入器,在需要注入依赖的目标类中使用Component完成注入。一个Component可以依赖一个或多个Component,并拿到被依赖Component暴露出来的实例。

Component中一般使用两种方式定义方法:

  1. void inject(目标类 obj):Dagger2会从目标类开始查找@Inject注解,自动生成依赖注入的代码,调用inject可完成依赖的注入。

  2. Object getObj()
    Dagger2会到Driver类中找被@Inject注解标注的构造器,自动生成提供Driver依赖的代码,这种方式一般为其他Component提供依赖。
    被依赖的Component必须暴露需要提供依赖的接口,否则依赖的Component无法完成注入。

public class MainActivity extends Activity {

    @Inject
    Driver driver;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 这个类是重新编译后Dagger2自动生成的,所以写这行代码之前要先编译一次
        DaggerMainActivityComponent.create().inject(this);
    }
}

@Scope和@Singleton

@Scope不能直接拿来使用,用于定义注解,@Singleton是它的默认实现。

用@Scope自定义一个注解@ActivityScope,注解名字要见名知意:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {}

这里先看下默认注解@Singleton,看看它的用法

@Module
public class DriverModule {

	@Singleton
    @Provides
    public Driver providerDriver(@TruckVehicle Vehicle vehicle) {
        return new Driver(vehicle);
    }
}

@Singleton
@Component(modules = {DriverModule.class, VehicleModule.class})
public interface DriverComponent {

    Driver getDriver();

    @CarVehicle
    Vehicle getVehicle();

}


@ActivityScope // 这里使用@Singleton,或者没有@Scope注解都会报错,需要自定义一个非@Singleton注解
@Component(dependencies = DriverComponent.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

这么实现,注入的多个Pot对象,公用一个实例,但是是有前提的,必须要在PotComponent对象实例相同的情况。
如果新建了一个PotComponent对象,通过该Component注入的对象,必然是不一样的。这里的单例可以理解为"局部单例",是在Component对象发生没有变化的前提下的单例。

那么,怎么来实现"全局单例"呢?这就需要利用Component对象不发生改变这条规则,保证Component对象全局不变,那就保证了用该Component对象注入的对象唯一性。

比如,我们可以在Application实例化一个Component,在多个需要用到的地方,使用这里定义的同一个Component。

public class App extends Application {

    private PotComponent potComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        potComponent = DaggerPotComponent.builder()
                .flowerComponent(DaggerFlowerComponent.create())
                .build();
    }
    
    public PotComponent getPotComponent() {
        return potComponent;
    }
}

@Subcomponent

@Subcomponent提供了另外一种联系Component的方式,可以理解为子组件,有点像继承关系,但是写法又不像,挺费解的。
类比类继承关系,子类继承父类,子类包含父类的特性;但是这里,被@Subcomponent标记的Component,像是一个零部件,作为部分功能存在,父Component却起主导作用。

看代码吧:

@Module
public class AppModule {
    @Provides
    public Engine provideEngine() {
        return new Engine();
    }
}

@Component(modules = AppModule.class)
public interface AppComponent {
    //需要将SubComponent追加到被依赖的Component中
    PresenterComponent addSub(PresenterModule PresenterModule);
}


@Module
public class PresenterModule {
    @Provides
    public Car provideCar(Engine engine) {
        // Car 依赖 Engine
        return new Car(engine);
    }
}

@Subcomponent(modules = PresenterModule.class)
public interface PresenterComponent {
    void inject(MainActivity activity);
}

public class MainActivity extends Activity {

    @Inject
    Car car;
	
	@Inject
    Engine engine;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        AppModule AppModule = new AppModule();
        AppComponent AppComponent = DaggerAppComponent.builder()
                .AppModule(AppModule)
                .build();

        PreseterModule PreseterModule = new PreseterModule();
        PresenterComponent PresenterComponent = DaggerPresenterComponent.builder()
                .AppComponent(AppComponent)
                .PreseterModule(PreseterModule)
                .inject(this);
    }
}

注意点:
1、dependencies实现的Component间的依赖关系,被依赖的Component需要暴露提供依赖的方法,否则无法注入依赖;而@SubComponent的实现,不需要在被依赖的Component暴露方法,可以完全继承Component中的全部依赖。
2、在Component间需要保持独立性的时候建议使用dependencies;而在Component间关系紧密,或者说需要组合功能时,建议使用@SubComponent

Lazy

Dagger2还支持Lazy模式,通过Lazy模拟提供的实例,在@Inject的时候并不初始化,而是等到你要使用的时候,主动调用其.get方法来获取实例。

public class MainActivity extends Activity {

    @Inject
    Lazy<Driver> driverLazy;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerMainActivityComponent.builder()
                .potComponent(((App) getApplication()).getDriverComponent())
                .build().inject(this);

        Driver driver = driverLazy.get();
    }
}

参考资料

Dagger2 最清晰的使用教程
dagger2系列之依赖方式dependencies、包含方式(从属方式)SubComponent

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值