美文网首页
Dagger 2 使用详解-基础篇

Dagger 2 使用详解-基础篇

作者: changchengfeng | 来源:发表于2018-05-22 21:17 被阅读85次

    这篇文章主要记录一下Dagger 2的使用,详情Dagger 2 官方文档

    dagger 2 是一款依赖注入框架,通俗的就是一个类中的属性对象(组合)通过框架注入而无需显示调用 new Object ,主要的好处就是解耦,降低两个类的关联.

    1. @Inject

    • 提供实例的类的是默认的构造函数
    1. @Inject 注解在一个 Filed 域. 表示在 MainActivity类中注入一个 Engine 对象
    public class MainActivity extends AppCompatActivity {
    
        // 这个 @Inject 表示要注入一个 Engine对象
        @Inject Engine mEngine;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            DaggerMainComponent.create().inject(this);
    
        }
    
        public void onClick(View view) {
            Toast.makeText(this,mEngine.toString(),Toast.LENGTH_SHORT).show();
        }
    }
    
    
    1. @Inject 注解在一个 构造函数中,表示可以提供该类的实例供注入别的类中 ,有且只有一个Engine 的构造函数能被 @Inject 标注
    public class Engine {
        private String model;
        private int age;
    
        // 提供 Engine 对象
        @Inject
        public Engine() {
            model = "xxxx";
            age = 1990;
        }
        
        public Engine(String model, int age) {
            this.model = model;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Engine{" +
                    "model='" + model + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
    1. 定义一个接口并用 @Component 标注

    用来把 Engine 注入到 MainActivity 中

    @Component
    public interface MainComponent {
        /**
         * 必须让Component知道需要往哪个类中注入,这个方法名可以是其它的,但是推荐用inject
         * 目标类MainActivity必须精确,不能用它的父类
         */
        void inject(MainActivity activity);
    }
    
    1. 在 MainActivity 中 onCreate 方法中调用
    // DaggerMainComponent 这个类是 Dagger 框架自动生成的辅助类,
    DaggerMainComponent.create().inject(this);
    

    之后就可以使用 mEngine 这个对象了,框架是通过调用 Engine 的构造函数生成了一个 Engine 对象 并注入了 MainActivity

    • 注入的对象构造函数有参数,也需要提供参数对象
    public class MainActivity extends AppCompatActivity {
    
        @Inject Car mCar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            DaggerMainComponent.create().inject(this);
    
        }
    
        public void onClick(View view) {
            Toast.makeText(this,mCar.toString(),Toast.LENGTH_SHORT).show();
        }
    }
    

    dagger 框架会直接通过 Engine Tire的被 @Inject 的构造函数提供 Car 构造函数的参数对象

    public class Car {
        private Engine mEngine;
        private Tire mTire;
    
        @Inject
        public Car(Engine engine,Tire tire) {
            mEngine = engine;
            mTire = tire;
    
        }
    
        @Override
        public String toString() {
            return "Car{" +
                    "mEngine=" + mEngine +
                    ", mTire='" + mTire + '\'' +
                    '}';
        }
    }
    
    

    Engine对象见 1.2

    public class Tire {
    
    
        private int radius;
    
        // 提供 Car 构造函数的 Tire 对象
        @Inject
        public Tire() {
            radius = 80;
        }
    
        @Override
        public String toString() {
            return "Tire{" +
                    "radius=" + radius +
                    '}';
        }
    }
    

    @Inject无法满足所有的要求 :

    • 接口无法被构造.
    • 引用的第三方框架对象无法被注解.
    • 可配置的对象无法被配置,只能使用构造函数构造

    2. 使用 @Module @Provides

    @Inject的局限性, Dagger 提供了另外一套提供实例的办法

    @Module
    public class TireModule {
    
        //可以提供一个 Tire 对象实例
        @Provides
        public static Tire provideTire()
        {
            return new Tire();
        }
    }
    
    @Module
    public class EngineModule {
    
        // 可以提供一个 Engine 对象实例
        @Provides
        public static Engine provideEngine()
        {
           return new Engine("F1234",2018);
        }
    }
    
    @Component(modules = {TireModule.class,EngineModule.class})
    public interface MainComponent {
        void inject(MainActivity activity);
    }
    
    

    当要注入 Car 现在构造函数有两个地方可以提供 Engine Tire对象作为 Car 构造函数的参数

    1. @Inject 标注的构造函数提供
    2. @Module @Provides 提供

    Dagger 优先使用 @Module @Provides 提供的对象.

    1. 利用 @Module @Provides 也可以直接提供 Car 对象

    3. @Scope:作用域 并不使用在类中,用于定义注解

    @Scope的作用主要是在组织Component和Module的时候起到一个实例作用范围的提醒(类似是生命周期)

    • 已经定义好的作用域 @Singleton
    @Scope
    @Documented
    @Retention(RUNTIME)
    public @interface Singleton {}
    

    @Provides 加上 @Singleton 表明会使用相同的 Car 对象去初始化所有需要 Car 对象的客户端

    
    @Module
    public class CarModule {
        @Provides
        @Singleton
        public static Car provideCar1()
        {
            return new Car(new Engine("F-7890",2020),new Tire());
        }
    }
    

    Component的也必须标注 @Singleton 不然会编译报错,作用域不匹配

    @Component(modules = CarModule.class)
    @Singleton
    public interface MainComponent {
        Car maker();
    }
    
    
    public class MainActivity extends AppCompatActivity {
        private Car car1;
        private Car car2;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            MainComponent component = DaggerMainComponent.create();
            car1=component.maker();
            car2=component.maker();
        }
    
        public void onClick(View view) {
            Toast.makeText(this," car1==car2 : "+(car1==car2),Toast.LENGTH_SHORT).show();
        }
    }
    
    

    car1 = car : true

    若去掉 @Singleton 注解结果为 false

    @Singleton并不是我们传统单例模式的那种作用,只能保证在一个Component当中只提供同一个实例对象 但并不是整个的应用全局只有一个对象,调用DaggerMainComponent.create()两次产生两个 MainComponent对象也会产生两个Car对象,要保证应用全局单例可以配合android中application的特殊性,就可以实现应用全局单例

    Module中的方法 使用@Singleton标注后,那对应的Component也必须采用@Singleton标注,表明它们的作用域一致,否则编译的时候会报作用域不同的错误。

    • 自定义作用域

    @ActivityScoped是一个自定义的作用域注解,作用是允许对象被记录在正确的组件中,当然这些对象的生命周期应该遵循activity的生命周期

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

    @FragmentScoped是一个自定义的作用域注解,作用是允许对象被记录在正确的组件中,当然这些对象的生命周期应该遵循Fragment的生命周期

    
    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE, ElementType.METHOD})
    public @interface FragmentScoped {}
    
    

    4. @Qualifier:限定符 并不使用在类中,用于定义注解

    • 定义好的限定符 @Named
    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Named {
    
        /** The name. */
        String value() default "";
    }
    
    
        @Named("provideCar2")
        @Inject Car mCar;
    

    在宿主中使用时,可以通过 @Named 来选择所需要的那个实例

    
    @Module
    public class CarModule {
        @Provides
        public static Car provideCar1()
        {
            return new Car(new Engine("F-7890",2020),new Tire());
        }
        @Provides
        @Named("provideCar2")
        public static Car provideCar2()
        {
            return new Car(new Engine("X-7890",2030),new Tire());
        }
    }
    
    
    @Component(modules = CarModule.class)
    public interface MainComponent {
        void inject(MainActivity activity);
    }
    
    • 自定义限定符
    @Qualifier
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ForTest {
    }
    

    5. @Binds

    @Binds 可以用于当需要一个父类对象时,用子类对象进行替代 (接口同理)

    public class Car {
    
    }
    
    public class BenzCar extends Car {
    
        @Inject
        public BenzCar() {
        }
    }
    
    
    
    
    @Module
    public abstract class CarModule {
        
        @Binds
        abstract Car provideCar(BenzCar car);
    }
    
    
    
    @Component(modules = CarModule.class)
    public interface MainComponent {
        Car maker();
    }
    
    
    
    
    public class MainActivity extends AppCompatActivity {
        @Inject
        Car car;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            car=DaggerMainComponent.create().maker();
        }
    
        public void onClick(View view) {
            Toast.makeText(this,car.getClass().getSimpleName(),Toast.LENGTH_SHORT).show();
        }
    
    }
    

    结果为 :

    BenzCar

    通过 @Binds 当需要注入一个 Car 对象 可以使用子类对象 BenzCar 进行绑定注入

    6. @BindsInstance

    上述注入对象都是通过 Dagger 自动注入的,如果我们需要 new 一个对象传入 Dagger 就可以使用 @BindsInstance

    public class Car {
        private Engine mEngine;
        private Tire mTire;
        // 提供一个 Car 对象,但是需要 Engine Tire对象作为参数
        @Inject 
        public Car( Engine engine, Tire tire) {
            mEngine = engine;
            mTire = tire;
    
        }
        
    }
    
    
    public class Engine {
        private String model;
        private int age;
        public Engine(String model, int age) {
            this.model = model;
            this.age = age;
        }
    }
    
    public class Tire {
    
    
        private int radius;
        // Tire 不提供对象,手动注入
        public Tire(int radius) {
            this.radius = radius;
        }
    }
    
    
    @Module
    public class EngineModule {
    
        // 提供 Engine 对象
        @Provides
        public static Engine provideEngine()
        {
           return new Engine("F1234",2018);
        }
    }
    
    

    @Component.Builder 注明一个 MainComponent builder @BindsInstance 定义一个传入手动对象的方法

    
    @Component(modules = EngineModule.class)
    public interface MainComponent {
        Car maker();
    
        @Component.Builder
        interface Builder {
            @BindsInstance
            Builder tire(Tire tire);
            MainComponent build();
        }
    }
    
    
    
    

    要构造 Car 对象需要 Engine Tire对象 ,Engine对象框架自动生成, Tire对象由我们手动传入

    
    public class MainActivity extends AppCompatActivity {
        @Inject
        Car car;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 手动注入 Tire 对象
            car=DaggerMainComponent.builder().tire(new Tire(90)).build().maker();
        }
    
        public void onClick(View view) {
            Toast.makeText(this,car.toString(),Toast.LENGTH_SHORT).show();
        }
    
    }
    
    

    7. Lazy<T> 和 Provider<T>

    • Lazy<T>
    @Module
    public class CarModule {
        @Provides
        public static Car provideCar1()
        {
            return new Car(new Engine("F-7890",2020),new Tire());
        }
    
    }
    
    
    @Component(modules = CarModule.class)
    public interface MainComponent {
        Lazy<Car> maker();
    }
    
    
    public class MainActivity extends AppCompatActivity {
        private Car car1;
        private Car car2;
    
        @Inject
        Lazy<Car> mCarLazy;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mCarLazy=DaggerMainComponent.create().maker();
            car1=mCarLazy.get();
            car2=mCarLazy.get();
        }
    
        public void onClick(View view) {
            Toast.makeText(this," car1==car2 : "+(car1==car2),Toast.LENGTH_SHORT).show();
        }
    
    }
    

    car1 = car : true

    当注入 Lazy<Car>对象的时候 只有在调用 get 方法的时候才会去调用 provideCar1 初始化 Car 对象,实现懒加载 且会缓存 Car 对象 每次进行 get()的时候返回的是同一个 Car 对象

    • Provider<T>

    **把上述的代码的 Lazy<Car> 改为 Provider<Car>,其他不变 **

    car1 = car : false

    当注入 Provider<Car> 对象的时候 每次调用 get 方法的时候才会去调用 provideCar1去创建一次新的 Car 对象

    如果 Provider<Car> 注入过程中给 @Provides ,@Component 加上 @Singleton结果会如何呢 ?

    @Module
    public class CarModule {
        @Provides
        @Singleton
        public static Car provideCar1()
        {
            return new Car(new Engine("F-7890",2020),new Tire());
        }
    
    }
    
    @Component(modules = CarModule.class)
    @Singleton
    public interface MainComponent {
        Provider<Car> maker();
    }
    
    

    结果为 :

    car1 = car : true

    当注入 Provider<Car> 对象的时候 每次调用 get 方法的时候才会去调用 provideCar1去创建 Car 对象,但是 provideCar1已经被 @Singleton ,所以不管怎样,provideCar1只会返回相同的Car 对象

    相关文章

      网友评论

          本文标题:Dagger 2 使用详解-基础篇

          本文链接:https://www.haomeiwen.com/subject/vwnvdftx.html