Dagger 2注入两个改装对象

时间:2018-06-03 13:31:57

标签: android retrofit2 dagger-2

我在使用MVP时使用Dagger 2和retrofit2库。一切顺利,直到我尝试集成另一个服务(基本上我试图将另一个改造对象初始化为另一个服务)。我遵循了answer,但没有成功。

每当我收到错误时,我的每个片段和应用程序类似乎都无法识别组件类。

错误:找不到符号类DaggerApplicationComponent 错误:找不到符号类DaggerEpisodeComponent

代码

ApplicationComponent

@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {

    Retrofit exposeStreamingRetrofit();

    Retrofit exposeRetrofit();

    Context exposeContext();

    AppPreferenceHelper exposePrefs();

}

申请模块

   @Module
public class ApplicationModule
{
    private String mBaseUrl;
    private Context mContext;
    private AppPreferenceHelper mPrefsHelper;

    public ApplicationModule(Context context,String baseUrl)
    {
        mContext = context;
        mBaseUrl = baseUrl;
        mPrefsHelper = new AppPreferenceHelper(context, Consts.PREF_NAME);
    }


    @Singleton
    @Provides
    GsonConverterFactory provideGsonConverterFactory()
    {
        GsonConverterFactory gsonConverterFactory = GsonConverterFactory.create();
        return gsonConverterFactory;
    }

    @Singleton
    @Provides
    @Named("ok-1")
    OkHttpClient provideOkHttpClient()
    {

        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);

        return new OkHttpClient().newBuilder()
                .connectTimeout(500, TimeUnit.MILLISECONDS)
                .readTimeout(500,TimeUnit.MILLISECONDS)
                .addInterceptor(logging)
                .build();
    }

    @Singleton
    @Provides
    RxJava2CallAdapterFactory provideRxJava2CallAdapterFactory()
    {
        return RxJava2CallAdapterFactory.create();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(@Named("ok-1") OkHttpClient client, GsonConverterFactory convectorFactory, RxJava2CallAdapterFactory adapterFactory)
    {
        return new Retrofit.Builder()
                .baseUrl(mBaseUrl)
                .addConverterFactory(convectorFactory)
                .addCallAdapterFactory(adapterFactory)
                .client(client)
                .build();
    }

    @Provides
    @Singleton
    Retrofit provideStreamingRetrofit(@Named("ok-1") OkHttpClient client, GsonConverterFactory convectorFactory, RxJava2CallAdapterFactory adapterFactory) {
        return new Retrofit.Builder()
                .baseUrl(Consts.STREAMING_BASE_PATH)
                .addConverterFactory(convectorFactory)
                .addCallAdapterFactory(adapterFactory)
                .client(client)
                .build();
    }

    @Singleton
    @Provides
    Context provideContext()
    {
        return mContext;
    }

    @Singleton
    @Provides
    AppPreferenceHelper provideAppPreferenceHelper(){
        return mPrefsHelper;
    }

}

StreamingService

public interface StreamingService
{
    @GET("search")
    Observable<StreamingItems> getStreamingItems(@Query("keyword") String query);
}

流式传输模块 @Module

public class StreamingModule
{
    private StreamingView mView;

    public StreamingModule(StreamingView view)
    {
        mView = view;
    }

    @PerFragment
    @Provides
    StreamingService provideStreamingService(Retrofit retrofit)
    {
        return retrofit.create(StreamingService.class);
    }

    @PerFragment
    @Provides
    StreamingView provideView()
    {
        return mView;
    }

    public void disposeView()
    {
        mView = null;
    }
}

流式广告

@PerFragment
@Component(modules = StreamingModule.class, dependencies = ApplicationComponent.class)
public interface StreamingComponent {
    void inject(StreamingFragment streamingFragment);
}

Streaming Presenter

public class StreamingPresenter extends BasePresenter<StreamingView>
{
    private long                    mMaxPagesOfTopSeries;

    @Inject
    protected StreamingService mApiService;

    @Inject
    protected Mapper                mTopSeriesMapper;

    @Inject
    protected AppPreferenceHelper   mPrefsHelper;

    @Inject
    public StreamingPresenter()
    {
        mMaxPagesOfTopSeries = 1;

    }
}

问题可能与在组件应用程序类中公开另一个Retrofit实例有关,但我不确定。

更新1

EpisodeModule

@PerFragment
@Component (modules = EpisodeModule.class, dependencies = ApplicationComponent.class)
public interface EpisodeComponent
{
    void inject(EpisodeFragment episodeFragment);
}

在我创建了流类(服务,演示者,模块,组件)之后,我没有改变其他类中的任何内容,因此我认为问题出在应用程序模块/组件或流类中的某个地方,但是我是不确定因为我可能会改变其他片段的改造对象,因为我为流媒体添加了一个新的改造实例。

2 个答案:

答案 0 :(得分:4)

你是对的,问题是应用程序模块中第二个暴露的Retrofit实例。解决方案是使用dagger限定符。只需用以下代码替换适当的代码块:

应用程序模块

中定义具有限定符@Named("streaming")的改造提供程序
@Provides
@Singleton
@Named("streaming")
Retrofit provideStreamingRetrofit(@Named("ok-1") OkHttpClient client, GsonConverterFactory convectorFactory, RxJava2CallAdapterFactory adapterFactory) {
    return new Retrofit.Builder()
            .baseUrl(Consts.STREAMING_BASE_PATH)
            .addConverterFactory(convectorFactory)
            .addCallAdapterFactory(adapterFactory)
            .client(client)
            .build();
}

请勿在应用程序组件

中使用完全相同的限定符来公开改进实例
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {

    @Named("streaming") Retrofit exposeStreamingRetrofit();

    Retrofit exposeRetrofit();

    Context exposeContext();

    AppPreferenceHelper exposePrefs();

}

每当您需要流媒体服务改造实例时 - 不要忘记设置限定符。 流式传输模块

中的示例
@PerFragment
@Provides
StreamingService provideStreamingService(@Named("streaming") Retrofit retrofit) {
    return retrofit.create(StreamingService.class);
}

答案 1 :(得分:0)

您可以按照@ilosqu的描述使用@Named注释,但是我无法克服magic strings的使用和产生的代码异味。在具有许多Retrofit实例的大型项目中,开发人员将需要记住(或更糟糕的查找)每次需要实例时的Name值。这似乎很乏味,难以管理并且难以重构。我不是在这里判断,我只是想说明我对另一种方法的推理。

我的解决方案(Kotlin)是为每个Retrofit实例创建唯一的类,类似于Java中标记接口的想法。由于Retrofit是一个最终类,并且不提供任何接口以方便扩展,因此不得不创建一个抽象包装类,以实现相同的结果。

/**
 * This abstract class allows for extension and can expose 
 * convenience functions to Retrofit functionality by composition
 */

abstract class RetrofitContainer(private val retrofit: Retrofit) {
    fun <T> create(service: Class<T>): T {
        return retrofit.create(service)
    }

    fun getRetrofit(): Retrofit {
        return retrofit
    }
}

对于Retrofit的每个唯一实例,您需要创建一个扩展RetrofitContainer的具体类。

/**
 * This class can be injected anywhere you want the Streaming Retrofit instance
 */
class StreamingRetrofit(retrofit: Retrofit): RetrofitContainer(retrofit)

现在,您可以在模块中为每个“标记”类创建@Provides方法,并通过类进行注入,而无需@Named

@Provides
@Singleton
fun provideStreamingService(retrofit: StreamingRetrofit): StreamingService{
    return retrofit.create(StreamingService::class.java)
}

@Provides
@Singleton
fun provideStreamingRetrofit(
        client: OkHttpClient,
        adapterFactory: RxJava2CallAdapterFactory,
        converterFactory: GsonConverterFactory
): StreamingRetrofit {
    val retrofit = Retrofit.Builder()
            .baseUrl(Consts.STREAMING_BASE_PATH)
            .addConverterFactory(convectorFactory)
            .addCallAdapterFactory(adapterFactory)
            .client(client)
            .build();

    return StreamingRetrofit(retrofit)
}

此技术允许简洁的查找和重构,而无需记住任何魔术。在大多数情况下,可能可以使用与否则需要使用@Named实例相同的方法。