Dagger2 Component讲解

来源:互联网 发布:傲战无双坐骑进阶数据 编辑:程序博客网 时间:2024/05/17 06:54

Dagger2 不是通过反射的方式去实现注入的,是通过生成代码的方式来达到注入效果的,在Android的很多开源框架中都是用到了代码生成工具(APT), 比如ButterKnife。
现在一般使用Dagger2来注入对象的时候,都会使用一个AppComponent和一个ActivityComponent(FragmentComponent),AppComponent用于提供一些全局的对象,例如:OkHttpClient,ActivityComponent可用于提供Activity生命周期内的对象,例如:Presenter,Model。代码实现如下:

@Singleton@Component(modules = AppModule.class)public interface AppComponent {//在AppModule中提供的全局变量,ActivityComponent注入的类中要想使用,这里必须声明,且向外提供类型对象的方法不能有参数OkHttpClient getOkHttpClient();}@Modulepublic class AppModule {    @Provides    @Singleton    OkHttpClient provideContext() {...        return mOkHttpClient;     }}@ActivityScope@Component(dependencies = AppComponent.class,modules = ActivityModule.class)public interface ActivityComponent {    void inject(MainActivity activity);}@Modulepublic class ActivityModule {}

Application初始化的时候对用

AppComponent mAppComponent;    public AppComponent getAppComponent() {        if (mAppComponent == null) {            mAppComponent = DaggerAppComponent.builder()                    .appModule(new AppModule())                    .build();        }        return mAppComponent;    }
@Modulepublic class AppModule {//提供全局对象}
Activity初始化的时候调用,可放在基类中

public ActivityComponent getActivityComponent() {        return DaggerActivityComponent.builder()                .appComponent(getAppComponent())                .build();    }
Activity基类的实现类调用

getActivityComponent().inject(this);

现在我有这样的一个需求,项目结构发生了点变化,核心库和业务逻辑库里面都有向外提供全局对象的组件和注入全局初始化的类(inject),这时核心库就需要一个AppComponent,业务逻辑库就需要一个ServiceComponent,并且ActivityComponent注入的类中需要用到AppComponent和SeviceComponent向外提供的类。那么这时就需要ActivityComponent中要dependencies AppComponent和ServiceComponent两个组件才行,书写如下:

@ServiceScope@Component(modules = ServiceModule.class)public interface ServiceComponent {}@Modulepublic class ServiceModule {}
@ActivityScope@Component(dependencies = {AppComponent.class,ServiceComponent.class},modules = ActivityModule.class)public interface ActivityComponent {    void inject(MainActivity activity);   }
这里直接使用dependencies依赖了两个Conponment (dependencies = {AppComponent.class,ServiceComponent.class}),这时问题就来了,编译时直接报了个错,错误如下
Error:(14, 1) 错误: @com.cj.dagger2.di.scope.ActivityScope com.cj.dagger2.di.component.ActivityComponent depends on more than one scoped component:@Singleton com.cj.dagger2.di.component.AppComponent@com.cj.dagger2.di.scope.ServiceScope com.cj.jian.dagger2.di.component.ServiceComponent
那么意思就是说不能dependencies 多个Component咯,那怎么搞呢,只能依赖一个组件,要么dependencies AppComponent,要么dependencies ServiceComponent,这样一来就不能同时使用AppComponent和ServiceComponent中提供的类型对象(或不能同时使用注解提供的单例对象(单例@Singleton和@ServiceScope)。那么如何解决呢,在不使用Subcomponent的情况下,依赖两个以上的反正我是不知道如何解决,不能同时依赖两个以上的,那么我就一个一个依赖,让ServiceComponent中依赖AppComponent, ActivityComponent中依赖ServiceComponent即可,然后在ServiceComponent中向外提供AppComponent中的所有提供的类型方法(也就是copy一下到ServiceComponent中),如若不提供,ServiceComponent中就不具备提供AppComponent中的类型对象,导致ActivityComponent注入类中不能使用AppComponent组件提供的单例对象,所以一定要提供,这是再次编译就可以了。还有一点需要注意:两个拥有依赖关系的 Component 是不能有相同 @Scope 注解的!使用@SubComponent 则可以使用相同的@Scope注解。以下还有几点:
1.注入要初始化的成员变量不能是private的
2.构造依赖有两种方式:1.在类的构造函数上用@Inject标注,要是单例就在类上加相应单例的注解(构造中有参数时,参数类型必须可以自动构造),2.在Module中用@Provides提供,要是单例就再加相应单例的注解
3.Module中不能有返回相同类型的方法,但是可以使用@Qualifier和@Named注解过的注解加在方法上就可以加以区别,声明的变量也需要加此注解予以区别。
4.自定义@ActivityScope注解,对应Activity的生命周期,ActivityComponent生命周期跟Activity一样的组件,这里提供了inject方法将Activity注入到ActivityComponent中,通过该方法,将Activity中需要注入的对象注入到Activity中。
以下提供完整的代码:

@Scope@Retention(RetentionPolicy.RUNTIME)public @interface ActivityScope {}@Scope@Retention(RetentionPolicy.RUNTIME)public @interface ServiceScope {}@Modulepublic class ActivityModule {}@Modulepublic class ServiceModule {}@Modulepublic class AppModule {@Provides    @Singleton    OkHttpClient provideContext() {...        return mOkHttpClient;     }}@ActivityScope@Component(dependencies = {ServiceComponent.class},modules = ActivityModule.class)public interface ActivityComponent {    void inject(MainActivity activity);}@ServiceScope@Component(dependencies = AppComponent.class,modules = ServiceModule.class)public interface ServiceComponent {//此方法必须提供,否则ActivityComponent组件中提供不了OkHttpClient类型对象OkHttpClient getOkHttpClient();}@Singleton@Component(modules = AppModule.class)public interface AppComponent {//在AppModule中提供的全局变量,ActivityComponent注入的类中要想使用,这里必须声明,且向外提供类型对象的方法不能有参数OkHttpClient getOkHttpClient();}//可在Application中提供,或是生命周期等同与Application的类中提供AppComponent mAppComponent;    public AppComponent getAppComponent() {        if (mAppComponent == null) {            mAppComponent = DaggerAppComponent.builder()                    .appModule(new AppModule())                    .build();        }        return mAppComponent;    }    ServiceComponent mServiceComponent;    public ServiceComponent getServiceComponent() {        if (mServiceComponent == null) {            mServiceComponent = DaggerServiceComponent.builder()                    .appComponent(getAppComponent())                    .serviceModule(new ServiceModule())                    .build();        }        return mServiceComponent;    }//Activity的基类中提供public ActivityComponent getActivityComponent() {        return DaggerActivityComponent.builder()                .serviceComponent(App.getApp().getServiceComponent())                .build();    }//具体Activity中注入,例如MainActivity总就可以@Inject OkHttpClient对象getActivityComponent().inject(this);
这里主要就是记录一下依赖多个Component编译出错问题,如若有其他好的解决办法欢迎留言