Введение

В современной Android-разработке использование Jetpack Compose для создания UI, наряду с надежными сетевыми библиотеками, такими как Retrofit и Ktor, и фреймворком для внедрения зависимостей, таким как Dagger Hilt, формирует масштабируемую и поддерживаемую архитектуру. Это руководство поможет настроить простое Android-приложение для отображения списка автомобилей с использованием сетевых библиотек (сначала Retrofit, затем Ktor), интегрированных с Dagger Hilt для внедрения зависимостей.

Jetpack Compose, Dagger Hilt, Retrofit, Ktor: общие сведения

Jetpack Compose

Jetpack Compose — современный Android-инструментарий для разработки нативных пользовательских интерфейсов. Он использует декларативный подход, позволяя разработчикам создавать сложные интерфейсы с меньшим количеством кода и большей гибкостью по сравнению с традиционным UI-фреймворком на основе XML.

Dagger Hilt

Dagger Hilt — библиотека внедрения зависимостей для Android, которая упрощает настройку Dagger. Она автоматизирует большую часть шаблонного кода и позволяет устанавливать зависимости в Android-компонентах проще и удобнее.

Retrofit

Retrofit — типизированный HTTP-клиент для Android и Java, созданный на базе OkHttp. Он абстрагирует процесс выполнения сетевых запросов и парсинга ответов, упрощая работу с REST API.

Ktor

Ktor — фреймворк для создания асинхронных серверов и клиентов в связанных системах с использованием Kotlin. Гибкий и настраиваемый HTTP-клиент, который предоставляет Ktor, можно применять в Android-приложениях в качестве альтернативы Retrofit.

1. Отображение списка автомобилей с помощью Retrofit

Шаг 1: Настройка Retrofit

Для начала настройте Retrofit в своем проекте для получения данных из сетевого источника.

Добавление зависимостей

В build.gradle (на уровне приложения) включите следующие зависимости:

dependencies {
implementation "com.squareup.retrofit2:retrofit:<latest-version>"
implementation "com.squareup.retrofit2:converter-gson:<latest-version>"
implementation "com.squareup.okhttp3:okhttp:<latest-version>"
implementation "com.squareup.okhttp3:logging-interceptor:<latest-version>"
implementation "androidx.compose.ui:ui:<latest-version>"
implementation "androidx.compose.material:material:<latest-version>"
implementation "androidx.compose.ui:ui-tooling-preview:<latest-version>"
implementation "androidx.hilt:hilt-navigation-compose:<latest-version>"
}

Создание модели данных

Определите класс данных, представляющий модель автомобиля:

data class Car(
val id: Int,
val name: String,
val manufacturer: String
)

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

Определите интерфейс, который Retrofit будет использовать для получения списка автомобилей:

interface CarApiService {
@GET("cars")
suspend fun getCars(): List<Car>
}

Создание модуля Retrofit с помощью Dagger Hilt

Создайте модуль Dagger Hilt, который будет предоставлять экземпляры Retrofit и OkHttp:

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideCarApiService(retrofit: Retrofit): CarApiService {
return retrofit.create(CarApiService::class.java)
}
}

Шаг 2: Получение данных в ViewModel

Создайте ViewModel для управления получением и хранением списка автомобилей:

@HiltViewModel
class CarViewModel @Inject constructor(
private val carApiService: CarApiService
) : ViewModel() {

private val _cars = mutableStateOf<List<Car>>(emptyList())
val cars: State<List<Car>> = _cars
init {
fetchCars()
}
private fun fetchCars() {
viewModelScope.launch {
_cars.value = carApiService.getCars()
}
}
}

Детали

  • ApiService Retrofit: вызов carApiService.getCars() в ViewModel использует экземпляр созданного Retrofit интерфейса CarApiService. Retrofit динамически генерирует реализацию этого интерфейса, что позволяет легко определять и использовать конечные точки API безопасным для типов образом.
  • Интеграция с OkHttp: Retrofit полагается на OkHttp в качестве базового HTTP-клиента. OkHttpClient настраивается и предоставляется для Retrofit в NetworkModule, что позволяет настраивать поведение сети: логирование, таймауты и перехватчики. OkHttp обрабатывает низкоуровневые детали выполнения HTTP-запросов и управления соединениями.
  • Конвертер Gson: Retrofit использует фабрику конвертеров, например GsonConverterFactory, для автоматической сериализации и десериализации JSON, что позволяет Retrofit конвертировать JSON-ответы от API непосредственно в объекты данных Kotlin (например, Car) с минимальными настройками, упрощая процесс работы с ответами API.

Шаг 3: Отображение списка автомобилей с помощью LazyColumn

Теперь можно отобразить список автомобилей с помощью LazyColumn в Jetpack Compose:

@Composable
fun CarListScreen(viewModel: CarViewModel = hiltViewModel()) {
val cars by viewModel.cars

LazyColumn {
items(cars) { car ->
CarItem(car)
}
}
}

@Composable
fun CarItem(car: Car) {
Card(modifier = Modifier.padding(8.dp).fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = car.name, style = MaterialTheme.typography.h6)
Text(text = car.manufacturer, style = MaterialTheme.typography.body2)
}
}
}

Шаг 4: Интеграция в MainActivity

Наконец, интегрируйте CarListScreen в класс MainActivity:

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CarListScreen()
}
}
}

2. Отображение списка автомобилей с помощью Ktor

Теперь реализуем ту же функциональность, используя Ktor вместо Retrofit.

Шаг 1: Настройка Ktor

Добавление зависимости

Замените зависимости Retrofit на зависимости Ktor в файле build.gradle:

dependencies {
implementation "io.ktor:ktor-client-core:<latest-version>"
implementation "io.ktor:ktor-client-cio:<latest-version>"
implementation "io.ktor:ktor-client-serialization:<latest-version>"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:<latest-version>"
}

Создание модуля клиента Ktor

Создайте модуль Dagger Hilt для предоставления HttpClient Ktor:

@Module
@InstallIn(SingletonComponent::class)

object NetworkModule {
@Provides
@Singleton
fun provideHttpClient(): HttpClient {
return HttpClient(CIO) {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
}
}
}

Шаг 2: Получение данных во ViewModel с помощью Ktor

Обновите ViewModel, чтобы использовать HttpClient Ktor для получения данных:

@HiltViewModel
class CarViewModel @Inject constructor(
private val client: HttpClient
) : ViewModel() {

private val _cars = mutableStateOf<List<Car>>(emptyList())
val cars: State<List<Car>> = _cars
init {
fetchCars()
}
private fun fetchCars() {
viewModelScope.launch {
try {
_cars.value = client.get("https://api.example.com/cars")
} catch (e: Exception) {
_cars.value = emptyList()
}
}
}
}

Детали

  •  HttpClient Ktorclient в вызове client.get("https://api.example.com/cars") является экземпляром HttpClient Ktor, а не OkHttp. Класс HttpClient является частью фреймворка Ktor и предназначен для обработки асинхронных сетевых операций.
  • Движок CIO: в модуле NetworkModule  HttpClient настроен на использование движка CIO, который является стандартным движком ввода/вывода Ktor, основанным на корутинах — этот механизм обрабатывает HTTP-запросы и ответы.
  • СериализацияKotlinxSerializer используется вместе с JsonFeature для обработки сериализации и десериализации JSON, что позволяет автоматически преобразовывать ответы в объекты Kotlin.

Шаг 3: Отображение списка автомобилей с помощью LazyColumn

Можете повторно использовать те же composable-функции CarListScreen и CarItem , которые были определены ранее, поскольку пользовательский интерфейс остается неизменным:

@Composable
fun CarListScreen(viewModel: CarViewModel = hiltViewModel()) {

val cars by viewModel.cars

LazyColumn {
items(cars) { car ->
CarItem(car)
}
}
}
@Composable
fun CarItem(car: Car) {
Card(modifier = Modifier.padding(8.dp).fillMaxWidth()) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = car.name, style = MaterialTheme.typography.h6)
Text(text = car.manufacturer, style = MaterialTheme.typography.body2)
}
}
}

Шаг 4: Интеграция в MainActivity

Настройка класса MainActivity остается прежней:

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CarListScreen()
}
}
}

Заключение

И Retrofit, и Ktor могут быть эффективно использованы для получения и отображения данных в Android-приложении с помощью Jetpack Compose и Dagger Hilt. Выбор между этими двумя библиотеками зависит от требований конкретного проекта.

  • Retrofit: подойдет разработчикам, которым нужна хорошо зарекомендовавшая себя, простая в использовании библиотека с большим сообществом и встроенной интеграцией OkHttp.
  • Ktor: предлагает больше гибкости и возможностей для настройки, что делает этот фреймворк идеальным выбором для проектов, требующих тонкого контроля над сетевыми операциями.

Следуя этому руководству, вы сможете реализовать простое отображение списка, используя как Retrofit, так и Ktor. Выбирайте тот вариант, который соответствует вашим потребностям.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Dobri Kostadinov: Jetpack Compose: Setup Retrofit and Ktor using Dagger Hilt for Dependency Injection

Предыдущая статья70% интервьюеров задают эти 5 вопросов по React.js 
Следующая статьяРеализация ролевого контроля доступа в Elasticsearch