Android-Dagger2

Android基础 再看dagger2

2019-11-23  本文已影响0人  奋斗小青年Jerome

dagger2三年前写项目就一直在用,今天再炒一次剩饭,总结归纳,便于以后复习

仙图

dagger2理解

一个很厉害的"工厂",它能提供各种对象,并通过@Inject将对象注入到目标类,目标类就能使用这个对象了,而不用new关键字去生成对象
理解dagger2需要知道下面三个最基础的东西

@Module

它通常与@Provides一起使用,用于"生产"对象,但是用@Inject标记构造函数就可以提供依赖了么,为什么还需要@Module?很多时候我们需要提供依赖的构造函数是第三方库的,我们没法给它加上@Inject注解,又比如说提供以来的构造函数是带参数的,如果我们之所简单的使用@Inject标记它,那么他的参数又怎么来呢?@Module正是帮我们解决这些问题的,我习惯形象的理解成药水

@ Component

用于标注接口,是依赖需求方和依赖提供方之间的桥梁。被Component标注的接口在编译时会生成该接口的实现类(Dagger+Component名字),我们通过调用这个实现类的方法完成注入;Component接口中主要定义一些提供依赖的声明,我理解为注射器

@Inject

一是用来标记需要依赖的变量,以此告诉Dagger2为它提供依赖;
二是用来标记构造函数,Dagger2通过@Inject注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,以此来为被@Inject标记了的变量提供依赖,我理解为针头

一般我们在项目中,都会有这三个Module和对应的Component,分别是AppModule,ActivityModule,FragmentModule以及对应的ActivityComponent,FragmentComponent,AppComponent,先分析AppModule,AppComponent,其他稍微简单一些

@Module
public class AppModule {
private Application mApplication;
    public AppModule(Application application) {
        this.mApplication = application;
    }

    @Singleton
    @Provides
    public Application provideApplication() {
        return mApplication;
    }

    @Singleton
    @Provides
    public Gson provideGson() {
        return new Gson();
    }

    @Provides
    @Singleton
    IDataBase provideIDataBase(DBHelper dbHelper) {
        return dbHelper;
    }

}

可以看到Module就是提供这些依赖的地方,dagger会根据@Provides标记的方法返回依赖对象,这个AppModule中提供了Application和Gson对象的创建,当然还可以有其他的对象创建都可以放进去

@Singleton
@Component(modules = {AppModule.class, HttpModule.class})
public interface AppComponent {

    Application application();
    Gson gson();
    DataManager getDataManager();
    HttpHelper getHttpHelper();
    DBHelper getDBHelper();
}

上面代码中,可以看到装载了HttpHelper,DBHelper等对象,那再看看这两个生产者(Module)

@Module
public class HttpModule {

    @Singleton
    @Provides
    Retrofit.Builder provideRetrofitBuilder() {
        return new Retrofit.Builder();
    }


    @Singleton
    @Provides
    OkHttpClient.Builder provideOkHttpBuilder() {
        return new OkHttpClient.Builder();
    }


    @Singleton
    @Provides
    Retrofit provideRetrofit(Retrofit.Builder builder, OkHttpClient client) {
        return createRetrofit(builder, client);
    }

    @Singleton
    @Provides
    OkHttpClient provideClient(OkHttpClient.Builder builder) {
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
        builder.addInterceptor(loggingInterceptor);
        builder.connectTimeout(10, TimeUnit.SECONDS);
        builder.readTimeout(20, TimeUnit.SECONDS);
        builder.writeTimeout(20, TimeUnit.SECONDS);
        //错误重连
        builder.retryOnConnectionFailure(true);
        return builder.build();
    }

    @Singleton
    @Provides
    UserAPI provideUserService(Retrofit retrofit) {
        return retrofit.create(UserAPI.class);
    }

    private Retrofit createRetrofit(Retrofit.Builder builder, OkHttpClient client) {
        return builder
                .baseUrl(URLS.URL_HOST)
                .client(client)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
}

注意: provideUserService, provideClient这几个的生产者里面都是含有参数的,dagger2在使用编译时注解时,它寻找注解参数的顺序如下

步骤1:首先查找@Module标注的类中是否存在提供依赖的方法。
步骤2:若存在提供依赖的方法,查看该方法是否存在参数。
a:若存在参数,则按从步骤1开始依次初始化每个参数;
b:若不存在,则直接初始化该类实例,完成一次依赖注入。
步骤3:若不存在提供依赖的方法,则查找@Inject标注的构造函数,看构造函数是否存在参数。
a:若存在参数,则从步骤1开始依次初始化每一个参数
b:若不存在,则直接初始化该类实例,完成一次依赖注入。

这里拆分着看一下:
第一步,查找是否有@Module标注,意思就是从Module找是否有被@Provides标注并且返回值正好是需要的那个对象,比如在MainActivity中@Inject注入了UserAPI这个类,dagger就会去@Module中(也就是HttpModule)去找是不是有Provides标注,且返回值是UserAPI的
第二步,这一找就找到了,确实存在provideUserService这个方法,然后查看是否有参数
发现有参数Retrofit,这个时候要初始化这个Retrofit参数就又回到第一步了,一看,有provideRetrofit这个方法,有Provides且返回值正确,这就初始化参数完成依赖注入
那如果是上述步骤3的情况呢,那就是AppModule中provideIDataBase这个方法,返回IDataBase,它的参数DBHelper我这里没有定义Module去Provides注解生成,则就会去DBHelper中查找@Inject标注的构造函数

public class DBHelper implements IDataBase {

    @Inject
    public DBHelper(){

    }
}

下面总结一下除了上述三个注解外,其他我们用到的很重要的几个注解

@Provides

@Provides用于标注Module所标注的类中的方法,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值;

@Scope

@Scope用于自定义注解,我们可以通过@Scope自定义的注解来限定注解作用域,实现局部的单例;比如我们前面使用到的@ActivityScope:

@Scope
@Retention(RUNTIME)
public @interface ActivityScope {}

如果需要提供局部单例支持,则需要在Component中和@provides注解的方法上@ActivityScope,这里说的局部单例的意思是在该Component中是唯一的,如果Component是全局唯一的话就是全局单例了,比如AppComponent。

@Singleton
@Singleton其实就是一个通过@Scope定义的注解,我们一般通过它来标记全局单例(AppComponent)。

@Qualifiers 和 @Name

Qualifiers 是修饰符的意思,那么它修饰的是什么呢?我们在一个 Module 中 @Provides 提供的依赖是由返回值决定的。这样就会出现问题,同一种类型不同实例,怎么去区别?比如

public class SecondActivity extends AppCompatActivity {
    @Inject
    String phone;
    @Inject
    String computer;

}

当我们注解的时候,分别要Module生成两个不同的字符串,我们很显然想到如下这样的代码,但是这样是编译不通过的

@Module
public class SecondActivityModule {

    @Provides
    @Singleton
    public TestSingleton provideTestSingleton(){
        return new TestSingleton();
    }

    @Provides
    public String providePhone() {
        return "手机";
    }

    @Provides
    public String providePhone() {
        return "电脑";
    }

}

那么dagger2使用@Name即可实现

 @Inject
@Named("phone")
String phone;

@Inject
@Named("computer")
String computer;

@Module
public class SecondActivityModule {

    @Provides
    @Singleton
    public TestSingleton provideTestSingleton(){
        return new TestSingleton();
    }

    @Provides
    @Named("phone")
    public String providePhone() {
        return "手机";
    }

    @Provides
    @Named("computer")
    public String provideComputer() {
        return "电脑";
    }

}

但是还是很麻烦,我们可以自定义注解,因为@Name只是被@Qualifier注解的一个注解

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Phone {
}


@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Computer {
}

使用的时候,在目标类上这么注解

@Inject
@Phone
String phone;

@Inject
@Computer
String computer;

在Module中这么写

@Module
public class SecondActivityModule {

    @Provides
    @Singleton
    public TestSingleton provideTestSingleton(){
        return new TestSingleton();
    }

    @Provides
    @Phone
    public String providePhone() {
        return "手机";
    }

    @Provides
    @Computer
    public String provideComputer() {
        return "电脑";
    }

}

dependencies 和 @SubComponent

上面我们说要将Module提供给调用者进行注入,那么就需要一根注射器@Component,dagger2除了这种直接写一个@Component来提供连接器的形式,还支持连接器依赖
Component 依赖 是通过 @Component 的注解中 dependencies 选项来标识的,意思是指 该 Component依赖 dependencies 指定的 Component

@ActivityScope
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
    void inject(MainActivity target);
    void inject(LoginActivity target);
}
@Singleton
@Component(modules = {AppModule.class, HttpModule.class})
public interface AppComponent {
    App getContext();
    DataManager getDataManager();
    HttpHelper getHttpHelper();
    DBHelper getDBHelper();
}

上面的代码通过dependencies关键字,让ActivityComponent依赖AppComponent
注意:如果ActivityComponent中要能提供注入上述DataManager,HttpHelper,DBHelper等的能力,那么就必须如上代码所示,显示提供getXXX方法,否则编译会报错
这样在Activity中就能够注入上述对象了

@BindLayout(R.layout.activity_main)
public class MainActivity extends BaseActivity<MainPresenter> implements MainContract.MainView {
    @Inject
    DataManager dataManager;
...
}

再看SubComponent
@SubComponent 也是管理 Component 间的依赖,不同的是这种方式不需要 在被依赖的 Component 中显式的声明可以获取相应类实例的方法。通过 @SubComponent 来管理的 Component 之间是一种 继承关系,子 Component 理所当然的可以使用父 Component 的可以提供的类实例。
具体用法,这里只举例,我用的很少
TestActivityComponent

@Subcomponent(modules = TestModule.class)
public interface TestActivityComponent {
    void injectActivity(TestActivity Activity);
}

AppComponent

@Component(modules = APPModule.class)
public interface AppComponent {
    //注意这里的写法,就是显示关联二者
    TestActivityComponent addSub(TestModule testModule);
}

注意:这个格式是固定的

子Component 方法名 (子Component 对应的 Module);

在Activity中使用

public class TestActivity extends AppCompatActivity {

    @Inject
    TestBean ;//这个TestBean是APPModule中提供的
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_component_sub);
        DaggerAppComponent  daggerAppComponent= DaggerAppComponent.builder().build();
        daggerAppComponent.addSub(new TestModule()). injectActivity(this);
    }
}

那么dependencies 和 @SubComponent有什么区别了,官方文档显示

In general we have two ways to do this - with @Subcomponent annotation or with Components dependencies. The main difference between them is an objects graph sharing. Subcomponents have access to entire objects graph from their parents while Component dependency gives access only to those which are exposed in Component interface.
And what is more important all scoping stuff happens here. All instances taken from UserComponent inherited from AppComponent still are singletons (in Application scope). But those which are produced by UserModule (which is a part of UserComponent) will be “local singletons” which live as long as this UserComponent instance.

翻译过来:

Subcomponent 可以访问到 父 Component 的全部对象图,而 Component 只可以访问到在所依赖的 Component 中暴露出来的类。
更重要的是,所有范围内的事情都发生在这里。从AppComponent继承的UserComponent获取的所有实例仍然是单例(在应用程序范围内)。但是由UserModule(它是UserComponent的一部分)生成的那些将是“本地单例”,其存在与此UserComponent实例一样长。

总结区别就是:

  1. Subcomponent 可以访问到 父 Component 的全部对象图,而 Component 只可以访问到在所依赖的 Component 中暴露出来的类。
  2. Subcomponent继承得到的子组件可以访问到父组件中提供的实例,并且该实例的scope 和父组件定义的scope 相同。

还有一点要注意,多 Component 与 Scope 使用时有如下限制

  1. Component 和他所依赖的 Component 不能公用相同的 Scope,每个Component 都要有自己的 Scope,编译时会报错,因为这有可能破坏Scope的范围。
  2. @Singleton 的 Component 不能依赖其他 Component。正常来说使用 @Singleton 注解的Component应为全局的Component 。
  3. 无Scope的Component不能依赖有Scope的Component,因为这也会导致Scope 被破坏。
  4. Module提供 或通过构造函数注入依赖图的类和其 Component 必须使用相同的Scope 。
上一篇下一篇

猜你喜欢

热点阅读