本文为《Android Architecture Components学习笔记》的一部分
文档代码为Kotlin,但是系统生成的代码仍然为Java
本人水平有限,如有不当之处请不吝赐教
Dagger初接触
Dagger并不是AAC的一部分,但是在项目中却是个狠角色。Dagger的应用对项目组件解耦以及单元测试还是会带来许多益处。所以,我觉得有必要好好研究一下。
刚开始接触Dagger2时,感觉就像简称D(dan)T(teng)。随着谷歌接手维护以来这种情况得到了一些改善,同时谷歌也赋予了更多的功能。
需要说明的是这里的Dagger-Android与Dagger2并不是一回事。
通过一个非常简单的栗子,来说明Dagger最基础的用法。
我有一个Person
:
class Person {
var name: String
var age: Int
constructor() {
this.name = "超越"
this.age = 28
}
override fun toString(): String {
return "Person(name='$name', age=$age)"
}
}
普通的用法MainActivity
:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
……
var person=Person()
……
}
}
我们来看看用Dagger是怎么实现的:
- 第一步、将被注入类的构造器加
@Inject
注解
class Person {
var name: String
var age: Int
@Inject//构造函数加上这个注解
constructor() {
this.name = "超越"
this.age = 28
}
override fun toString(): String {
return "Person(name='$name', age=$age)"
}
}
- 第二步、需要一个组件接口,建一个
PersonComponent
@Component//必须用@Component注解
interface PersonComponent {
//接口、函数名可以自己起,参数必须是注入目标类
fun injectMain(mainActivity: MainActivity):Unit
第三步、Build一下,然后MainActivity
里:
class MainActivity() : AppCompatActivity(){
@Inject//这样写代表注入了变量
lateinit var person:Person
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
//前两步完成Build一下就会有DaggerPersonComponent
DaggerPersonComponent.create().injectMain(this)
Log.v("111", person.toString())
}
}
OK,这是Dagger最简单的用法。
简单原理
用了三步即可完成Dagger注入,下面就简单看看如何实现的。
被注入类
在被注入类(这里指的是Person
)的构造函数前面加上@Inject
注解并Build项目后,会自动生成一个***_Factory的类,看看下面的代码:
public final class Person_Factory implements Factory<Person> {
private static final Person_Factory INSTANCE = new Person_Factory();
@Override
public Person get() {return provideInstance(); }
public static Person provideInstance() { return new Person(); }
public static Person_Factory create() { return INSTANCE; }
public static Person newPerson() {return new Person(); }
}
这个自动生成的类用来返回一个Person对象
如果不熟悉工厂模式,可以看一下Factory
与Provider
。
注入目标类
我们是要将Person
注入到MainActivity
。
所以在MainActivity
里person
变量加上@Inject
。
Build后会生成MainActivity_MembersInjector
类:
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Person> personProvider;
public MainActivity_MembersInjector(Provider<Person> personProvider) {
this.personProvider = personProvider;
}
public static MembersInjector<MainActivity> create(Provider<Person> personProvider) {
return new MainActivity_MembersInjector(personProvider);
}
//后面两个方法完成了注入
@Override
public void injectMembers(MainActivity instance) {
injectPerson(instance, personProvider.get());
}
public static void injectPerson(MainActivity instance, Person person) {
instance.person = person;
}
}
组件接口
注入类与目标类都生成了相应的代码,那么他们是怎样联系在一起的呢。
写一个用@Component标注的接口PersonComponent
Build后会生成DaggerMainActivityComponent
类:
public final class DaggerPersonComponent implements PersonComponent {
private DaggerPersonComponent(Builder builder) {}
public static Builder builder() {return new Builder(); }
//在MainActivity里我们先调用了这个方法
public static PersonComponent create() {return new Builder().build();}
//在MainActivity里调用create()后直接调用这个方法
@Override
public void injectMain(MainActivity mainActivity) {injectMainActivity(mainActivity); }
private MainActivity injectMainActivity(MainActivity instance) {
//我用版本比较新,这个场景下,没用到被注入类的_Factory。
//之前java下是要使用Person_Factory.create()的,而且代码冗余。
MainActivity_MembersInjector.injectPerson(instance, new Person());
return instance;
}
public static final class Builder {
private Builder() {}
public PersonComponent build() {return new DaggerPersonComponent(this);}
}
}
Component起到了组装的作用。
简单用Module
如果被注入类是别人的,比如网络请求、图片处理或其他等等
是没办法在构造器上加@Inject,就需要使用Module了
改一下Person:
data class Person(var name: String, var age: Int) {
override fun toString(): String {
return "Person(name='$name', age=$age)"
}
}
前面Person类延用了Java的风格,这里用Kotlin支持的data class简单多了。
- 首先,建一个用
@Module
注解的Module类
@Module//必须用这个注解
class PersonModule {
@Provides//也是必须的
fun person(): Person {
return Person("高歌", 28)
}
}
- 然后,在Component上引用
//新版的modules=后边是个array
@Component(modules = [PersonModule::class])
interface PersonComponent {
fun injectMain(mainActivity: MainActivity):Unit
}
然后Build即可。
完成后Person_Factory
就没有了,会生成PersonModule_PersonFactory
。
public final class PersonModule_PersonFactory implements Factory<Person> {
private final PersonModule module;
public PersonModule_PersonFactory(PersonModule module) {this.module = module;}
@Override
public Person get() { return provideInstance(module); }
public static Person provideInstance(PersonModule module) { return proxyPerson(module);}
public static PersonModule_PersonFactory create(PersonModule module) {
return new PersonModule_PersonFactory(module);
}
public static Person proxyPerson(PersonModule instance) {
return Preconditions.checkNotNull( instance.person(), "Cannot ……"); }
}
在DaggerMainActivityComponent
里,会加上PersonModule_PersonFactory
:
public final class DaggerPersonComponent implements PersonComponent {
private PersonModule personModule;
private DaggerPersonComponent(Builder builder) {initialize(builder);}
public static Builder builder() {return new Builder();}
public static PersonComponent create() {return new Builder().build(); }
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) { this.personModule = builder.personModule; }
@Override
public void injectMain(MainActivity mainActivity) {injectMainActivity(mainActivity);}
private MainActivity injectMainActivity(MainActivity instance) {
//这里变了,之前就是简单的new Person()
MainActivity_MembersInjector.injectPerson(
instance, PersonModule_PersonFactory.proxyPerson(personModule));
return instance;
}
public static final class Builder {
private PersonModule personModule;
private Builder() {}
public PersonComponent build() {
if (personModule == null) {this.personModule = new PersonModule();}
return new DaggerPersonComponent(this);
}
public Builder personModule(PersonModule personModule) {
this.personModule = Preconditions.checkNotNull(personModule);
return this;
}
}
}
MainActivity_MembersInjector
还是那个样。
总结一下
先到这里吧,这篇主要是简单说明一下Dagger的基本注入原理。
使用上也不难:
- 分别在注入与被注入的类里使用
@Inject
注解 - 需要注入的类初始化配置,就放在Module里
- 做一个用
@Component
注解的Component - 最后就可以使用系统生成的
Dagger****
注入了
几个需要注意的事项:
- 使用
@Inject
注解的成员不能是private的 - 组件接口名最好以Component结尾
- Component里注入接口参数不能是注入类型的父类或子类