40、Android--Dagger2

Dagger2

Dagger2是一款基于 Java 注解来实现的彻底在编译阶段完成依赖注入的开源库。Dagger2 应于 Java 和 Android 开发而不仅仅是 Android,主要用于模块间解耦、提升代码的健壮性和可维护性。java

依赖引入

在app/build.gradle中的dependencies{ }加入如下代码同步便可:android

implementation 'com.google.dagger:dagger:2.23.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.23.1'

三大组成部分

dagger2三个重要组成部分为:module、component,、container,三者的关系以下图所示:json

因为Dagger2主要是编译期对注解内容进行获取,因此这三个组成部分也有相应的注解符号。app

Containeride

@Inject : 用于标记须要注入的依赖。函数

Modulegradle

@Module : 用该注解修饰的类,里面主要提供该依赖对象的实例化方法,方法名通常为provide + 对象名。ui

@Provides : 用这个来修饰@Module注解类中的方法,代表要提供的实例化对象this

Componetgoogle

@Componet : 能够理解为注入器,它会把目标类依赖的实例注入到目标类中。。

在构建好注解以后,项目须要rebuild一下,整个依赖注入的过程就算完成。

Dagger2基本使用

@Inject和@Component

一、首先编写Person类,使用@Inject注解在构造函数上,用于标记须要注入的依赖。以下所示:

public class Person {
    private static final String TAG = "Person";
    @Inject
    public Person() {
    }

    public void eat(){
        Log.e(TAG, "eat....");
    }
}

二、接下来用@Component注解来完成依赖注入。咱们须要定义一个接口,接口命名建议为:目标类+Component,在编译后Dagger2就会为咱们生成名为Dagger+目标类名+Component的辅助类。具体代码以下所示:

@Component
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

Component则能够理解为注入器,它会把目标类依赖的实例注入到目标类中。在这里须要定义inject方法,传入须要注入依赖的目标类。

三、在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。

一、定义一个Engine抽象类

public abstract class Engine {
    public abstract void work();
}

二、接着定义它的实现类GasolineEngine:

public class GasolineEngine extends Engine{
    @Override
    public String work() {
        return "汽油发动机发动";
    }
}

三、随后在Car类中引用Engine,以下所示:

public class Car {
    private Engine engine;
    @Inject
    public Car(Engine engine) {
        this.engine = engine;
    }

    public String run(){
        return engine.work();
    }
}

四、建立EngineModule类,以下所示:

@Module
public class EngineModule {
    @Provides
    public Engine provideEngine(){
        return new GasolineEngine();
    }
}

五、接着在Component中指定EngineModule:

@Component(modules = EngineModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

六、最后在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);
    }
}
相关文章
相关标签/搜索