最近几天在看一些新的Android技术,突然看到Dagger2这个,去github上面看了下1W+的star了,赶紧看下吧
1.简介
Dagger-匕首,鼎鼎大名的Square公司旗下又一把利刃(没错!还有一把黄油刀,唤作ButterKnife)。
Dagger2起源于Dagger,是一款基于Java注解来实现的完全在编译阶段完成依赖注入的开源库,主要用于模块间解耦、提高代码的健壮性和可维护性。Dagger2在编译阶段通过apt利用Java注解自动生成Java代码,然后结合手写的代码来自动帮我们完成依赖注入的工作。
起初Square公司受到Guice的启发而开发了Dagger,但是Dagger这种半静态半运行时的框架还是有些性能问题(虽说依赖注入是完全静态的,但是其有向无环图(Directed Acyclic Graph)还是基于反射来生成的,这无论在大型的服务端应用还是在Android应用上都不是最优方案)。因此Google工程师Fork了Dagger项目,对它进行了改造。于是变演变出了今天我们要讨论的Dagger2,所以说Dagger2其实就是高配版的Dagger。(转自https://github.com/BaronZ88/Blog/blob/master/开源框架解析/Dagger2)
dagger2是一种依赖注入的编译时框架
依赖注入理解
在一般的java程序中某个对象依赖其他对象可以通过成员变量直接引用,这种情况一般是主动传入的比如常见的setData这种方法,而依赖注入则是说,不需要用户手动设置,程序在编译器期通过注解处理器,自动给相关对象添加依赖关系。
适用对象
Java 和Android (A fast dependency injector for Android and Java)
2.导入使用
Android studio3.0导入dagger2
compile 'com.google.dagger:dagger:2.7'
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
dagger2的库可以直接在Androidstudio里面看到,dagger-compiler的库可以在下面的路径中找到
Mac系统默认下载到:/Users/(用户名)/.gradle/caches/modules-2/files-2.1
Windows系统默认下载到:C:\Users\(用户名)\.gradle\caches\modules-2\files-2.1
3.基本语法介绍
Dagger2是怎么通过注解来注入的呢?
首先想下平常写注解就是@xxxx这样就好了,那么Dagger2用的都是那些注解:
@Inject:@Inject有两个作用,一是用来标记需要注入的变量,以此告诉Dagger2为它提供注入;第二个作用是标记构造函数,当构造函数被@Inject标记,dagger2为@Inject标记的变量创建实例的时候就是通过这个注解找到合适的构造函数的
@Module:@Module用于封装提供依赖的类。为什么需要专门提供这个注解,因为在很多情况下,我们需要注入的类是第三方类库,我们无法再构造函数直接加@Inject注解,还有一个问题就是如果有多个构造函数的问题,Module封装依赖就是为了解决这些问题。
@Provides:@Provides和Module配合使用,用来标注方法,用@Provide表明这个方法是对外提供注入对象的,提供的对象就是方法的返回类型。
@Component:@Component用于标注接口或者抽象类,是依赖需求方和依赖提供方之间的桥梁。被Component标注的接口在编译时会生成该接口的实现类带有Dagger前缀
4.实战练习
public class Book {
@Inject
public Book() {
}
}
自己写的一个对象所以可以直接在构造函数上加@Inject注解表明这个构造函数可以在需要的时候被使用生成实例
之前说过当需要注入依赖某个对象的时候直接在声明上面直接加@Inject就行,因此只需要声明实例的时候带上就好了
最后想怎么把这两个类关联起来呢,这个就是@Component
@Component
public interface BookComponent {
void inject(MainActivity activity);
}
写完这个类以后build一下会发现生成了DaggerBookComponent
最后看下完整的调用是什么
public class MainActivity extends AppCompatActivity {
@Inject
Book mBook;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerBookComponent.builder()
.build()
.inject(this);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("wyd", "mBook: " + mBook.toString());
}
});
}
}
这时候可以运行一下可以看到这个实例已经可以正常使用了,这个时候可以看下DaggerBookComponent
生成的,打开这个文件可以看到
public final class DaggerBookComponent implements BookComponent {
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DaggerBookComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static BookComponent create() {
return builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(Book_Factory.create());
}
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}
public static final class Builder {
private Builder() {}
public BookComponent build() {
return new DaggerBookComponent(this);
}
}
}
public enum Book_Factory implements Factory<Book> {
INSTANCE;
@Override
public Book get() {
return new Book();
}
public static Factory<Book> create() {
return INSTANCE;
}
}
Book_Factory这个类也是通过注解来生成的很简单,注意它是一个枚举类型,所以直接返回INSTANCE,通过get方法返回Book对象,因为这里的自动生成都涉及到dagger-complier库,这个库看起来实现比较麻烦,里面涉及到好多java解析的,暂时不深究,后边有时间可以找个重点的了解下
这里可以看到通过Builder生成DaggerBookComponent实例最终调用initialize生成mainActivityMembersInjector
这个变量顾名思义就是Mainactivity的成员注入器,MainAcitivity需要的注入都是由它提供,至于这个类的生成可以简单想下就是Dagger的注解处理器通过解析@inject标签,来获得它需要注入的类,然后合起来生成MainActivity_MembersInjector
最终我们调用inject方法就直接调用 mainActivityMembersInjector.injectMembers(activity);看下这个方法
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Book> mBookProvider;
public MainActivity_MembersInjector(Provider<Book> mBookProvider) {
assert mBookProvider != null;
this.mBookProvider = mBookProvider;
}
public static MembersInjector<MainActivity> create(Provider<Book> mBookProvider) {
return new MainActivity_MembersInjector(mBookProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mBook = mBookProvider.get();
}
public static void injectMBook(MainActivity instance, Provider<Book> mBookProvider) {
instance.mBook = mBookProvider.get();
}
}
这里就是完成了赋值instance.mBook = mBookProvider.get();
因此我们这里就可以直接使用注入的类了这里有个问题如果我们要注入多个类呢,加一个Phone的类,使用方法和Book一样,看下Dagger重新生成的这个类
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Book> mBookProvider;
private final Provider<Phone> mPhoneProvider;
public MainActivity_MembersInjector(
Provider<Book> mBookProvider, Provider<Phone> mPhoneProvider) {
assert mBookProvider != null;
this.mBookProvider = mBookProvider;
assert mPhoneProvider != null;
this.mPhoneProvider = mPhoneProvider;
}
public static MembersInjector<MainActivity> create(
Provider<Book> mBookProvider, Provider<Phone> mPhoneProvider) {
return new MainActivity_MembersInjector(mBookProvider, mPhoneProvider);
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mBook = mBookProvider.get();
instance.mPhone = mPhoneProvider.get();
}
public static void injectMBook(MainActivity instance, Provider<Book> mBookProvider) {
instance.mBook = mBookProvider.get();
}
public static void injectMPhone(MainActivity instance, Provider<Phone> mPhoneProvider) {
instance.mPhone = mPhoneProvider.get();
}
}
大家可以看到这里创建MainActivity_MembersInjector 传入了两个provider,赋值的时候也调用了各自的provider赋值
还有个问题如果我们使用的对象是个有参数的对象该怎么调用呢?
直接声明一个试下:
Error:(11, 12) 错误: Types may only contain one @Inject constructor.
直接报错了,这里我们就需要使用Module了,后边有时间继续