【问题标题】:Dagger + Retrofit dynamic URLDagger + Retrofit 动态 URL
【发布时间】:2016-08-14 16:10:15
【问题描述】:

问题

我需要从 USER 输入的域调用 API,并且我需要在调用之前根据插入的数据编辑我的 Retrofit 单例。

有没有办法“重置”我的单例,强制它重新创建?

有没有办法在通话前用我的数据(可能在拦截器中?)更新我的baseUrl

代码

单身人士

@Provides
@Singleton
Retrofit provideRetrofit(SharedPreferences prefs) {

    String apiUrl = "https://%1s%2s";
    apiUrl = String.format(apiUrl, prefs.getString(ACCOUNT_SUBDOMAIN, null), prefs.getString(ACCOUNT_DOMAIN, null));

    OkHttpClient httpClient = new OkHttpClient.Builder()
            .addInterceptor(new HeaderInterceptor())
            .build();

    return new Retrofit.Builder()
            .baseUrl(apiUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .client(httpClient)
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();
}

@Provides
@Singleton
API provideAPI(Retrofit retrofit) {
    return retrofit.create(API.class);
}

API

@FormUrlEncoded
@POST("endpoint")
Observable<Response> logIn(@Field("login") String login, @Field("password") String password);

现在的运作方式

这个想法是在 API 调用之前通过SharedPrefs 保存用户域数据,并使用格式化字符串修改baseUrl

【问题讨论】:

  • 这就是你的单身?你在哪里做你的retrofit.create()
  • 是的,这是负责为我的演示者提供改造的单身人士。我确实有更多单身人士,但他们与问题无关。编辑:我已将我的 create() 单例添加到问题代码中。
  • 尝试过类似baseUrl() 和您的请求@POST("{url}"),然后url 等于您的函数的参数,例如@Path("url") String url?只是一个想法。您能说明如何调用您的 API 吗?
  • 我认为通过 API 接口中的注释插入完整 url 的唯一方法是使用 @URL 并且只能在 @GET 调用中使用。
  • provideRetrofit 做得太多了。提供 url、okhttp 客户端,以及 2 个调用适配器和 header 拦截器中的每一个都应该是它自己的方法

标签: android retrofit dagger retrofit2 dagger-2


【解决方案1】:

我在这里看到 2 个选项:

  • 按原意使用匕首。为每个baseUrl 创建自己的Retrofit 客户,或
  • 在发送请求之前使用拦截器修改请求

匕首方法

如果您要暴力破解网址,这可能不是正确的选择,因为它依赖于为每个网址创建一个新的 Retrofit 实例。

现在,每次 url 更改时,您只需通过提供新的 UrlModule 来重新创建以下演示的 UrlComponent

清理

清理您的@Singleton 模块,使其提供GsonConverterFactoryRxJavaCallAdapterFactory 以正确使用dagger 而不会重新创建共享对象。

@Module
public class SingletonModule {

  @Provides
  @Singleton
  GsonConverterFactory provideOkHttpClient() {/**/}

  @Provides
  @Singleton
  RxJavaCallAdapterFactory provideOkHttpClient() {/**/}
}


@Singleton
@Component(modules = SingletonModule.class)
interface SingletonComponent {

    // sub component
    UrlComponent plus(UrlModule component);
}

网址范围

引入@UrlScope 来限定Retrofit 实例。

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UrlScope {
}

然后创建一个子组件

@SubComponent(modules=UrlModule.class)
public interface UrlComponent {}

还有一个模块

@Module
class UrlModule {

    private final String mUrl;

    UrlModule(String url) { mUrl = url; }

    @Provides
    String provideUrl() {
        return mUrl;
    }

    @Provides
    @UrlScope
    OkHttpClient provideOkHttpClient(String url) {
        return new OkHttpClient.Builder().build();
    }

    @Provides
    @UrlScope
    Retrofit provideRetrofit(OkHttpClient client) {
        return new Retrofit.Builder().build();
    }

}

使用作用域Retrofit

实例化组件并使用它。

class Dagger {

    public void demo() {
        UrlModule module = new UrlModule(/*some url*/);
        SingletonComponent singletonComponent = DaggerSingletonComponent.create();
        UrlComponent urlComponent = singletonComponent.plus(module);

        urlComponent.getRetrofit(); // done.
    }
}

OkHttp 方法

提供一个适当范围的拦截器(在本例中为@Singleton)并实现相应的逻辑。

@Module
class SingletonModule {

    @Provides
    @Singleton
    GsonConverterFactory provideGsonConverter() { /**/ }

    @Provides
    @Singleton
    RxJavaCallAdapterFactory provideRxJavaCallAdapter() { /**/ }

    @Provides
    @Singleton
    MyApiInterceptor provideMyApiInterceptor() { /**/ }

    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient(MyApiInterceptor interceptor) {
        return new OkHttpClient.Builder().build();
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(OkHttpClient client) {
        return new Retrofit.Builder().build();
    }
}

@Singleton
@Component(modules = SingletonModule.class)
interface SingletonComponent {

    Retrofit getRetrofit();

    MyApiInterceptor getInterceptor();
}

todo 实施MyApiInterceptor。您需要为基本 url 设置一个 setter,然后只需重写/修改通过的请求。

然后,再一次,继续使用它。

class Dagger {

    public void demo() {
        SingletonComponent singletonComponent = DaggerSingletonComponent.create();
        MyService service = singletonComponent.getRetrofit().create(MyService.class);
        MyApiInterceptor interceptor = singletonComponent.getInterceptor();

        interceptor.setBaseUrl(myUrlA);
        service.doA();
        interceptor.setBaseUrl(someOtherUrl);
        service.doB();
    }
}

作为第三种方法,您还可以使用 reflection 直接更改基 URL — 我最后添加这个只是为了完整性。

【讨论】:

    【解决方案2】:

    您可以实现 BaseUrl 并传递它而不是固定 URL。check out this link 其他方法是实现Endpoint 并使用setUrl()。因此,要在运行时更改某些标头值,您可以使用拦截器并将其添加到 OkHttp。

    【讨论】:

    猜你喜欢
    • 2016-07-29
    • 2018-01-02
    • 1970-01-01
    • 2018-04-17
    • 2017-08-06
    • 1970-01-01
    • 2018-09-22
    • 2014-07-30
    • 1970-01-01
    相关资源
    最近更新 更多