Dagger2,提供不同URL的Retrofit实例

Dagger2, providing Retrofit instances with different URLs

目前我正在使用 Dagger 2 注入 Retrofit 的实例以用于小部件中的 api 调用。根据我的理解,Dagger 使用类型搜索要注入的东西,因此声明 2 个不同名称的单独 @Provides Retrofit providesRetrofit() 是行不通的。

这是我当前的代码:

模块:

@Module
public class ApiModule {

    @Provides
    @Singleton
    GsonConverterFactory provideGson() {
        return GsonConverterFactory.create();
    }

    @Provides
    @Singleton
    RxJavaCallAdapterFactory provideRxCallAdapter() {
        return RxJavaCallAdapterFactory.create();
    }

    @Singleton
    @Provides
    Retrofit providePictureRetrofit(GsonConverterFactory gsonConverterFactory, RxJavaCallAdapterFactory rxJavaCallAdapterFactory) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(MarsWeatherWidget.PICTURE_URL)
                .addConverterFactory(gsonConverterFactory)
                .addCallAdapterFactory(rxJavaCallAdapterFactory)
                .build();
        return retrofit;
    }

....
//Here is the other Retrofit instance where I was wanting to use a different URL.

//    @Singleton
//    @Provides
//    Retrofit provideWeatherRetrofit(GsonConverterFactory gsonConverterFactory, RxJavaCallAdapterFactory rxJavaCallAdapterFactory) {
//        Retrofit retrofit = new Retrofit.Builder()
//                .baseUrl(MarsWeatherWidget.WEATHER_URL)
//                .addConverterFactory(gsonConverterFactory)
//                .addCallAdapterFactory(rxJavaCallAdapterFactory)
//                .build();
//        return retrofit;
//    }
}

组件:

@Singleton
@Component(modules = ApiModule.class)
public interface ApiComponent {

    void inject (MarsWeatherWidget marsWeatherWidget);

}

class 扩展 Application:

public class MyWidget extends Application {

    ApiComponent mApiComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        mApiComponent = DaggerApiComponent.builder().apiModule(new ApiModule()).build();
    }

    public ApiComponent getApiComponent() {
        return mApiComponent;
    }
}

最后是我实际注入它的地方:

@Inject Retrofit pictureRetrofit;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        mAppWidgetIds = appWidgetIds;
        ((MyWidget) context.getApplicationContext()).getApiComponent().inject(this);
        final int N = appWidgetIds.length;
        for (int i = 0; i < N; i++) {
            updateAppWidget(context, appWidgetManager, appWidgetIds[i]);
        }
    }

......
//use the injected Retrofit instance to make a call

那么我如何组织它来给我一个单独的 Retrofit 实例,它是用不同的 URL 构建的,用于访问不同的 API?如果需要更多信息,请告诉我。

同类型提供不同版本

您可以使用@Named(或使用@Qualifier注释的自定义注释)来区分相同类型的变体。

添加如下注释:

@Singleton
@Provides
@Named("picture")
Retrofit providePictureRetrofit(GsonConverterFactory gsonConverterFactory, RxJavaCallAdapterFactory rxJavaCallAdapterFactory) {
    return retrofit = new Retrofit.Builder()
            .baseUrl(MarsWeatherWidget.PICTURE_URL) // one url
            .build();
}


@Singleton
@Provides
@Named("weather")
Retrofit provideWeatherRetrofit(GsonConverterFactory gsonConverterFactory, RxJavaCallAdapterFactory rxJavaCallAdapterFactory) {
    return retrofit = new Retrofit.Builder()
            .baseUrl(MarsWeatherWidget.WEATHER_URL) // other url
            .build();
}

自定义@Qualifier

您也可以像下面这样创建自定义注释:

@Qualifier
@Retention(RUNTIME)
public @interface Picture {}

您只需使用它而不是 @Named(String)

注入合格版本

当你的模块提供限定类型时,你只需要在需要依赖的地方添加限定符。

MyPictureService provideService(@Named("picture") Retrofit retrofit) {
    // ...
}

您应该使用 限定符 注释来区分具有相同类型的不同对象,例如区分图片的 Retrofit 和表示图片的 Retrofit天气。

您将相同的限定符应用于 @Provides 方法和 @Inject 参数(构造函数或方法参数,或字段)。

@Named 是一个限定符注释,但使用它意味着您必须记住在提供点和所有注入点使用完全相同的字符串。 (很容易在某处打错 @Named("whether")。)

但是定义您自己的限定符注解很容易。只需定义一个自定义注释类型,并用 @Qualifier:

注释
@Documented
@Qualifier
public @interface Picture {}

@Documented
@Qualifier
public @interface Weather {}

然后你可以不同地绑定每个 Retrofit:

@Provides @Picture Retrofit providePictureRetrofit(…) {…}
@Provides @Weather Retrofit provideWeatherRetrofit(…) {…}

然后将每个注入到你需要的地方:

@Inject @Picture Retrofit pictureRetrofit;
@Inject @Weather Retrofit weatherRetrofit;
// (But constructor injection is better than field injection!)