Dagger2
Dagger2是一款基于 Java 注解来实现的完全在编译阶段完成依赖注入的开源库。Dagger2 应于 Java 和 Android 开发而不单单是 Android,主要用于模块间解耦、提高代码的健壮性和可维护性。
依赖引入
在app/build.gradle中的dependencies{ }加入以下代码同步即可:
implementation 'com.google.dagger:dagger:2.23.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.23.1'
三大组成部分
dagger2三个重要组成部分为:module、component,、container,三者的关系如下图所示:
由于Dagger2主要是编译期对注解内容进行获取,所以这三个组成部分也有相应的注解符号。
Container
@Inject : 用于标记需要注入的依赖。
Module
@Module : 用该注解修饰的类,里面主要提供该依赖对象的实例化方法,方法名一般为provide + 对象名。
@Provides : 用这个来修饰@Module注解类中的方法,表明要提供的实例化对象
Componet
@Componet : 可以理解为注入器,它会把目标类依赖的实例注入到目标类中。。
在构建好注解之后,项目需要rebuild一下,整个依赖注入的过程就算完成。
Dagger2基本使用
@Inject和@Component
1、首先编写Person类,使用@Inject注解在构造函数上,用于标记需要注入的依赖。如下所示:
public class Person {
private static final String TAG = "Person";
@Inject
public Person() {
}
public void eat(){
Log.e(TAG, "eat....");
}
}
2、接下来用@Component注解来完成依赖注入。我们需要定义一个接口,接口命名建议为:目标类+Component,在编译后Dagger2就会为我们生成名为Dagger+目标类名+Component的辅助类。具体代码如下所示:
@Component
public interface MainActivityComponent {
void inject(MainActivity activity);
}
Component则可以理解为注入器,它会把目标类依赖的实例注入到目标类中。在这里需要定义inject方法,传入需要注入依赖的目标类。
3、在MainActivity中使用@Inject构建Person对象
public class MainActivity extends AppCompatActivity {
@Inject
Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
person.eat();
}
}
@Module和@Provides
如果项目中使用了第三方的类库,可以采用@Module和@Provides来处理。我们创建GsonModule类,如下所示:
@Module
public class GsonModule {
@Provides
public Gson provideGson(){
return new Gson();
}
}
@Module标注在类上,用来告诉Component,可以从这个类中获取依赖对象,也就是Gson类。
@Provodes 标记在方法上,表示可以通过这个方法来获取依赖对象的实例。
@Module标注的类其实就是一个工厂,用来生成各种类;@Provodes标记的方法,就是用来生成这些类的实例的。接着来编写Component类,如下所示:
@Component(modules = GsonModule.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}
这和此前的区别就是加上了modules=GsonModule.class,用来指定Module。需要注意的是,Component 中可以指定多个Module。接下来在MainActivity中使用Gson,如下所示:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Inject
Gson gson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
User user = new User("Legend", 31);
String json = gson.toJson(user);
Log.e(TAG, json);
}
}
其中,User类就是一个包含name和age的JavaBean,非常简单。
还有一种情况,当我们使用依赖注入的时候,如果需要注入的对象是抽象的,则@Inject也无法使用,因为抽象的类并不能实例化。这时也可以采用@Module和@Provides。
1、定义一个Engine抽象类
public abstract class Engine {
public abstract void work();
}
2、接着定义它的实现类GasolineEngine:
public class GasolineEngine extends Engine{
@Override
public String work() {
return "汽油发动机发动";
}
}
3、随后在Car类中引用Engine,如下所示:
public class Car {
private Engine engine;
@Inject
public Car(Engine engine) {
this.engine = engine;
}
public String run(){
return engine.work();
}
}
4、创建EngineModule类,如下所示:
@Module
public class EngineModule {
@Provides
public Engine provideEngine(){
return new GasolineEngine();
}
}
5、接着在Component中指定EngineModule:
@Component(modules = EngineModule.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}
6、最后在MainActivity中使用,如下所示:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Inject
Car car;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
Log.e(TAG, car.run());
}
}
@Named和@Qualifier
@Qualifier 是限定符,@Named 则是@Qualifier 的一种实现。
当有两个相同的依赖时,它们都继承同一个父类或者均实现同一个接口。当它们被提供给高层时,Component 就不知
道我们到底要提供哪一个依赖 对象了,因为它找到了两个。
比如在上面的汽车例子中,如果我们再提供一个DieselEngine给它, EngineModule可以改写为如下代码所示:
@Module
public class EngineModule {
@Provides
@Named("Gasoline")
public Engine provideGasoline(){
return new GasolineEngine();
}
@Provides
@Named("Diesel")
public Engine provideDiesel(){
return new DieselEngine();
}
}
给不同的Provides定义不同的@Named,接下来在Car类中指定要采用哪种Provides:
public class Car {
private Engine engine;
@Inject
public Car(@Named("Diesel") Engine engine) {
this.engine = engine;
}
public String run(){
return engine.work();
}
}
上面的例子也可以用@Qualifier来 实现,@Named传递的值只能是字符串;而@Qualifier则更灵活一些,@Qualifier不是直接标记在属性上的,而是用来自定义注解的,如下所示:
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Gasoline {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Diesel {
}
分别自定义两个注解@Gasoline和@Diesel,接下来修改EngineModule类,如下所示:
@Module
public class EngineModule {
@Provides
@Gasoline
public Engine provideGasoline(){
return new GasolineEngine();
}
@Provides
@Diesel
public Engine provideDiesel(){
return new DieselEngine();
}
}
最后在Car类指定需要哪种依赖:
public class Car {
private Engine engine;
@Inject
public Car(@Gasoline Engine engine) {
this.engine = engine;
}
public String run(){
return engine.work();
}
}
@Singleton和@Scope
@Scope是用来自定义注解的,而@Singleton则是用来配合实现局部单例和全局单例的。
首先在 GsonModule 中添加@Singleton,如下所示:
@Module
public class GsonModule {
@Singleton
@Provides
public Gson provideGson(){
return new Gson();
}
}
接下来在MainActivityComponent中添加@Singleton:
@Singleton
@Component(modules = GsonModule.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}
我们在MainActivity中打印Gson的hashCode值,就会发现值是相同的,如下所示:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Inject
Gson gson;
@Inject
Gson gson1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
Log.e(TAG, gson.hashCode() + "--------" + gson1.hashCode());
}
}
Gson 在 MainActivity 中是单例,如果在其他Activity中则不再是单例的形式。(局部单例)
如果想要实现全局单例,就需要保证对应的Component只有一个实例。可以用@Singleton结合Application来实现:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
接下来在GsonModule中使用@ApplicationScope:
@Module
public class GsonModule {
@ApplicationScope
@Provides
public Gson provideGson(){
return new Gson();
}
}
为了处理多个Activity,我们创建ActivityComponent,并使用@ApplicationScope,如下所示:
@ApplicationScope
@Component(modules = GsonModule.class)
public interface ActivityComponent {
void inject(MainActivity activity);
void inject(SecondActivity activity);
}
创建App类继承自Application,用来提供ActivityComponent实例,如下所示:
public class App extends Application {
ActivityComponent activityComponent;
@Override
public void onCreate() {
super.onCreate();
activityComponent = DaggerActivityComponent.builder().build();
}
public static App get(Context context){
return (App) context.getApplicationContext();
}
public ActivityComponent getActivityComponent() {
return activityComponent;
}
}
最后在MainActivity中实现如下代码:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Inject
Gson gson;
@Inject
Gson gson1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
App.get(this).getActivityComponent().inject(this);
Button btnJump = findViewById(R.id.btn_jump);
btnJump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
}
}
SecondActivity中的代码也是类似的,运行程序,发现MainActivity和SecondActivity的Gson的内存地址是一样的。
@Component的dependencies
@Component也可以用dependencies依赖于其他Component。我们重新创建一个Swordsman类:
public class Swordsman {
@Inject
public Swordsman() {
}
public String fighting(){
return "真是麻烦呀!";
}
}
接下来,按照惯例创建SwordsmanModule和SwordsmanComponent,如下所示:
@Module
public class SwordsmanModule {
@Provides
public Swordsman providerSwordsman(){
return new Swordsman();
}
}
@Component(modules = SwordsmanModule.class)
public interface SwordsmanComponent {
Swordsman getSwordsman();
}
随后在此前定义的App中引入SwordsmanComponent,如下所示:
public class App extends Application {
ActivityComponent activityComponent;
@Override
public void onCreate() {
super.onCreate();
activityComponent = DaggerActivityComponent.builder().build();
DaggerSwordsmanComponent.builder().build();
}
public static App get(Context context){
return (App) context.getApplicationContext();
}
public ActivityComponent getActivityComponent() {
return activityComponent;
}
}
最后我们在SecondActivity中使用Swordsman,如下所示:
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "SecondActivity";
@Inject
Swordsman swordsman;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
App.get(this).getActivityComponent().inject(this);
String content = swordsman.fighting();
Log.e(TAG, "swordsman:" + content);
}
}
注意:自定义的全局Application需要在清单文件中定义:
android:name=".App"
懒加载
Dagger2提供了懒加载模式,在@Inject的时候不初始化,而是使用的时候,调用get方法来获取实例。 接着我们改写上面提到的Swordsman,将它改写为懒加载模式。
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "SecondActivity";
@Inject
Lazy<Swordsman> swordsmanLazy;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
App.get(this).getActivityComponent().inject(this);
Swordsman swordsman = swordsmanLazy.get();
String content = swordsman.fighting();
Log.e(TAG, "swordsman:" + content);
}
}