← Назад к вопросам
Как будут выглядеть запросы в твоем проекте
2.0 Middle🔥 241 комментариев
#Работа с данными#Сетевое взаимодействие
Комментарии (1)
🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Архитектура сетевых запросов в проекте
В моем проекте запросы реализуются через многоуровневую архитектуру с четким разделением ответственности. Я использую ретроспективный подход (Retrofit) в сочетании с Kotlin Coroutines для асинхронных операций.
1. Уровень определения API (Declarative Interface)
interface UserApiService {
@GET("users/{id}")
suspend fun getUserById(@Path("id") userId: Long): Response<UserDto>
@POST("users")
suspend fun createUser(@Body user: UserDto): Response<CreateUserResponse>
@Multipart
@POST("upload")
suspend fun uploadFile(@Part file: MultipartBody.Part): Response<UploadResponse>
@GET("users")
suspend fun getUsers(
@Query("page") page: Int,
@Query("limit") limit: Int = 20
): Response<PagedResponse<UserDto>>
}
2. Слой репозитория (Repository Pattern)
class UserRepository @Inject constructor(
private val apiService: UserApiService,
private val networkHandler: NetworkHandler
) {
suspend fun fetchUser(userId: Long): Result<User> = withContext(Dispatchers.IO) {
if (!networkHandler.isConnected()) {
return@withContext Result.failure(NetworkException.NoInternet)
}
return@withContext try {
val response = apiService.getUserById(userId)
when {
response.isSuccessful -> {
val userDto = response.body()
Result.success(userDto.toDomainModel())
}
response.code() == 404 -> {
Result.failure(NetworkException.NotFound("User not found"))
}
else -> {
Result.failure(NetworkException.ServerError(response.message()))
}
}
} catch (e: Exception) {
Result.failure(NetworkException.Unknown(e.message ?: "Unknown error"))
}
}
}
3. Конфигурация Retrofit с зависимостями
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(LoggingInterceptor())
.addInterceptor(AuthInterceptor())
.addInterceptor(ConnectivityInterceptor())
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(okHttpClient)
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()
}
@Provides
@Singleton
fun provideUserApiService(retrofit: Retrofit): UserApiService {
return retrofit.create(UserApiService::class.java)
}
}
4. Кастомные интерцепторы
class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer ${SessionManager.token}")
.addHeader("Content-Type", "application/json")
.addHeader("App-Version", BuildConfig.VERSION_NAME)
.build()
return chain.proceed(request)
}
}
class LoggingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
Timber.d("Request: ${request.method()} ${request.url()}")
val response = chain.proceed(request)
Timber.d("Response: ${response.code()} ${response.message()}")
return response
}
}
5. Обработка ошибок и состояний
sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(val exception: NetworkException) : NetworkResult<Nothing>()
object Loading : NetworkResult<Nothing>()
}
sealed class NetworkException : Exception() {
object NoInternet : NetworkException()
data class ServerError(val message: String) : NetworkException()
data class NotFound(val details: String) : NetworkException()
data class Unknown(val details: String) : NetworkException()
}
6. Использование в ViewModel
class UserViewModel @ViewModelInject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _userState = MutableStateFlow<NetworkResult<User>>(NetworkResult.Loading)
val userState: StateFlow<NetworkResult<User>> = _userState.asStateFlow()
fun loadUser(userId: Long) {
viewModelScope.launch {
_userState.value = NetworkResult.Loading
_userState.value = when (val result = userRepository.fetchUser(userId)) {
is Result.Success -> NetworkResult.Success(result.data)
is Result.Failure -> NetworkResult.Error(result.exception)
}
}
}
}
Ключевые принципы:
- Соблюдение SOLID - каждый слой отвечает за свою задачу
- Реактивное программирование - StateFlow/LiveData для UI обновлений
- Dependency Injection - чистая архитектура с Hilt/Dagger
- Безопасность - обработка SSL, хранение токенов в SecurePreferences
- Кэширование - стратегии кэширования через OkHttp Cache
- Логирование - детальное логирование запросов и ответов
- Тестирование - MockWebServer для unit-тестов
Такая архитектура обеспечивает масштабируемость, легкую поддержку и надежность сетевого слоя приложения.