Мы слишком долго использовали такие технологии, как Retrofit и Volley, и отрасль также очень эффективно адаптировала их. Тем не менее, с интенсивным ростом популярности Kotlin среди Android-разработчиков появился новый API для совершения сетевых вызовов в Kotlin: Ktor-client.

JetBrains каждый день стремительно развивает Kotlin. Это один из самых любимых языков в мире, покоривший сердца разработчиков Android. Однако там его не ограничили. Мы также можем создать внутренний сервер с Kotlin с API под названием Ktor, а Ktor-client — это его клиентская версия.

Настройка Ktor-клиента

Как обычно, нам нужно установить зависимости для Ktor-client. В этом уроке мы будем использовать следующие зависимости:

def ktor_version = "1.6.8"
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-android:$ktor_version"
implementation "io.ktor:ktor-client-serialization:$ktor_version"
implementation "io.ktor:ktor-client-logging:$ktor_version"
implementation "ch.qos.logback:logback-classic:1.2.9"

Ktor-client способствует использованию реализующих модулей, поэтому мы используем только те модули, которые необходимы, а не все, что есть в Ktor.

Создание интерфейса

Хотя для Ktor-client не требуется создавать интерфейс, рекомендуется использовать этот шаблон проектирования.

interface ApiService {
  suspend fun getPost(id: String): Response<PostDto>
  suspend fun createPost(post: Post): Response<PostDto>
}

и теперь мы можем реализовать этот интерфейс в конкретном классе. Здесь мы совершим настоящий сетевой вызов.

class ApiServiceImpl(
  private val client: HttpClient
) : ApiService {
  override suspend fun getPost(id: String): Response<PostDto> {
    return try {
      val response = client.get<PostDto> {
        url("$BASE_URL/posts/$id/?key=value")
      }
      return Response.Success(response)
    } catch (e: RedirectResponseException) { // 3xx 
      Response.Error(e.response.status.description)
    } catch (e: ClientRequestException) { // 4xx
      Response.Error(e.response.status.description)
    } catch (e: ServerResponseException) { // 5xx
      Response.Error(e.response.status.description)
    }
  }
  [...]
}

Ktor-client — это библиотека на основе Kotlin, которая использует все предоставляемые ею функции. Чтобы сделать HTTP-вызов GET, мы используем функцию suspend fun get() для объекта client. Функция будет анализировать входящие данные в общий параметр. Лямбда-функция компоновщика имеет метод url(), который принимает URL-адрес String с параметрами пути и параметрами запроса — необработанный URL-адрес.

Создание объекта клиента

Обычно я бы посоветовал вам сделать это с внедрением зависимостей, но сейчас я сделаю companion object внутри интерфейса ApiService.

interface ApiService {
  [...]
  companion object {
    val client = HttpClient(Android) {
      install(Logging) {
        level = LogLevel.ALL
      }            
      install(JsonFeature) {                
        serializer = KotlinxSerializer()
      }
    }
  }
}

Завершение

Теперь вы можете завершить все и начать сетевой вызов. Скажите, что вы хотите получать сообщения всякий раз, когда пользователь нажимает кнопку:

btnGetPost.setOnClickListener { _ ->
  lifecycleScope.launch {
    ApiService.client.use { 
      val service = ApiServiceImpl(it)
  
      when (val response = service.getPost("2")) {
        is Response.Success -> { /* do something */ }
        is Response.Error -> toast(response.message)
      }
    }
  }
}

Делаем POST-запрос

Это так же просто, как сделать get запрос.

override suspend fun createPost(post: Post): Response<PostDto> {
  try {
    val response = client.post<PostDto> {
      url("$BASE_URL/posts")
      contentType(ContentType.Application.Json)
      setBody(post)
    }
   } catch ([...]) {
     [ same as in the get function ]
   }
}

Мы используем метод post вместо метода get. Указываем Content-Type и передаем тело запроса.

Разные примечания

  1. Убедитесь, что все используемые data classs должны быть отмечены аннотацией @kotlinx.serialization.Serializable
  2. Класс Response, используемый в приведенном выше коде, выглядит следующим образом:
sealed class Response<T> {    
  data class Success<T>(val data: T) : Response<T>()    
  data class Error<T>(val message: String) : Response<T>()
}

3. Не забудьте добавить разрешения INTERNET в файл AndroidManifest.

Заключение

Ktor-client очень прост в настройке и очень удобен в использовании. Нет аннотаций для HTTP-методов или частей URL-адресов. Это простой вызов сетевого запроса, который возвращает объект класса данных, с которым нам нужно работать.