如何将依赖项注入需要来自上一个Activity的参数的Activity?

时间:2018-04-10 01:00:25

标签: java android dagger-2

我有一个LoginActivity,用户通过Auth0登录并返回一个身份验证令牌。此标记将传递给MainActivity:

Intent intent = new Intent(LoginActivity.this, MainActivity.class);
intent.putExtra(KEY_ACCESS_TOKEN, credentials.getAccessToken());
intent.putExtra(KEY_ID_TOKEN, credentials.getIdToken());
startActivity(intent);

我能够通过following this guide使用LoginActivity来完成依赖注入工作。

现在我正在尝试将依赖项注入MainActivity。我的MainActivity有一个MainActivityViewModel来处理UI和数据层之间的所有交互。我想将我的API注入我的ViewModel:

PetshackApi apiService;

@Inject
public PetMapViewModel(PetshackApi apiService) {
    this.apiService = apiService;
}

我定义了ViewModelModuleViewModelKeyMainActivityViewModelFactory(从GithubViewModelFactory重命名)。我在我的MainActivity顶部注入了viewModelFactory:

@Inject
ViewModelProvider.Factory viewModelFactory;

然后使用工厂获取我的viewModel:

viewModel = ViewModelProviders.of(this, viewModelFactory).get(MainActivityViewModel.class);

我使用this answer进行了设置。

问题是我的Retrofit / PetshackApi依赖项需要LoginActivity中的accessToken。所以我在MainActivity中定义了另一种方法来允许检索它:

public String getAccessToken() {
    return getIntent().getStringExtra(LoginActivity.KEY_ACCESS_TOKEN);
}

我在设置模块/组件时遇到问题/ ???我想我需要以某种方式将MainActivity注入到我的模块中,所以我尝试了Injecting Activity objects

MainActivityComponent.java

@Component(modules={AndroidSupportInjectionModule.class, AppModule.class, MainActivityModule.class, ViewModelModule.class})
public interface MainActivityComponent extends AndroidInjector<PetApplication> {
    @Component.Builder
    abstract class Builder extends AndroidInjector.Builder<PetApplication>{
        @BindsInstance
        abstract Builder application(Application application);
    }
    void inject(MainActivity mainActivity);
}

MainActivityModule.java

@Module(subcomponents = MainActivitySubcomponent.class)
abstract class MainActivityModule {
    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
}

MainActivitySubcomponent.java

@Subcomponent(modules = MainActivityChildModule.class)
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}

MainActivityChildModule.java

@Module
abstract class MainActivityChildModule {
    @Provides
    @Singleton
    Retrofit providesRetrofit(Application application, MainActivity mainActivity) {
        final String accessToken = mainActivity.getAccessToken();
        Interceptor interceptor = new Interceptor() {
            @Override
            public okhttp3.Response intercept(Chain chain) throws IOException {
                Request newRequest = chain.request().newBuilder()
                        .addHeader("authorization", "Bearer " + accessToken).build();
                return chain.proceed(newRequest);
            }
        };

        // Add the interceptor to OkHttpClient
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.interceptors().add(interceptor);
        OkHttpClient client = builder.build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(application.getString(R.string.endpoint_url))
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();
        return retrofit;
    }

    @Provides
    @Singleton // needs to be consistent with the component scope
    PetshackApi providesPetshackApiInterface(Retrofit retrofit) {
        return retrofit.create(PetshackApi.class);
    }
}

我是否在正确的轨道上?有关如何执行此操作的任何提示或示例?

1 个答案:

答案 0 :(得分:1)

我建议您将网络代码移到Activity模块之外,并创建一个可在整个应用程序中共享的应用程序模块。

重要的是,如果您有一个为每个请求提供令牌的TokenStore,您需要在发送请求时更新该值。

@Module
abstract class NetworkModule {

    @Provides
    @Singleton
    static TokenStore provideTokenStore(TokenStoreImpl tokenStore) {
        return tokenStore;
    }

    @Provides
    @Singleton
    static OkHttpClient provideOkHttpClient(AuthInterceptor authInterceptor) {
        // Add the interceptor to OkHttpClient
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.interceptors().add(authInterceptor);
        return builder.build();
    }

    @Provides
    @Singleton
    static Retrofit providesRetrofit(Application application, OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(application.getString(R.string.endpoint_url))
                .addConverterFactory(GsonConverterFactory.create())
                .client(okHttpClient)
                .build();
        return retrofit;
    }

    @Provides
    @Singleton // needs to be consistent with the component scope
    static PetshackApi providesPetshackApiInterface(Retrofit retrofit) {
        return retrofit.create(PetshackApi.class);
    }
}

interface TokenStore {
    String getToken();
    void setToken(String token);
}

@Singleton
class TokenStoreImpl implements TokenStore {

    String token;

    @Inject
    public TokenStoreImpl() { }

    @Override
    public String getToken() {
        return token;
    }

    @Override
    public void setToken(String token) {
        this.token = token;
    }
}

@Singleton
class AuthInterceptor implements Interceptor {

    private final TokenStore tokenStore;

    @Inject
    public AuthInterceptor(TokenStore tokenStore) {
        this.tokenStore = tokenStore;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request newRequest = chain.request()
                .newBuilder().addHeader("authorization", "Bearer " + tokenStore.getToken()).build();
        return chain.proceed(newRequest);
    }
}