最近一直在研究MVVM框架的具体实现,在这其中就遇到一个小问题,就是在ViewModel中使用@Inject注解注入和使用构造函数注入是有细小差别的,也给不知道的童鞋标个重点,以后遇到了也能够迅速想到我的这个文章。
Dagger注入的两种方式
这里简单的介绍下注入的两种方式。
成员变量注入
所谓的成员变量注入其实就是在你想注入的成员变量上使用@Inject注解。那么当你通过Dagger去提供当前对象的时候,会去找到当前类的成员变量注入器,类似于这种XXX_MembersInjector。然后调用这个注入器的对应注入的方法,就实现了成员变量方式的注入。举个例子:
public class MainViewModel extends ViewModel {
@Inject
String title;
@Inject
public MainViewModel() {
Log.d("TAG", "MainViewModel: " + title);
}
}
这种就是成员变量注入。
构造函数注入
所谓的构造函数注入就是通过构造函数去将需要的数据注入到当前的类中,似乎也不需要够多的解释,也举个例子,用于之后的对比。
public class FragmentViewModle extends ViewModel {
String title;
@Inject
FragmentViewModle(String title) {
Log.d("TAG", "FragmentViewModle: " + title);
}
}
这种的就是构造函数注入,对比一下上面的成员变量注入,有没有发现区别?
思
考
五
秒
区别就是少写了个@Inject ~哈 ~哈 ~哈 ~哈 ,当然不是,下面进入正题。
两种注入方式的细小差别
这两种注入方式其实在平时的使用过程中是看不出有什么不一样的,反正之前我也没有注意过。今天就莫名奇妙的遇到注入对象总为NULL的情况。我个人习惯一直是写成员变量方式注入的,然后发现在构造函数中用不了,之前也一直没注意过这个问题然后就研究了一下具体的是因为什么,发现两种方式还是有小差别的。
还是上面的例子,两个ViewModel的构造函数中,通过成员变量注入的是无法获取到当前值的,通过构造函数注入的是可以的。这个是结论。下面说为什么。
查看Dagger编译过的源码,我可以看到这几个类
其中不是重点的我已经给划掉了,可以通过命名就知道是具体干嘛的,分别是两个Model的工厂类和成员变量注入类。
下面看一下具体实现:
MainViewModle
public final class MainViewModel_Factory implements Factory<MainViewModel> {
private final Provider<String> titleProvider;
public MainViewModel_Factory(Provider<String> titleProvider) {
this.titleProvider = titleProvider;
}
@Override
public MainViewModel get() {
//获得一个MainViewModel 实例
MainViewModel instance = new MainViewModel();
//使用对应的成员变量注入器为其注入成员变量
MainViewModel_MembersInjector.injectTitle(instance, titleProvider.get());
return instance;
}
public static Factory<MainViewModel> create(Provider<String> titleProvider) {
return new MainViewModel_Factory(titleProvider);
}
public static MainViewModel newMainViewModel() {
return new MainViewModel();
}
}
重点已经用注释标上了,会看到是先创建实例,然后通过成员变量注入器在注入需要的数据的。这同时也说明,使用成员变量的方式进行注入的时候,在当前类的构造方法中是无法使用注入的数据的。
FragmentViewModel
public final class FragmentViewModle_Factory implements Factory<FragmentViewModle> {
private final Provider<String> titleProvider;
public FragmentViewModle_Factory(Provider<String> titleProvider) {
this.titleProvider = titleProvider;
}
@Override
public FragmentViewModle get() {
//在获取实例的同时将需要的数据通过构造函数传递
return new FragmentViewModle(titleProvider.get());
}
public static Factory<FragmentViewModle> create(Provider<String> titleProvider) {
return new FragmentViewModle_Factory(titleProvider);
}
public static FragmentViewModle newFragmentViewModle(String title) {
return new FragmentViewModle(title);
}
}
这里就是直接通过构造函数将数据传递到当前的类中,所以在构造函数中是可以使用的。
总结
通过一个工作中的小问题发现,使用两种注入方式的区别。不过总体上推荐还是使用成员变量注入的方式,要不使用构造函数的方式,你还是得把传入的数据变成成员变量。不过如果想要在构造函数中使用的话,那么就得使用构造函数注入的方式。所以总体上来说的话。
如果想在构造函数中使用需要的数据,那就使用构造函数注入的方式,其他的就全使用成员变量的方式就可以了。
小思考:如果使用构造函数注入,那么成员变量上同时使用@Inject标记的话会怎么样?也就是说两种方式同时使用会怎么样?