本片文章将介绍Dagger2的作用及使用方式。
什么是Dagger2?
- Dagger2是在编译时期生成代码实现完整依赖注入的框架
Dagger 2 is a compile-time evolution approach to dependency injection. Taking the approach started in Dagger 1.x to its ultimate conclusion, Dagger 2.x eliminates all reflection, and improves code clarity by removing the traditional ObjectGraph/Injector in favor of user-specified @Component interfaces.
什么是依赖注入?
- 依赖注入作用
-
是实现控制反转(Inversion of Control)的最常见的方式之一
控制反转 是一种面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度。其基本思想是:借助于“第三方”实现具有依赖关系的对象之间的解耦。
简单说就是如果对象A依赖于对象B,那么对象A自己需要主动去创建对象B,无论是创建还是使用对象B,控制权都在自己手上。而控制反转的思想是对象A与对象B之间失去直接联系,当对象A需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
-
目的在于解耦
- 常见耦合场景:一个类中直接创建另一个类的对象的代码,这样的操作和硬编码一样,会导致耦合,我们把这种初始化方式称为硬初始化(hard init)。
- 硬初始化的坏处:
- 修改构造函数实现时,需要修改创建处的代码
- 不便于测试,直接创建另一个类对象的类无法单独被测试,其行为和另一个类紧紧耦合在一起,同时,也会导致代码的可读性问题。
-
- 依赖注入常见3种方式:
- 构造函数注入
public class MovieLister { private MovieFinder finder; public MovieLister(MovieFinder finder) { this.finder = finder; } ... }
- setter注入
public class MovieLister { ... public void setFinder(MovieFinder finder) { this.finder = finder; } }
- 接口注入
public interface InjectFinder { void injectFinder(MovieFinder finder); } class MovieLister implements InjectFinder { ... public void injectFinder(MovieFinder finder) { this.finder = finder; } ... }
- 构造函数注入
使用Dragger2进行依赖注入
- 常见的硬初始化代码
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UserModel user = new UserModel();
((TextView) findViewById(R.id.user_desc_line)).setText(user.id + "\n" + user.name + "\n" + user.gender);
}
...
}
一但UserModel的构造发生改变那么我们要修改所有实例化UserModel的代码。
- 使用Dagger2实现方式
- 目标:对于依赖类的修改不影响使用该类实例的类代码
- 步骤:
-
构建依赖
- 将创建对象的代码隔离起来,保证使用的类不受其后期更改影响
- 负责提供依赖的组件被称为Module
@Module public class ActivityModule { @Provides UserModel provideUserModel() { return new UserModel(); } }
- 使用@Module标识该类为Module,并用@Provides标识提供依赖的方法。
-
构建Injector
- 将依赖注入到需要的对象中
- 连接提供依赖和消费依赖对象的组件被称为Injector。Dagger2中,我们将其称为component。
@Component(modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity activity); }
- Component是一个使用@Component标识的Java 接口。接口的inject方法入参为需要该依赖的类型。
- 注意:这里必须是真正消耗依赖的类型MainActivity,而不可以写成其父类,比如Activity。因为Dagger2在编译时生成依赖注入的代码,会到inject方法的参数类型中寻找可以注入的对象,但是实际上这些对象存在于MainActivity,而不是Activity中。所以如果函数声明参数为Activity,Dagger2在Activity中找不到要注入的对象,导致所有注入都失败。
-
完成依赖注入:在需要依赖的地方创建Injector,完成注入
public class MainActivity extends ActionBarActivity { private ActivityComponent mActivityComponent; @Inject UserModel userModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mActivityComponent = DaggerActivityComponent.builder().activityModule(new ActivityModule()).build(); mActivityComponent.inject(this); ((TextView) findViewById(R.id.user_desc_line)).setText(userModel.id + "\n" + userModel.name + "\n" + userModel.gender); } ... }
- 使用@Inject标志被注入的对象userModel(注意userModel不能为private)
- 通过Dagger2生成的实现ActivityComponent接口类DaggerActivityComponent创建component,调用其inject方法完成注入。
-
Dagger2通过将创建依赖类对象的代码独立起来,保证使用的类不受依赖类的构造改变而受到影响,同时通过注解在编译时动态生成代码注入到被依赖类中,实现依赖类和被依赖类解耦。
参考文献: