Dagger2整理一波

在讲Dagger2之前呢,我先列一个事例代码,看看用了Dagger2之后有什么不一样的地方。

public class A {
    private B b;
    private C c;

    public A() {
        b = new B();
        c = new C();
    }
}

public class B {
}

public class C {
}

该事例代码在A中引用了B和C,这个没什么,那如果当B和C业务复杂了呢,需要增加属性:

public class B {
    private String name;

    public B(String name) {
        this.name = name;
    }
}

public class C {
    private int age;

    public C(int age) {
        this.age = age;
    }
}

那是不是A里面的代码也跟着发生变化了呢,是的,传参变了,那调用方肯定也发生变化了。所以说这种业务逻辑的改变是说不准的。下面看看Dagger2是怎么写这种变动的逻辑:

public class B {
    @Inject
    public String name;

    @Inject
    public B() {
    }
}

public class C {
    @Inject
    public int age;

    @Inject
    public C() {
    }
}

public class A {
    @Inject
    public B b;
    @Inject
    public C c;

    @Inject
    public A() {
//        b = new B("张三");
//        c = new C(12);
    }
}

上面改动的代码中,A中依赖了B和C,B和C中的构造方法提供了@Inject注解来说明自己是被Dagger2来生成,同样A中也说明了依赖的B和C是被Dagger2来管理起来了。注意上面如果需要用到@Inject注解,属性是不能用private来修饰的。仅有上面的代码,是没法动态生成A的,此时需要借助@Component注解和@Module注解:

@Component(modules = AbcMondel.class)
public interface Abc {
    A getA();
}

@Module
public class AbcMondel {

    @Provides
    public String provideName() {
        return "张三";
    }

    @Provides
    public int provideAge() {
        return 10;
    }
}

其实这里可以看做是Component注解是主要组合@Module注解的,而@Module就是专门提供模型和数据的。当初始化B和C的时候,发现Module中有提供参数age和name,因此直接使用该处的内容。在调用方直接去生成A:

《Dagger2整理一波》 image.png

其实对于这种生成数据肯定在实际业务逻辑中不行,比如说我们的数据来自于网络层,数据肯定得从activity中传过来的呀,那此时怎么去传这个值呢:

@Module
public class AbcMondel {
    private String name;
    private int age;

    public AbcMondel(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Provides
    public String provideName() {
        return name;
    }

    @Provides
    public int provideAge() {
        return age;
    }
}

这里主要改动的地方AbcMondel,通过构造器将参数传进来。那调用的地方是啥样的:

《Dagger2整理一波》 image.png

上面多了一个abcMondel方法,该方法就是根据Module来生成的。
其实上面的B和C对象也可以直接放在model中通过Provides来生成,具体怎么玩看如下代码:

public class B {

    public String name;

    public B(String name) {
        this.name = name;
    }

   //无参的注入构造器,为了验证dagger2是以inject注入为主,还是    以Provides注解的注入方式为主
    @Inject
    public B() {
        this.name = "李四";
    }
}

public class C {
    public int age;

    @Inject
    public C() {
    }

    public C(int age) {
        this.age = age;
    }
}

public class Cc extends C {

    @Inject
    public Cc() {
        super(100);
    }

    public Cc(int age) {
        this.age = age;
    }
}

@Module
public class AbcMondel {
//    private String name;
//    private int age;

//    public AbcMondel(String name, int age) {
//        this.name = name;
//        this.age = age;
//    }
    
    //Provides直接提供的是B的有参构造
    @Provides
    public B provideB() {
        return new B("xc");
    }
}

@Module
public class AbcMondel {
//    private String name;
//    private int age;

//    public AbcMondel(String name, int age) {
//        this.name = name;
//        this.age = age;
//    }

    @Provides
    public B provideB() {
        return new B("xc");
    }

    @Provides
    public C provideC(Cc c) {
        return c;
    }
}

《Dagger2整理一波》 image.png

看到打印的数据没,name是通过Provides注解传递进去的,age是通过Cc类里面获取到的,因此这里可以得到两个结论,
如果inject和Provides同时提供了依赖,那此时优先去看Provides有没有提供相应的依赖,没有的话才去找inject相应的构造器;再个就是看到C提供的依赖没,他是通过传参的形式生成依赖的,所以从B和C生成依赖的方式也大概知道什么时候通过传参的形式,什么时候通过new的形式

new的形式呢,是通过手动去new,说明该类的构造器没有被依赖,直接通过形参的形式呢,说明该类已经在构造器上标注了@inject注解,因此像我们自己写的类就通过传参的形式,因为自己写的一般构造器上都有inject注解,像第三方的sdk没有办法改动源码,因此只能通过new的形式生成注解

Component通过传参的形式实现注入

这里用android中常用的mvp框架来做例子:
首先写好p层:

public class MainPresenter {
    private static final String TAG = MainPresenter.class.getSimpleName();
    MainActivityView.Iview mainActivityView;

    @Inject
    public MainPresenter(MainActivityView.Iview mainActivityView) {
        this.mainActivityView = mainActivityView;
    }

    public void loadPage() {
        mainActivityView.loadStart();
        Log.d(TAG, "loadPage");
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mainActivityView.loadEnd();
            }
        }, 3000);

    }
}

p层有MainActivityView.Iview引用,实际上就是MainActivity的实例,那此时肯定要在Module中提供该形参的实例:

@Module
public class PresenterModel {
    private final MainActivityView.Iview mView;

    public PresenterModel(MainActivityView.Iview view) {
        mView = view;
    }

    @Provides
    MainActivityView.Iview provideMainView() {
        return mView;
    }
}

前面已经说过,如果该类是系统定义的类,那么此时是可以通过new的方式提供给注入的参数,如果不是系统的可以通过在该类的构造器上面加上@inject注解。显然这个MainActivityView.Iview是activity实例,因此需要我们new或者传参进来,很显然MainActivity实例是不允许直接new的,因此需要传参进来。
Component部分代码:

@Component(modules = PresenterModel.class)
public interface PresenterComponent {
    void inject(MainActivity mainActivity);

//    @Component.Builder
//    interface Builder {
//        PresenterComponent.Builder view(MainActivityView.Iview view);
//
//        PresenterComponent build();
//    }
}

activity中的代码:

DaggerPresenterComponent.builder().presenterModel(new PresenterModel(this)).build().inject(this);

主要就这么一句,相信大家对mvp简单的架子搭建肯定是没问题了。
对于该框架大家需要多用,自然就比较顺手用了。

Singleton注解

其实从字面意思就大致明白是什么作用了,下面用一个事例来说明下该注解的意思:

《Dagger2整理一波》 image.png

这个是A类上加了singleton注解,还有在相应的Component接口类也需要加上该注解才能编译成功:

《Dagger2整理一波》 image.png

在MainActivity中可以验证这一注解:

《Dagger2整理一波》 image.png

这里改变了b中的name字段,那么可以看下两个name的变化:

《Dagger2整理一波》 image.png

所以从图上看也确实验证了这一结论。其实这里的单例是跟Component接口一起走的,因为Singleton注解的单例范围就是在某一个Component接口内的,跨Component是没有单例的说法。

说到这里其实很想知道大部分项目中用到了自定义的属性,比如说:

《Dagger2整理一波》 image.png

这里定义的注解和singleton注解是一样的,因此可以说明它的作用和singleton是一样的,为了保证在Component作用域内该对象一直处于单例的状态。

更多内容点击

关于mvp的例子大家可以阅读这里

    原文作者:的一幕
    原文地址: https://www.jianshu.com/p/4365dcd909ee
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞