I just got a problem to inject repository in the interceptor to get access token when its needed or rather expired. I just don't understand where I did something wrong. I just didn't find any example how to deal with interceptor and repository. Thinking about that double "Retrofit.Builder" maybe this is a problem. What do you think? And let's the code talk:
#Module
class AppModule {
#Singleton
#Provides
fun provideRefreshTokenService(client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
#Singleton
#Provides
fun provideHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addNetworkInterceptor(headerInterceptor)
.build()
}
#Singleton
#Provides
fun provideRetrofit(client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}
class TwichHeaderRepository #Inject constructor(
private val refreshTokenApi: RefreshTokenApi)
{
suspend fun fetchRefreshToken(): Response<RefreshToken> {
return withContext(Dispatchers.IO) {
refreshTokenApi.getRefreshToken()
}
}
}
private const val TAG = "AddRepositoryAction"
private const val HEADER_CLIENT_ID = "Client-ID"
private const val HEADER_AUTHORIZATION = "Authorization"
private const val HEADER_ACCEPT = "Accept"
private const val DEFAULT_ACCESS_TOKEN = "mjycvndz4sasons2mg990kqme6vu6d"
private const val UNAUTHORIZED_STATUS_CODE = 401
#Singleton
class HeaderInterceptor #Inject constructor(
private val context: Context,
private val twichHeaderRepository: TwichHeaderRepository
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $DEFAULT_ACCESS_TOKEN")
header(HEADER_ACCEPT, "application/json")
}.build())
val refreshToken = runBlocking { generateAccessToken(twichHeaderRepository) }
if (response.code == UNAUTHORIZED_STATUS_CODE) {
response.close()
val accessToken = ""
return chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $accessToken")
header(HEADER_ACCEPT, "application/json")
}.build())
}
return response
}
}
private suspend fun generateAccessToken(twichHeaderRepository: TwichHeaderRepository): String
{
val responseRefreshToken = twichHeaderRepository.fetchRefreshToken()
return responseRefreshToken.body().toString()
}
interface RefreshTokenApi {
#POST(
...
)
suspend fun getRefreshToken(
): Response<RefreshToken>
}
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent {
#Component.Factory
interface Factory {
fun create(#BindsInstance context: Context): AppComponent
}
fun inject(activity: MainActivity)
fun inject(fragment: AddFragment)
fun inject(interceptor: HeaderInterceptor)
}
class TwichRepository #Inject constructor(
private val twichApi: TwichApi
) {
suspend fun searchGames(title: String): Response<Game> {
return withContext(Dispatchers.IO) { twichApi.getGamesBySearch(title) }
}
}
Error message is
Found a dependency cycle okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRefreshTokenService(client)
com.example.glc.add.RefreshTokenApi is injected at
com.example.glc.add.TwichHeaderRepository(refreshTokenApi)
com.example.glc.add.TwichHeaderRepository is injected at
com.example.glc.add.HeaderInterceptor(�, twichHeaderRepository)
com.example.glc.add.HeaderInterceptor is injected at
com.example.glc.AppModule.provideHttpClient(headerInterceptor)
okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRetrofit(client)
com.example.glc.add.TwichApi is injected at
com.example.glc.add.TwichRepository(twichApi)
com.example.glc.add.TwichRepository is injected at
com.example.glc.add.AddViewModel(twichRepository)
com.example.glc.add.AddViewModel is injected at
com.example.glc.add.AddFragment.addViewModel
com.example.glc.add.AddFragment is injected at
com.example.glc.di.AppComponent.inject(com.example.glc.add.AddFragment)
You use Lazy on TwichHeaderRepository dependency in HeaderInterceptor's constructor to break the dependency cycle.
class HeaderInterceptor #Inject constructor(
private val lazyTwichHeaderRepository: Lazy<TwichHeaderRepository>
) {
// Access the dependency lazily with lazyTwichHeaderRepository.get()
}
I just needed to add #Named to resolve my problem and understand it as Mark said in the comment.
#Module
class AppModule {
#Singleton
#Provides
#Named("oauth2")
fun provideAccessTokenHttpClient() = OkHttpClient.Builder().build()
#Singleton
#Provides
fun provideRefreshTokenService(#Named("oauth2") client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
#Singleton
#Provides
#Named("igdb")
fun provideSearchDataHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(headerInterceptor)
.build()
}
#Singleton
#Provides
fun provideRetrofit(#Named("igdb") client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}
Related
I am new with dagger using. So, I can't solve whats the problem with this. I just want to ask here to solve it.
This is the error:
C:\Users\msi\Documents\MyAndroidProjects\MovieProjects\app\build\generated\hilt\component_sources\debug\com\example\movieapp\App_HiltComponents.java:128:
error: [Dagger/MissingBinding]
com.example.movieapp.api.MovieAppService cannot be provided without an
#Provides-annotated method. public abstract static class SingletonC
implements App_GeneratedInjector,
^
com.example.movieapp.api.MovieAppService is injected at
com.example.movieapp.repository.MovieRepository(movieAppService)
com.example.movieapp.repository.MovieRepository is injected at
com.example.movieapp.viewmodel.MainViewModel(repository, �)
com.example.movieapp.viewmodel.MainViewModel is injected at
com.example.movieapp.viewmodel.MainViewModel_HiltModules.BindsModule.binds(arg0)
#dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>>
is requested at
dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap()
[com.example.movieapp.App_HiltComponents.SingletonC ?
com.example.movieapp.App_HiltComponents.ActivityRetainedC ?
com.example.movieapp.App_HiltComponents.ViewModelC]
MainViewModel.kt
#HiltViewModel
class MainViewModel#Inject constructor(
private val repository: MovieRepository,
#ApplicationContext private val context: Context
) : ViewModel() {
val movieList = MutableLiveData<Resource<Movie>>()
fun getAllMovies(movieName: String) {
movieList.postValue(Resource.Loading())
viewModelScope.launch {
try {
if (hasInternetConnection(context)) {
val response = repository.getMovies(movieName, "ffe9063f")
movieList.postValue(Resource.Success(response.body()!!))
} else
movieList.postValue(Resource.Error("Internet yok"))
} catch (ex: Exception) {
when (ex) {
is IOException -> movieList.postValue(Resource.Error("Network Failure " + ex.localizedMessage))
else -> movieList.postValue(Resource.Error("Conversion Error"))
}
}
}
}
}
MovieRepository.kt
#Singleton
class MovieRepository #Inject constructor(private val movieAppService: MovieAppService) {
suspend fun getMovies(title: String, aKey: String): Response<Movie> = withContext(
Dispatchers.IO
) {
val movies = movieAppService.getMovies(title = title, aKey = aKey)
movies
}
}
ApiModule.kt
class ApiModule {
#Module
#InstallIn(SingletonComponent::class)
object ApiModule {
#Provides
#Singleton
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
}
#Provides
#Singleton
fun provideOkHttpClient(logging: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(15, TimeUnit.SECONDS) // connect timeout
.readTimeout(15, TimeUnit.SECONDS)
.build()
}
#Provides
#Singleton
fun provideRetrofit(client: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
}
#Provides
#Singleton
fun provideMovieAppService(retrofit: Retrofit): MovieAppService {
return retrofit.create(MovieAppService::class.java)
}
}
}
MovieAppService.kt
interface MovieAppService {
companion object {
const val ENDPOINT = "http://www.omdbapi.com/"
}
#GET(".")
suspend fun getMovies(#Query("t") title: String,#Query("apikey") aKey: String): Response<Movie>
}
Do not wrap your singleton object module with a same named class. Change your module file like this or change your class name
#Module
#InstallIn(SingletonComponent::class)
object ApiModule {
#Provides
#Singleton
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)
}
#Provides
#Singleton
fun provideOkHttpClient(logging: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(logging)
.connectTimeout(15, TimeUnit.SECONDS) // connect timeout
.readTimeout(15, TimeUnit.SECONDS)
.build()
}
#Provides
#Singleton
fun provideRetrofit(client: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
}
#Provides
#Singleton
fun provideMovieAppService(retrofit: Retrofit): MovieAppService {
return retrofit.create(MovieAppService::class.java)
}
}
Hello folks please help me to debug this dagger2 issue,I am very new to dagger2 and just started with a project.
public abstract interface ApplicationComponent {
^
java.util.Map<java.lang.Class<?
>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<?>>> is injected at
dagger.android.DispatchingAndroidInjector(injectorFactoriesWithClassKeys, �)
dagger.android.DispatchingAndroidInjector<java.lang.Object> is injected at
com.example.dictionary.App.dispatchingServiceInjector
com.example.dictionary.App is injected at
com.example.dictionary.di.components.ApplicationComponent.inject(com.example.dictionary.App)
error: [Dagger/MissingBinding]
java.util.Map<java.lang.String,javax.inject.Provider<dagger.android.AndroidInjector.Factory<?
>>> cannot be provided without an #Provides-annotated method.
My implementations are:
interface ApiService {
companion object {
const val BASE_URL= "https://od-api.oxforddictionaries.com/"
}
#GET("api/v2/entries/en-gb/")
fun getMeaning(#Query("word") word: String): Observable<Response>
}
}
open class RemoteDataSource #Inject constructor(private val apiService: ApiService) {
fun getWordMeaning(word: String) = apiService.getMeaning(word)
}
#ApplicationScope
#Component(modules = [ApplicationModule::class, NetworkModule::class])
interface ApplicationComponent {
fun inject(app: App)
fun inject(mainActivity: MainActivity)
}
#Module
class ApplicationModule(private val application: Application) {
#ApplicationScope
#Provides
fun provideApplicationContext(): Context = application.applicationContext
#ApplicationScope
#Provides
fun provideSharedPreferences(): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(application)
#ApplicationScope
#Provides
fun providerDisplayMetrics(): DisplayMetrics = application.resources.displayMetrics
}
#Module
class NetworkModule(
private val baseUrl: String,
private val application: Application
) {
companion object {
const val CACHE_SIZE = 10 * 1024 * 1024L // 10 MiB
const val TIME_OUT = 10L // time in minutes to get the response from server
}
#ApplicationScope
#Provides
fun provideGson(): Gson = GsonBuilder().create()
#ApplicationScope
#Provides
fun provideOkHttpCache() = Cache(application.cacheDir, CACHE_SIZE)
#ApplicationScope
#Provides
fun provideOkHttpClient(cache: Cache): OkHttpClient = with(OkHttpClient.Builder()) {
writeTimeout(3, TimeUnit.MINUTES)
.connectTimeout(3, TimeUnit.MINUTES)
.readTimeout(TIME_OUT, TimeUnit.MINUTES)
cache(cache)
addInterceptor(headersInterceptor())
build()
}
#ApplicationScope
#Provides
fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
#ApplicationScope
#Provides
fun provideApiService(retrofit: Retrofit): ApiService =
retrofit.create(ApiService::class.java)
private var authRequest: String? = null
private fun headersInterceptor() = Interceptor { chain ->
val requestBuilder = chain.request().newBuilder()
requestBuilder.addHeader("Accept", "application/json")
requestBuilder.addHeader("app_id", "SOME APP ID")
requestBuilder.addHeader("app_key", "Some API KEY")
chain.proceed(
requestBuilder.build()
)
}
}
class App : Application(), HasAndroidInjector {
#Inject
lateinit var dispatchingServiceInjector:
DispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
applicationComponent.inject(this)
RxJavaPlugins.setErrorHandler {
it.printStackTrace()
}
}
override fun androidInjector(): AndroidInjector<Any> {
return dispatchingServiceInjector
}
val applicationComponent: ApplicationComponent by
lazy<ApplicationComponent>(mode = LazyThreadSafetyMode.NONE) {
DaggerApplicationComponent
.builder()
.applicationModule(ApplicationModule(this))
.networkModule(NetworkModule(ApiService.BASE_URL, this))
.build()
}
}
I tried re-building the code, also attempted invalidate cache and restart to re-install dependencies, as dagger2 requires re-building the project every time once a new dependency injection is implemented, followed YouTube tutorials, to implement this so far, but unable to detect the problem, although in the build crash its indicating to provide #Provides, but in my NetworkModule, ApplicationModule I set this #Provides annotation.
Please help to figure out the solution.
As suggested in this answer, just add your App class in the android manifest under the application tag.
<application ...
android:name = ".App" ...>
...
I am trying to implement multiple instance of retrofit for two different base URL`s using Dagger2 and MVVM. I have already wasted a huge amount of time testing different approaches provided here in SO, but still unable to figure it out. I have created new Qualifiers as well and created a new instance but all my request are still made using first instance.Here is my implementation.
Qualifiers
#Retention(AnnotationRetention.BINARY)
#Qualifier
annotation class APIService1
#Retention(AnnotationRetention.BINARY)
#Qualifier
annotation class APIService2
AppModule class
#AssistedModule
#Suppress("unused")
#Module(includes = [ViewModelModule::class, CoreDataModule::class, AssistedViewModelModule::class, AssistedInject_AppModule::class])
class AppModule {
#Singleton
#Provides
fun provideServices(
APIService1 okHttpClient: OkHttpClient, converterFactory: MoshiConverterFactory
) =
provideService(okHttpClient, converterFactory, MyApi::class.java)
#Singleton
#Provides
#APIService2
fun provideMicroServices(
APIService1 okHttpClient: OkHttpClient, converterFactory: MoshiConverterFactory
) =
provideMicroService(okHttpClient, converterFactory, MyApi::class.java)
#APIService1
#Provides
fun providePrivateOkHttpClient(upstreamClient: OkHttpClient): OkHttpClient {
return upstreamClient.newBuilder().build()
}
#Singleton
#Provides
fun provideRemoteDataSource(myApiService: MyApi) = RemoteDataSource(myApiService)
private fun createRetrofit(
okhttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory
): Retrofit {
Retrofit.Builder()
.baseUrl("https://example1.com/api/")
.client(okhttpClient)
.addConverterFactory(converterFactory)
.build()
}
private fun createMicroServiceRetrofit(
okhttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory
): Retrofit {
Retrofit.Builder()
.baseUrl("https://example2.com/api/")
.client(okhttpClient)
.addConverterFactory(converterFactory)
.build()
}
private fun <T> provideService(
okhttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory, clazz: Class<T>
): T {
return createRetrofit(okhttpClient, converterFactory).create(clazz)
}
private fun <T> provideMicroService(
okhttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory, clazz: Class<T>
): T {
return createMicroServiceRetrofit(okhttpClient, converterFactory).create(clazz)
}
}
CoreDataModule class
#Module
class CoreDataModule {
#Provides
fun provideOkHttpClient(): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient().newBuilder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(90, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
return client.build()
}
#Provides
#Singleton
fun provideMoshi(): Moshi = Moshi.Builder().build()
#Provides
#Singleton
fun provideMoshiConverterFactory(): MoshiConverterFactory =
MoshiConverterFactory.create()
}
RemoteDataSource Class
#Keep
class RemoteDataSource #Inject constructor(private val service: MyApi) : BaseDataSource() {
suspend fun getOtp(data: String) = getResult { service.getOtp(data) }
suspend fun getData(data: String) = getResult { service.getData(data) }
}
BaseDataSource Class
abstract class BaseDataSource {
protected suspend fun <T> getResult(call: suspend () -> Response<T>): Result<T> {
val response = call()
if (response.isSuccessful) {
val body = response.body()
if (body != null) return Result.success(body)
}
return error(" ${response.code()} ${response.message()}")
}
}
MyApi Class
interface MyApi {
#POST("Register/Otp")
#FormUrlEncoded
suspend fun getOtp(#Field("data") data: String): Response<OtpResponse>
#POST("Home/Data")
#FormUrlEncoded
suspend fun getData(#Field("data") data: String): Response<DataResponse>
}
This is how i am using it in my ViewModel
One of My ViewModles
class OtpViewModel #Inject constructor(
private val remoteDataSource: RemoteDataSource
) :
ViewModel() {
fun getNewOtp() = liveData {
try {
emit(Resource.loading(data = null))
val response = remoteDataSource.getOtp(params))
emit(Resource.success(data = response))
} catch (e: Exception) {
emit(Resource.error(data = null, message = e.message ?: e.localizedMessage.orEmpty()))
}
}
Create 2 annotations like this:
#Qualifier
#Target(
AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FIELD,
AnnotationTarget.VALUE_PARAMETER
)
#Retention(AnnotationRetention.BINARY)
annotation class ApiService1
#Qualifier
#Target(
AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FIELD,
AnnotationTarget.VALUE_PARAMETER
)
#Retention(AnnotationRetention.BINARY)
annotation class ApiService2
Change your providers with:
#Provides
#Singleton
#ApiService1
fun provideServices(....): MyApi {
return ...
}
#Provides
#Singleton
#ApiService2
fun provideMicroServices(....): MyApi {
return ...
}
Then in your Android classes you can do this:
class MyActivity : FragmentActivity() {
#Inject
#ApiService1
lateinit var retrofit1: MyApi
#Inject
#ApiService2
lateinit var retrofit2: MyApi
....
}
Or in classes:
class Repository #Inject constructor (
#ApiService1 retrofit1: MyApi
) {
....
}
I want to refresh my token using an intercepter but my interceptor needs an API service to make API calls. I am stuck in a dependency cycle.
Here is my ApplicationModule class:
#Module
#InstallIn(ApplicationComponent::class)
class ApplicationModule {
#Provides
fun providerBaseUrl() = AppConstants.BASE_URL
#Provides
#Singleton
fun provideOkHttpClient(authInterceptor: AuthInterceptor,
networkInterceptor: NetworkInterceptor) = if (BuildConfig.DEBUG) {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
OkHttpClient.Builder()
.addInterceptor(authInterceptor)
.addInterceptor(loggingInterceptor)
.addInterceptor(networkInterceptor)
// .addInterceptor(refreshTokenInterceptor) // I want to put my interceptor here
.connectTimeout(2, TimeUnit.MINUTES)
.readTimeout(2, TimeUnit.MINUTES)
.writeTimeout(2, TimeUnit.MINUTES)
.build()
} else OkHttpClient
.Builder()
.connectTimeout(2, TimeUnit.MINUTES)
.readTimeout(2, TimeUnit.MINUTES)
.writeTimeout(2, TimeUnit.MINUTES)
.addInterceptor(authInterceptor)
.addInterceptor(networkInterceptor)
.build()
#Provides
#Singleton
fun provideRetrofit(
okHttpClient: OkHttpClient,
BASE_URL: String
): Retrofit =
Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create())
.baseUrl(BASE_URL)
.client(okHttpClient)
.build()
#Provides
#Singleton
fun provideApiService(retrofit: Retrofit): WebApi = retrofit.create(WebApi::class.java)
#Provides
#Singleton // this is my provider
fun provideRefreshTokenService(webApi: WebApi): RefreshTokenInterceptor {
return RefreshTokenInterceptor(webApi)
}
#Provides
fun provideAuthInterceptor(): AuthInterceptor {
return AuthInterceptor()
}
#Provides
fun provideNetWorkInterceptor(): NetworkInterceptor {
return NetworkInterceptor()
}
}
And this is my RefreshTokenInterceptor:
class RefreshTokenInterceptor #Inject constructor(webApi: WebApi) : Interceptor {
var api = webApi
override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
val updatedToken = getUpdatedToken(webApi = api)
requestBuilder.header("Authorization", updatedToken)
return chain.proceed(requestBuilder.build())
}
private fun getUpdatedToken(webApi: WebApi): String {
GlobalScope.launch {
val authTokenResponse = webApi.refreshToken()
val newToken = "${authTokenResponse.body()!!.data.token}"
SharedPref.getInstance(AppController.applicationContext()).setUserToken(newToken)
}
return SharedPref.getInstance(AppController.applicationContext()).getUserToken
}
}
I was facing a similar issue and found a solution. You can wrap the api instance in your Token Interceptor with Lazy interface. This will solve your cyclic dependency issue.
Here is a code snippet.
class RefreshTokenInterceptor #Inject constructor(private val webApi: Lazy<WebApi>) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val requestBuilder = chain.request().newBuilder()
val updatedToken = getUpdatedToken(webApi = api.get())
requestBuilder.header("Authorization", updatedToken)
return chain.proceed(requestBuilder.build())
}
private fun getUpdatedToken(webApi: WebApi): String {
GlobalScope.launch {
val authTokenResponse = webApi.refreshToken()
val newToken = "${authTokenResponse.body()!!.data.token}"
SharedPref.getInstance(AppController.applicationContext()).setUserToken(newToken)
}
return SharedPref.getInstance(AppController.applicationContext()).getUserToken
}
}
Look at this example:
#Provides
#Singleton
fun provideDatabase(
#ApplicationContext context: Context,
#RoomCreateCallback callback: RoomDatabase.Callback
) = Room.databaseBuilder(context, TaskDatabase::class.java, Constants.TaskKeys.TASK_DATABASE)
.fallbackToDestructiveMigration()
.addCallback(callback)
.build()
#Provides
fun provideTaskDao(taskDatabase: TaskDatabase) = taskDatabase.taskDao()
#Provides
#Singleton
#RoomCreateCallback
fun provideDatabaseCreateCallback(
taskDatabase: Provider<TaskDatabase>,
#ApplicationScope applicationScope: CoroutineScope
) = object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val dao = taskDatabase.get().taskDao()
applicationScope.launch {
dao.insert(Task("First task"))
dao.insert(Task("Second task"))
dao.insert(Task("Third task", important = true))
dao.insert(Task("Fourth task", completed = true))
dao.insert(Task("Fifth task"))
dao.insert(Task("Sixth task", completed = true))
dao.insert(Task("Seventh task"))
dao.insert(Task("Eighth task"))
}
}
}
I need RoomDatabase.Callback when I create RoomDatabase, but I also need RoomDatabase when executing this callback. In provideDatabaseCreateCallback() function I wrapped RoomDatabase inside Provider<>.
I'm working in a project and I need to inject Retrofit object with Dagger 2. I search info but I only have been able to do a few steps and now I don't know how to continue:
My component:
#Singleton
#Component(modules = arrayOf(NetworkModule::class))
interface NetworkComponent {
fun inject(foo: TheApplication)
}
My module:
#Module
class NetworkModule {
#Provides
#Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.chucknorris.io/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
}
And now I see I have to make a Class who extends from Application for init the Component and make an #Inject next:
class TheApplication: Application() {
override fun onCreate() {
super.onCreate()
val net: NetworkModule = Dagger...
}
}
Theoretically when I put the Dagger... it should be DaggerNetworkComponent but I rebuild project and still missing.
Anyone can explain to me for what is the Application.class made for and how I can continue?
Try this
AppComponent.kt
#Component(modules = [NetworkModule::class])
#Singleton
interface AppComponent {
fun inject(app: MyApp)
}
NetworkModule.kt
#Module
class NetworkModule {
#Singleton
#Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("YOUR_BASE_URL")
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
#Singleton
#Provides
fun provideApi(retrofit: Retrofit): WebApi {
return retrofit.create(WebApi::class.java)
}
#Singleton
#Provides
fun provideOkHttpClient(
interceptors: ArrayList<Interceptor>
): OkHttpClient {
val clientBuilder = OkHttpClient.Builder()
.followRedirects(false)
interceptors.forEach {
clientBuilder.addInterceptor(it)
}
return clientBuilder.build()
}
#Singleton
#Provides
fun provideInterceptors(): ArrayList<Interceptor> {
val interceptors = arrayListOf<Interceptor>()
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
interceptors.add(loggingInterceptor)
return interceptors
}
}
MyApp.kt
class MyApp : Application() {
companion object {
lateinit var instance: MyApp
private set
}
lateinit var appComponent: AppComponent
private set
override fun onCreate() {
super.onCreate()
instance = this
initComponent()
}
private fun initComponent() {
appComponent = DaggerAppComponent.builder()
.build()
appComponent.inject(this)
}
}
AndroidManifest.xml
<application
android:name=".MyApp"
....