Как предоставить именованное свойство, предоставляемое модулем, в компоненте

Я пытался решить свою проблему в Google, но до сих пор не понимаю, что делаю не так.

Дело: Существует NetworkModule, который предоставляет все необходимые части для построения объекта Retrofit (он также предоставляется). Одной из таких частей является Interceptor или OkHttpClient, где для всех запросов установлены HTTP-заголовки. Пока пользователь приложения не авторизован, я не хочу ничего делать с этим конкретным Interceptor, но как только приложение получит access_token, я хотел бы установить его в Interceptor, чтобы он установил еще один заголовок с этим конкретным access_token.

В ответах Google говорится, что правильный способ сделать это - открыть этот Interceptor, чтобы иметь возможность установить этот access_token, когда мне нужно. Итак, что я сделал:

Вот мой компонент приложения:

@ApplicationScope
@Component(modules = [
    AppModule::class,
    DataModule::class,
    NetworkModule::class,
    NetworkServicesModule::class,
    RepositoryModule::class,
    SchedulersModule::class])
interface AppComponent{
    /*
    ...
    */

    @Named(NetworkModule.Interceptors.HEADER)
    val headerInterceptor: Interceptor
}

А вот модуль, который предоставляет этот перехватчик:

@Module
class NetworkModule {
    object Interceptors {
        const val HEADER = "HTTP_HEADER_INTERCEPTOR"
        ...
    }
    ...
    @Provides
    @ApplicationScope
    @Named(Interceptors.HEADER)
    fun provideHeaderInterceptor(): Interceptor {
        return HeaderInterceptor()
    }
    ...
}

Но возникает проблема, когда я пытаюсь создать свой модуль «приложение» (Сборка -> Создать модуль «приложение»). При этом возникает ошибка и я не могу понять, что не так.

Вот как выглядит ошибка:

error: okhttp3.Interceptor cannot be provided without an @Provides-annotated method.
public abstract okhttp3.Interceptor getHeaderInterceptor();
okhttp3.Interceptor is provided at
com.example.app.di.components.AppComponent.getHeaderInterceptor()

вы использовали кинжал 2 для кода.

Android Team 30.03.2018 13:41

@AndroidTeam да, я использую Dagger2

vendettacore 30.03.2018 13:46

если вы хотите, предоставьте полную демонстрацию модернизации с помощью Dagger 2.

Android Team 30.03.2018 13:48

@AndroidTeam Я не уверен, что подписываюсь на вас. Какая идея разместить демо? Проблема на поверхности :) Все кроме этого нормально

vendettacore 30.03.2018 13:53
0
4
603
2

Ответы 2

Я предлагаю дооснащение с помощью Dagger 2. сделать сетевой модуль ..

@Module

public class NetModule { String baseUrl;

public NetModule(String baseUrl) {
    this.baseUrl = baseUrl;
}

@Provides
@Singleton
OkHttpClient provideOkhttpClient(Context context) {
    HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
    interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
    OkHttpClient.Builder client = new OkHttpClient.Builder();
    client.readTimeout(20, TimeUnit.SECONDS);
    client.writeTimeout(20,TimeUnit.SECONDS);
    client.connectTimeout(20, TimeUnit.SECONDS);
    client.addInterceptor(interceptor);
    client.addInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            if (context == null) {
                request = request
                        .newBuilder()
                        .build();
            } else {
                request = request
                        .newBuilder()
                        .addHeader("Authorization", "Bearer " + AppSetting.getStringSharedPref(context, Constants.USER_KEY_TOKEN, ""))
                        .build();
            }
            return chain.proceed(request);
        }
    });
    return client.build();
}
@Provides
@Singleton
Retrofit getRetrofit(){
    return new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .baseUrl(baseUrl)
            .client(provideOkhttpClient())
            .build();

}

}

сделать интерфейс для вызова API ..

public interface Api {
@GET("marvel/")
Call<List<Hero>> getHero();
@GET("/users")
Call<List<User>> getUser(@Query("since") int since, @Query("per_page") int perPage);

}

сделать компонент api для кинжала 2.

@Singleton @Component (модули = {NetModule.class}) общедоступный интерфейс ApiComponent { void injectApi (действие MainActivity); }

сделать класс уровня приложения ..

public class AppActivity extends Application {
private ApiComponent component;

@Override
public void onCreate() {
    super.onCreate();
    component = DaggerApiComponent.builder()
            .netModule(new NetModule("https://api.github.com")).build();
}

public ApiComponent getComponent() {
    return component;
}

}

определите этот класс в файле манифеста в теге xml приложения ..

        android:name = ".AppActivity"

затем после внедрения объекта модификации в класс деятельности ..

public class MainActivity extends AppCompatActivity {
private Button mBtnCallWs;
private ListView mLvData;
@Inject
Retrofit retrofit;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ((AppActivity)getApplication()).getComponent().injectApi(this);

    initView();
}

private void initView() {
    mBtnCallWs=findViewById(R.id.amBtnCall);
    mLvData=findViewById(R.id.amLvData);
    mBtnCallWs.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            getHeroes();
        }
    });
}

private void getHeroes() {
    Api api = retrofit.create(Api.class);
    Call<List<Hero>> call = api.getHero();

    call.enqueue(new Callback<List<Hero>>() {
        @Override
        public void onResponse(Call<List<Hero>> call, Response<List<Hero>> response) {
            List<Hero> heroList = response.body();
            String[] heroes = new String[heroList.size()];

            for (int i = 0; i < heroList.size(); i++) {
                heroes[i] = heroList.get(i).getName();
            }

            mLvData.setAdapter(new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, heroes));
        }

        @Override
        public void onFailure(Call<List<Hero>> call, Throwable t) {
            Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
        }
    });
}

}

Приведенный выше код обеспечивает привязку адаптера также там, чтобы вы могли внести изменения в соответствии с.

Это не решает проблему OP и совершенно не связано с вопросом

David Medenjak 30.03.2018 15:19

Эта ошибка возникает из-за того, что вы аннотируете свое поле (val) вместо метода предоставления (getHeaderInterceptor), который Dagger хочет использовать.

Вы можете (должны) правильно объявить метод предоставления:

@Named(NetworkModule.Interceptors.HEADER)
fun provideHeaderInterceptor(): Interceptor

Я не знаю, как и почему вы бы использовали val headerInterceptor, но вы также можете исправить это, объявив аннотацию, которая будет использоваться в получателе

@get:Named(NetworkModule.Interceptors.HEADER")
val foo : Any

Другие вопросы по теме