在讲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:
其实对于这种生成数据肯定在实际业务逻辑中不行,比如说我们的数据来自于网络层,数据肯定得从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,通过构造器将参数传进来。那调用的地方是啥样的:
上面多了一个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;
}
}
看到打印的数据没,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注解
其实从字面意思就大致明白是什么作用了,下面用一个事例来说明下该注解的意思:
这个是A类上加了singleton注解,还有在相应的Component接口类也需要加上该注解才能编译成功:
在MainActivity中可以验证这一注解:
这里改变了b中的name字段,那么可以看下两个name的变化:
所以从图上看也确实验证了这一结论。其实这里的单例是跟Component接口一起走的,因为Singleton注解的单例范围就是在某一个Component接口内的,跨Component是没有单例的说法。
说到这里其实很想知道大部分项目中用到了自定义的属性,比如说:
这里定义的注解和singleton注解是一样的,因此可以说明它的作用和singleton是一样的,为了保证在Component作用域内该对象一直处于单例的状态。
更多内容点击
关于mvp的例子大家可以阅读这里