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