Reuse OkHttp instance

This commit is contained in:
Kirill Kamakin
2021-11-20 22:23:51 +03:00
parent a6e948ca6b
commit dd9c302729
7 changed files with 63 additions and 33 deletions

View File

@@ -6,7 +6,6 @@ import androidx.room.TypeConverters
import gq.kirmanak.mealient.data.impl.RoomTypeConverters
import gq.kirmanak.mealient.data.recipes.db.RecipeDao
import gq.kirmanak.mealient.data.recipes.db.entity.*
import javax.inject.Singleton
@Database(
version = 1,
@@ -14,7 +13,6 @@ import javax.inject.Singleton
exportSchema = false
)
@TypeConverters(RoomTypeConverters::class)
@Singleton
abstract class AppDb : RoomDatabase() {
abstract fun recipeDao(): RecipeDao
}

View File

@@ -9,19 +9,35 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import gq.kirmanak.mealient.data.impl.OkHttpBuilder
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
interface AppModule {
companion object {
@Provides
fun createDb(@ApplicationContext context: Context): AppDb {
return Room.databaseBuilder(context, AppDb::class.java, "app.db").build()
}
@Singleton
fun createDb(@ApplicationContext context: Context): AppDb =
Room.databaseBuilder(context, AppDb::class.java, "app.db").build()
@Provides
fun createSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context)
@Singleton
fun createSharedPreferences(@ApplicationContext context: Context): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context)
@Provides
@Singleton
fun createOkHttp(okHttpBuilder: OkHttpBuilder): OkHttpClient =
okHttpBuilder.buildOkHttp()
@Provides
@Singleton
fun createJson(): Json = Json {
coerceInputValues = true
ignoreUnknownKeys = true
}
}
}

View File

@@ -1,18 +1,29 @@
package gq.kirmanak.mealient.data.auth.impl
import gq.kirmanak.mealient.data.auth.AuthStorage
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.Response
import timber.log.Timber
import javax.inject.Inject
const val AUTHORIZATION_HEADER = "Authorization"
class AuthOkHttpInterceptor(token: String) : Interceptor {
private val headerValue = "Bearer $token"
class AuthOkHttpInterceptor @Inject constructor(
private val authStorage: AuthStorage
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val newRequest = chain.request()
.newBuilder()
.addHeader(AUTHORIZATION_HEADER, headerValue)
.build()
return chain.proceed(newRequest)
Timber.v("intercept() called with: chain = $chain")
val token = runBlocking { authStorage.getToken() }
Timber.d("intercept: token = $token")
val request = if (token.isNullOrBlank()) {
chain.request()
} else {
chain.request()
.newBuilder()
.addHeader(AUTHORIZATION_HEADER, "Bearer $token")
.build()
}
return chain.proceed(request)
}
}

View File

@@ -8,12 +8,14 @@ import okhttp3.logging.HttpLoggingInterceptor
import timber.log.Timber
import javax.inject.Inject
class OkHttpBuilder @Inject constructor() {
fun buildOkHttp(token: String?): OkHttpClient {
Timber.v("buildOkHttp() called with: token = $token")
class OkHttpBuilder @Inject constructor(
private val authOkHttpInterceptor: AuthOkHttpInterceptor
) {
fun buildOkHttp(): OkHttpClient {
Timber.v("buildOkHttp() called")
val builder = OkHttpClient.Builder()
if (BuildConfig.DEBUG) builder.addNetworkInterceptor(buildLoggingInterceptor())
if (token != null) builder.addNetworkInterceptor(AuthOkHttpInterceptor(token))
builder.addNetworkInterceptor(authOkHttpInterceptor)
return builder.build()
}

View File

@@ -4,26 +4,23 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import timber.log.Timber
import javax.inject.Inject
@ExperimentalSerializationApi
class RetrofitBuilder @Inject constructor(private val okHttpBuilder: OkHttpBuilder) {
private val json by lazy {
Json {
coerceInputValues = true
ignoreUnknownKeys = true
}
}
fun buildRetrofit(baseUrl: String, token: String? = null): Retrofit {
Timber.v("buildRetrofit() called with: baseUrl = $baseUrl, token = $token")
class RetrofitBuilder @Inject constructor(
private val okHttpClient: OkHttpClient,
private val json: Json
) {
fun buildRetrofit(baseUrl: String): Retrofit {
Timber.v("buildRetrofit() called with: baseUrl = $baseUrl")
val contentType = "application/json".toMediaType()
val converterFactory = json.asConverterFactory(contentType)
return Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpBuilder.buildOkHttp(token))
.client(okHttpClient)
.addConverterFactory(converterFactory)
.build()
}

View File

@@ -38,7 +38,7 @@ class RecipeDataSourceImpl @Inject constructor(
val baseUrl = checkNotNull(authRepo.getBaseUrl()) { "Base url is null" }
val token = checkNotNull(authRepo.getToken()) { "Token is null" }
Timber.d("requestRecipes: baseUrl = $baseUrl, token = $token")
val retrofit = retrofitBuilder.buildRetrofit(baseUrl, token)
val retrofit = retrofitBuilder.buildRetrofit(baseUrl)
val createdService = retrofit.create(RecipeService::class.java)
_recipeService = createdService
createdService

View File

@@ -2,8 +2,10 @@ package gq.kirmanak.mealient.data.impl
import com.google.common.truth.Truth.assertThat
import dagger.hilt.android.testing.HiltAndroidTest
import gq.kirmanak.mealient.data.auth.AuthStorage
import gq.kirmanak.mealient.data.auth.impl.AUTHORIZATION_HEADER
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_TOKEN
import gq.kirmanak.mealient.test.AuthImplTestData.TEST_URL
import gq.kirmanak.mealient.test.MockServerTest
import okhttp3.OkHttpClient
import okhttp3.Request
@@ -17,16 +19,20 @@ class OkHttpBuilderTest : MockServerTest() {
@Inject
lateinit var subject: OkHttpBuilder
@Inject
lateinit var authStorage: AuthStorage
@Test
fun `when token null then no auth header`() {
val client = subject.buildOkHttp(null)
val client = subject.buildOkHttp()
val header = sendRequestAndExtractAuthHeader(client)
assertThat(header).isNull()
}
@Test
fun `when token isn't null then auth header contains token`() {
val client = subject.buildOkHttp(TEST_TOKEN)
authStorage.storeAuthData(TEST_TOKEN, TEST_URL)
val client = subject.buildOkHttp()
val header = sendRequestAndExtractAuthHeader(client)
assertThat(header).isEqualTo("Bearer $TEST_TOKEN")
}