Refactor: Migrate from Retrofit to Ktor
This commit migrates the network layer from Retrofit to Ktor. Specific changes include: - Replaced Retrofit with Ktor for network requests. - Updated dependencies to include Ktor libraries. - Refactored network service and data classes to use Ktor's API. - Removed Retrofit-specific code and dependencies. - Adjusted network module to provide Ktor client and services. - Updated PixabayImageRepository to use the new Ktor-based PixabayService.
This commit is contained in:
		| @@ -23,16 +23,19 @@ android { | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     implementation(libs.retrofit) | ||||
|  | ||||
|     implementation(platform(libs.okhttp.bom)) | ||||
|     implementation(libs.okhttp) | ||||
|     implementation(libs.logging.interceptor) | ||||
|  | ||||
|     implementation(libs.androidx.paging.compose) | ||||
|  | ||||
|     implementation(libs.kotlinx.serialization.json) | ||||
|     implementation(libs.retrofit2.kotlinx.serialization.converter) | ||||
|  | ||||
|     //Ktor | ||||
|     implementation(libs.ktor.client.android) | ||||
|     implementation(libs.ktor.client.resources) | ||||
|     implementation(libs.ktor.client.content.negotiation) | ||||
|     implementation(libs.ktor.client.logging) | ||||
|     implementation(libs.ktor.serialization.kotlinx.json) | ||||
|  | ||||
|     //Logging | ||||
|     implementation(libs.slf4j.android) | ||||
|  | ||||
|     implementation(libs.androidx.room.runtime) | ||||
|     ksp(libs.androidx.room.compiler) | ||||
|   | ||||
| @@ -1,52 +1,60 @@ | ||||
| package dev.adriankuta.pixabay.data.di | ||||
|  | ||||
| import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory | ||||
| import dagger.Binds | ||||
| import dagger.Module | ||||
| import dagger.Provides | ||||
| import dagger.hilt.InstallIn | ||||
| import dagger.hilt.components.SingletonComponent | ||||
| import dev.adriankuta.pixabay.data.BuildConfig | ||||
| import dev.adriankuta.pixabay.data.network.KtorPixabayService | ||||
| import dev.adriankuta.pixabay.data.network.PixabayService | ||||
| import io.ktor.client.HttpClient | ||||
| import io.ktor.client.engine.android.Android | ||||
| import io.ktor.client.plugins.contentnegotiation.ContentNegotiation | ||||
| import io.ktor.client.plugins.defaultRequest | ||||
| import io.ktor.client.plugins.logging.Logging | ||||
| import io.ktor.client.plugins.resources.Resources | ||||
| import io.ktor.http.URLProtocol | ||||
| import io.ktor.serialization.kotlinx.json.json | ||||
| import kotlinx.serialization.json.Json | ||||
| import okhttp3.MediaType.Companion.toMediaType | ||||
| import okhttp3.OkHttpClient | ||||
| import okhttp3.logging.HttpLoggingInterceptor | ||||
| import retrofit2.Retrofit | ||||
| import javax.inject.Singleton | ||||
|  | ||||
| @InstallIn(SingletonComponent::class) | ||||
| @Module | ||||
| internal class NetworkModule { | ||||
|     @Singleton | ||||
|     @Provides | ||||
|     fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor { | ||||
|         val logging = HttpLoggingInterceptor() | ||||
|         logging.level = | ||||
|             if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE | ||||
|         return logging | ||||
|     } | ||||
| internal abstract class NetworkModule { | ||||
|  | ||||
|     @Singleton | ||||
|     @Provides | ||||
|     fun provideOkHttpClient( | ||||
|         httpLoggingInterceptor: HttpLoggingInterceptor | ||||
|     ): OkHttpClient { | ||||
|         return OkHttpClient.Builder() | ||||
|             .addInterceptor(httpLoggingInterceptor) | ||||
|             .build() | ||||
|     } | ||||
|     @Binds | ||||
|     abstract fun provideKtorApi(ktorPixabayService: KtorPixabayService): PixabayService | ||||
|  | ||||
|     companion object { | ||||
|         @Singleton | ||||
|         @Provides | ||||
|         fun provideKtorClient(): HttpClient { | ||||
|             val httpClient = HttpClient(Android) { | ||||
|                 install(ContentNegotiation) { | ||||
|                     json( | ||||
|                         Json { | ||||
|                             ignoreUnknownKeys = true | ||||
|                         } | ||||
|                     ) | ||||
|                 } | ||||
|                 install(Resources) | ||||
|                 install(Logging) | ||||
|                 defaultRequest { | ||||
|                     url { | ||||
|                         protocol = URLProtocol.HTTP | ||||
|                         host = "pixabay.com" | ||||
|                         parameters.append("key", BuildConfig.PIXABAY_API_KEY) | ||||
|                     } | ||||
|                 } | ||||
|                 engine { | ||||
|                     connectTimeout = 100_000 | ||||
|                     socketTimeout = 100_000 | ||||
|                 } | ||||
|             } | ||||
|             return httpClient | ||||
|         } | ||||
|  | ||||
|     @Singleton | ||||
|     @Provides | ||||
|     fun providePixabayApi( | ||||
|         okHttpClient: OkHttpClient | ||||
|     ): PixabayService { | ||||
|         val networkJson = Json { ignoreUnknownKeys = true } | ||||
|         return Retrofit.Builder() | ||||
|             .client(okHttpClient) | ||||
|             .baseUrl("https://pixabay.com/") | ||||
|             .addConverterFactory(networkJson.asConverterFactory("application/json".toMediaType())) | ||||
|             .build() | ||||
|             .create(PixabayService::class.java) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| package dev.adriankuta.pixabay.data.dto.request | ||||
|  | ||||
| import io.ktor.resources.Resource | ||||
| import kotlinx.serialization.SerialName | ||||
|  | ||||
| @Resource("/api") | ||||
| internal class PixabayImages( | ||||
|     @SerialName("q") | ||||
|     val query: String, | ||||
|     val page: Int, | ||||
|     val pageSize: Int | ||||
| ) | ||||
| @@ -0,0 +1,8 @@ | ||||
| package dev.adriankuta.pixabay.data.dto.request | ||||
|  | ||||
| import io.ktor.resources.Resource | ||||
|  | ||||
| @Resource("/api") | ||||
| internal class PixabayImagesById( | ||||
|     val id: String, | ||||
| ) | ||||
| @@ -1,5 +1,6 @@ | ||||
| package dev.adriankuta.pixabay.data.dto.response | ||||
|  | ||||
| import io.ktor.resources.Resource | ||||
| import kotlinx.serialization.Serializable | ||||
|  | ||||
| @Serializable | ||||
|   | ||||
| @@ -0,0 +1,26 @@ | ||||
| package dev.adriankuta.pixabay.data.network | ||||
|  | ||||
| import dev.adriankuta.pixabay.data.dto.request.PixabayImages | ||||
| import dev.adriankuta.pixabay.data.dto.request.PixabayImagesById | ||||
| import dev.adriankuta.pixabay.data.dto.response.SearchPixabayImagesResponse | ||||
| import io.ktor.client.HttpClient | ||||
| import io.ktor.client.call.body | ||||
| import io.ktor.client.plugins.resources.get | ||||
| import javax.inject.Inject | ||||
|  | ||||
| internal class KtorPixabayService @Inject constructor( | ||||
|     private val client: HttpClient, | ||||
| ) : PixabayService { | ||||
|  | ||||
|     override suspend fun searchImages( | ||||
|         query: String, | ||||
|         page: Int, | ||||
|         pageSize: Int | ||||
|     ): SearchPixabayImagesResponse { | ||||
|         return client.get(PixabayImages(query, page, pageSize)).body() | ||||
|     } | ||||
|  | ||||
|     override suspend fun searchImageById(id: String): SearchPixabayImagesResponse { | ||||
|         return client.get(PixabayImagesById(id)).body() | ||||
|     } | ||||
| } | ||||
| @@ -1,23 +1,16 @@ | ||||
| package dev.adriankuta.pixabay.data.network | ||||
|  | ||||
| import dev.adriankuta.pixabay.data.BuildConfig | ||||
| import dev.adriankuta.pixabay.data.dto.response.SearchPixabayImagesResponse | ||||
| import retrofit2.http.GET | ||||
| import retrofit2.http.Query | ||||
|  | ||||
| internal interface PixabayService { | ||||
|  | ||||
|     @GET("api") | ||||
|     suspend fun searchImages( | ||||
|         @Query("q") query: String, | ||||
|         @Query("page") page: Int, | ||||
|         @Query("per_page") pageSize: Int, | ||||
|         @Query("key") key: String = BuildConfig.PIXABAY_API_KEY | ||||
|         query: String, | ||||
|         page: Int, | ||||
|         pageSize: Int | ||||
|     ): SearchPixabayImagesResponse | ||||
|  | ||||
|     @GET("api") | ||||
|     suspend fun searchImageById( | ||||
|         @Query("id") query: String, | ||||
|         @Query("key") key: String = BuildConfig.PIXABAY_API_KEY | ||||
|         id: String, | ||||
|     ): SearchPixabayImagesResponse | ||||
| } | ||||
| @@ -5,7 +5,6 @@ import androidx.paging.PagingState | ||||
| import dev.adriankuta.pixabay.data.model.PixabayImage | ||||
| import dev.adriankuta.pixabay.data.network.PixabayService | ||||
| import dev.adriankuta.pixabay.data.repository.PixabayImageRepository.Companion.NETWORK_PAGE_SIZE | ||||
| import retrofit2.HttpException | ||||
| import java.io.IOException | ||||
|  | ||||
| private const val STARTING_PAGE_INDEX = 1 | ||||
| @@ -34,8 +33,6 @@ internal class PixabayPagingSource( | ||||
|             ) | ||||
|         } catch (exception: IOException) { | ||||
|             return LoadResult.Error(exception) | ||||
|         } catch (exception: HttpException) { | ||||
|             return LoadResult.Error(exception) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -7,12 +7,10 @@ import androidx.paging.LoadType | ||||
| import androidx.paging.PagingState | ||||
| import androidx.paging.RemoteMediator | ||||
| import androidx.room.withTransaction | ||||
| import dev.adriankuta.pixabay.data.model.PixabayImage | ||||
| import dev.adriankuta.pixabay.data.network.PixabayService | ||||
| import dev.adriankuta.pixabay.data.room.AppDatabase | ||||
| import dev.adriankuta.pixabay.data.room.entity.PixabayImageEntity | ||||
| import dev.adriankuta.pixabay.data.room.entity.RemoteKeys | ||||
| import retrofit2.HttpException | ||||
| import java.io.IOException | ||||
|  | ||||
| private const val STARTING_PAGE_INDEX = 1 | ||||
| @@ -78,8 +76,6 @@ internal class PixabayRemoteMediator( | ||||
|             return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached) | ||||
|         } catch (exception: IOException) { | ||||
|             return MediatorResult.Error(exception) | ||||
|         } catch (exception: HttpException) { | ||||
|             return MediatorResult.Error(exception) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.flow.map | ||||
| import timber.log.Timber | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Named | ||||
|  | ||||
| internal class PixabayImageRepository @Inject constructor( | ||||
|     private val database: AppDatabase, | ||||
| @@ -75,6 +76,6 @@ internal class PixabayImageRepository @Inject constructor( | ||||
|  | ||||
|     companion object { | ||||
|         const val NETWORK_PAGE_SIZE = 30 | ||||
|         const val USE_CACHE_PAGER = true | ||||
|         const val USE_CACHE_PAGER = false | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user