Dagger 2更新模块中的对象

时间:2016-11-21 01:12:37

标签: android dependency-injection dagger

我对DI一般都很陌生,尤其是Dagger。我目前遇到了这个问题,但我还没有找到解决方案。假设我有一个AppModule来为Retrofit客户端提供放入请求标头的访问令牌

@Module
public class AppModule {

    @Provides
    @Singleton
    SharedPref provideSharedPreferences(Application application) {
        return new SharedPref(application);
    }


    @Provides
    @Singleton
    AuthInterceptor provideAuthenticationInterceptor(SharedPref sharedPref) {
        return new AuthInterceptor(sharedPref.getAccessToken());
    }

    @Provides
    @Singleton
    OkHttpClient provideHttpClient(AuthInterceptor authInterceptor) {
    return new OkHttpClient.Builder()
            .addInterceptor(authInterceptor)
            .build();
    }

    @Provides
    @Singleton
    DribbbleApi provideClient(OkHttpClient client, Gson gson) {
        return new Retrofit.Builder()
            .baseUrl(DribbbleApi.ENDPOINT)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build()
            .create((DribbbleApi.class));
    }
}

现在,当用户首次使用该应用时,SharedPreference中的访问令牌不存在,因此我为其提供了默认值。但是一旦他们登录,访问令牌将保存到SharedPreference,但我无法更新OkHttpClient以接受新值,因为AuthInterceptor已使用默认访问令牌创建应用的开始。

我在看这里有一些同样问题的问题,但似乎没有一个在我的案例中有用。我正在考虑使用这个模块:

public class AuthenticationManager {
    private String accessToken;

    public String getAccessToken() {
        return accessToken;
    }

    public void setAccessToken(String ac) {
        this.accessToken = ac;
    }
}

@Module
public class AuthenticationModule {
    @Provides
    @Singleton
    AuthenticationManager provideAuthenticationManager() {
        return new AuthenticationManager();
    }
}

只需获取访问令牌并动态创建Retrofit / OkHttp客户端,但这对我来说似乎不对。任何人都有解决方案吗?

1 个答案:

答案 0 :(得分:1)

您的AuthenticationManager解决方案对我来说很合适,或者您可以使用您编写的AtomicReference<String>或一般Holder<String>课程。

您最好的选择是通过应用程序提供类似@Named("access-token") String或自定义限定符 - 注释@AccessToken String的内容,使用非单身@Provides方法,该方法始终使用模块状态返回最新值,但也存在许多问题:

  1. 这里没有自然的setter,与AuthenticationManager不同。除非通过您在@Provides方法中可以接受的依赖关系图中的其他内容提供当前值,否则您将必须注入Module或可以访问Module的内容}可变字段。这听起来不容易理解。

  2. 字符串不可变,因此如果您想要一个返回最新值的对象,您将始终需要@AccessToken Provider<String>而不是@AccessToken String。 Dagger并不容易制作只能注入提供者的密钥,所以除非你完全控制这个代码库或者可以设置静态分析检查,否则这将是脆弱的,容易被滥用。

  3. 您对Dagger解决方案的线程安全性和同步性有一些更有限的控制权,而您自己的可设置的持有者具有您可以自己定义的语义。

  4. 在单元测试中,如果您希望在不创建自定义测试Dagger组件的情况下更改Provider的值,则必须创建一个可设置的Provider类。这看起来非常像AtomicReference,Holder或您的AuthenticationManager,您可以从其中一个开始。

  5. 作为最后的替代方案,如果您可以将请求的状态表示为一个短期不可变对象,您可能更愿意创建其中一个具有故意限制的生命周期。通过这种方式,您可以使用短期对象而不是单例,而不必担心以后更新现有实例。如果(例如)您希望使用旧访问令牌进行重试,但是要使用新访问令牌创建新请求,则这也可能具有吸引人的重试语义。如果此选项对您有吸引力,也可以查找Dagger子组件:您可以为每个请求创建一个带有新的不可变模块的新子组件,然后拥有对象图的完全访问权限,包括访问临时访问令牌和状态尽可能深的需要。