Dagger2 中最常用的是 @Inject,它来自 javax.inject 包,是依赖注入标准框架(JSR330)的成员。@Inject 一般用来注解字段,表示自动将依赖注入其中,也用来注解构造函数,表示自动创建其实例。
API 描述:
Identifies injectable constructors, methods, and fields. May apply to static as well as instance members. An injectable member may have any access modifier (
private
,package-private
,protected
,public
). Constructors are injected first, followed by fields, and then methods. Fields and methods in superclasses are injected before those in subclasses. Ordering of injection among fields and among methods in the same class is not specified.Injectable constructors are annotated with
@Inject
and accept zero or more dependencies as arguments.@Inject
can apply to at most one constructor per class.
简单翻译:
标识可注入的构造函数、方法和字段。可以应用于静态成员,也可以应用于实例成员。一个可注入成员可以有任意的访问修饰符(
private
,package-private
,protected
,public
)。首先注入构造函数,接着是字段,然后是方法。父类中的字段和方法是在子类之前注入的。同一个类中的字段之间以及方法之间的注入顺序没有规定。可注入构造函数使用
@Inject
来注解,并接受零个或多个依赖项作为参数。@Inject
只能应用于每个类中最多一个构造函数上。
注意,由于 Dagger2 不支持反射,因此可注入成员的访问修饰符,并不包括 private
级别。
1.1 入门实战
假设我们需要开发一个主页,简单起见,它仅展示账户中的用户名和密码。
1.1.1 账户
创建 Account
类,使用 @Inject
注解在构造函数上,用来创建实例:
public class Account {
private String username;
private String password;
@Inject
public Account() {
this.username = "fssd";
this.password = "123456";
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@NonNull
@Override
public String toString() {
return "Account{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
提示:使用 Alt + Insert 快捷键,可以一键生成 toString()
等模板方法。
使用 Ctrl + F9 快捷键进行编译:
编译生成的 Account_Factory
类:
public final class Account_Factory implements Factory {
@Override
public Account get() {
return newInstance();
}
public static Account_Factory create() {
return InstanceHolder.INSTANCE;
}
public static Account newInstance() {
return new Account();
}
private static final class InstanceHolder {
private static final Account_Factory INSTANCE = new Account_Factory();
}
}
可以看到,它实现 Factory
接口,get
方法返回的是 new
出来的 Account
实例。
1.1.2 主页
创建 activity_main.xml
布局,可以显示文字:
创建 MainActivity
活动,使用 @Inject
注解在字段上,用来注入依赖:
public class MainActivity extends AppCompatActivity {
@Inject
Account account;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView contentText = findViewById(R.id.content_text);
contentText.setText(account.toString());
}
}
将 MainActivity
活动在 AndroidManifest.xml
中进行注册:
编译生成的 MainActivity_MembersInjector
类,关键内容如下:
public final class MainActivity_MembersInjector implements MembersInjector {
// ...
@Override
public void injectMembers(MainActivity instance) {
injectAccount(instance, accountProvider.get());
}
@InjectedFieldSignature("com.github.mrzhqiang.dagger2_example.MainActivity.account")
public static void injectAccount(MainActivity instance, Account account) {
instance.account = account;
}
}
可以看到,它实现 MembersInjector
接口,injectMembers
方法是直接给 MainActivity
活动实例的 account
字段赋值,和正常开发时一样的赋值操作,没有用到反射。
1.2 运行
启动程序,发现闪退,查看日志:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.github.mrzhqiang.dagger2_example.account.Account.toString()' on a null object reference
因为在空对象引用上调用 Account.toString()
方法导致空指针异常,说明依赖没有被注入。
1.3 总结
- 注解在构造函数上,编译生成
Factory
接口的实现,每次获取都是new
出来的实例 - 注解在字段上,编译生成
MembersInjector
接口的实现,直接给目标字段赋值 - 仅使用
@Inject
,无法完成依赖注入,运行会抛出NullPointerException
异常
下一章,讨论 Dagger2 的 @Component
,它可以构建依赖图谱,完成依赖注入。