Retrofit в Android: REST API интеграция для стабильной разработки
Для кого эта статья:
- Разработчики, изучающие Android-разработку и интеграцию сетевых технологий
- Люди, желающие улучшить свои навыки программирования и изучить библиотеку Retrofit
Специалисты, ищущие практические советы по эффективному использованию REST API в мобильных приложениях
Сетевые операции — кровеносная система любого современного Android-приложения. Когда в 2018 году мы с командой запустили приложение, которое "падало" при каждом втором запросе к API, я понял одну простую истину: качественная работа с сетью определяет успех проекта. Retrofit стал для меня настоящим откровением, превратив хаотичный AsyncTask-ад в элегантную и предсказуемую систему запросов. В этом руководстве я раскрою все секреты эффективной интеграции REST API с использованием Retrofit — библиотеки, которая экономит нервы, время и ресурсы устройства. 🚀
Хотите построить карьеру в программировании без мучительных проб и ошибок? Курс Java-разработки от Skypro не только обучит вас языку Java с нуля, но и раскроет все тонкости разработки под Android. Вы научитесь грамотно интегрировать сетевые компоненты, работать с REST API и использовать Retrofit для создания стабильных и быстрых приложений — навыки, за которые работодатели готовы платить премиальные зарплаты.
Основы работы с сетью в Android: REST API и Retrofit
REST (Representational State Transfer) — это архитектурный стиль, который определяет набор ограничений и свойств для построения распределенных систем. В контексте программирования андроид приложений, REST API становится стандартным способом взаимодействия с внешними сервисами.
Retrofit — это типобезопасная HTTP-библиотека для Android и Java, разработанная Square. Она упрощает процесс взаимодействия с REST API, абстрагируя множество низкоуровневых деталей работы с сетью, позволяя разработчикам сосредоточиться на бизнес-логике.
Александр Петров, Lead Android Developer
Когда я начал разрабатывать приложение для крупной торговой сети, первым вызовом стала интеграция с их системой каталогов. Бэкенд предоставлял REST API с непредсказуемым поведением: некоторые запросы возвращали JSON, другие — XML, а иногда даже обычный текст. Поначалу я пытался использовать стандартный HttpURLConnection, но быстро погряз в бесконечных проверках и преобразованиях.
Retrofit кардинально изменил ситуацию. Я потратил день на настройку конвертеров для разных типов ответов, и библиотека взяла всю сложность на себя. Приложение стало стабильно работать даже при нестабильном соединении, а количество кода сократилось примерно на 60%. Руководство было настолько впечатлено скоростью работы, что выделило дополнительный бюджет на развитие мобильного направления.
Основные преимущества использования Retrofit в разработке приложений на android:
- Декларативный подход — определяете интерфейс API и получаете готовую реализацию
- Автоматическая сериализация/десериализация данных из JSON в Java/Kotlin-объекты
- Встроенная поддержка RxJava и Coroutines для асинхронной обработки
- Легкая интеграция с OkHttp для тонкой настройки HTTP-клиента
- Простое управление заголовками и параметрами запросов
Архитектурно, REST API основан на ряде принципов, которые делают его привлекательным для мобильной разработки:
| Принцип REST | Значение для Android-разработки |
|---|---|
| Клиент-серверная архитектура | Разделение ответственности между Android-приложением и бэкендом |
| Statelessness (Отсутствие состояния) | Упрощает работу при нестабильном мобильном соединении |
| Кэширование | Позволяет экономить трафик пользователя и ускорять работу приложения |
| Единообразие интерфейса | Предсказуемые HTTP-методы и статус-коды для обработки |
| Многоуровневая система | Возможность использовать промежуточные прокси и балансировщики нагрузки |
Прежде чем приступить к настройке Retrofit, убедитесь, что ваше приложение имеет разрешение на доступ к интернету. Добавьте следующие строки в файл AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Настройка Retrofit для разработки приложений под Android
Начальная настройка Retrofit — критический этап, который определяет, насколько гибко и эффективно ваше приложение будет взаимодействовать с сетью. Правильная конфигурация экономит часы отладки в будущем. 🔧
Первым шагом при разработке приложений под Android с использованием Retrofit необходимо добавить зависимости в файл build.gradle на уровне модуля:
dependencies {
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
// Gson конвертер
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// OkHttp
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1'
}
После добавления зависимостей Gradle, базовая настройка Retrofit выполняется в несколько шагов:
- Создание экземпляра OkHttpClient с необходимыми настройками
- Инициализация Retrofit Builder с базовым URL вашего API
- Добавление конвертера для парсинга JSON (обычно Gson)
- Создание экземпляра Retrofit
- Генерация имплементации вашего API-интерфейса
Вот пример полной настройки Retrofit с продвинутыми опциями для программирования андроид приложений:
// Создаем интерцептор для логирования
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG)
HttpLoggingInterceptor.Level.BODY
else
HttpLoggingInterceptor.Level.NONE
}
// Настраиваем OkHttpClient
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.build()
// Создаем экземпляр Retrofit
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
// Создаем сервис API из интерфейса
val apiService = retrofit.create(ApiService::class.java)
Михаил Соколов, Android System Architect
В одном из финтех-проектов мы столкнулись с серьезной проблемой производительности при взаимодействии с API. Каждый запрос занимал от 3 до 5 секунд, что категорически не устраивало пользователей.
Анализ показал, что основной причиной была неоптимальная настройка Retrofit и OkHttp. Я пересмотрел всю конфигурацию и внес несколько критических изменений: настроил кэширование ответов, оптимизировал таймауты и добавил механизм повторных попыток для нестабильных соединений.
Результат превзошел все ожидания. Среднее время запроса упало до 300-500 мс, а количество "падений" приложения из-за сетевых ошибок снизилось на 87%. Пользователи отметили заметное ускорение работы приложения, а рейтинг в Play Store вырос с 3.7 до 4.5 за два месяца. Все это — результат правильной настройки Retrofit.
Для более сложных сценариев при разработке приложений на android часто требуется настроить дополнительные компоненты:
| Компонент | Назначение | Пример использования |
|---|---|---|
| Interceptor | Модификация запросов и ответов | Добавление аутентификационных заголовков, логирование |
| Cache | Сохранение ответов для оффлайн-режима | Кэширование данных каталогов и изображений |
| Call Adapter | Изменение возвращаемых типов запросов | Интеграция с RxJava, Coroutines, LiveData |
| Converter Factory | Преобразование форматов данных | Работа с JSON, XML, Protocol Buffers |
| Connection Pool | Управление соединениями | Оптимизация повторного использования соединений |
Для обеспечения безопасного соединения в продакшн-приложениях рекомендуется настраивать SSL/TLS с проверкой сертификатов. Вот пример конфигурации безопасного клиента:
// Создаем менеджер доверенных сертификатов
val trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
)
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
// Настраиваем SSL
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, trustManagers, null)
// Добавляем в OkHttpClient
val okHttpClient = OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustManagers[0] as X509TrustManager)
.hostnameVerifier { hostname, session -> true }
.build()
Создание API интерфейсов при программировании Android
Создание правильных API интерфейсов — это искусство, которое определяет, насколько элегантно ваше приложение будет взаимодействовать с бэкендом. Retrofit превращает ваши интерфейсы в функциональные эндпойнты, делая сложное простым. 📱
При программировании андроид приложений с Retrofit, API интерфейсы играют центральную роль. Они представляют собой декларативное описание эндпоинтов, к которым приложение будет обращаться. Retrofit динамически создает имплементацию этих интерфейсов, что избавляет от необходимости вручную писать код для работы с HTTP запросами.
Пример базового API интерфейса для работы с сервисом пользователей:
interface UserApiService {
@GET("users")
suspend fun getUsers(): Response<List<User>>
@GET("users/{id}")
suspend fun getUserById(@Path("id") userId: Int): Response<User>
@POST("users")
suspend fun createUser(@Body user: User): Response<User>
@PUT("users/{id}")
suspend fun updateUser(
@Path("id") userId: Int,
@Body user: User
): Response<User>
@DELETE("users/{id}")
suspend fun deleteUser(@Path("id") userId: Int): Response<Void>
}
Retrofit поддерживает множество аннотаций для гибкой настройки запросов:
- @GET, @POST, @PUT, @DELETE, @HEAD, @OPTIONS, @PATCH — HTTP методы
- @Path — для подстановки переменных в URL
- @Query — для параметров запроса (например, ?name=value)
- @QueryMap — для группы параметров запроса
- @Body — для отправки объекта в теле запроса
- @Field и @FieldMap — для form-encoded запросов
- @Header и @HeaderMap — для установки HTTP заголовков
- @Part и @PartMap — для multipart запросов (например, загрузки файлов)
- @Url — для динамической установки полного URL
Работа с параметрами запросов особенно важна для фильтрации данных:
@GET("products")
suspend fun searchProducts(
@Query("category") category: String,
@Query("min_price") minPrice: Double? = null,
@Query("max_price") maxPrice: Double? = null,
@Query("sort") sortOrder: String = "popularity",
@Query("page") page: Int = 1,
@Query("per_page") perPage: Int = 20
): Response<PagedResponse<Product>>
Для загрузки файлов в разработке приложений под Android можно использовать multipart-запросы:
@Multipart
@POST("upload")
suspend fun uploadProfilePicture(
@Part("user_id") userId: RequestBody,
@Part profileImage: MultipartBody.Part
): Response<UploadResult>
// Пример использования
fun createUploadRequest(userId: String, imageFile: File) {
val userIdPart = RequestBody.create(MediaType.parse("text/plain"), userId)
val requestFile = RequestBody.create(
MediaType.parse(getContentResolver().getType(imageUri)),
imageFile
)
val imagePart = MultipartBody.Part.createFormData(
"profile_image",
imageFile.name,
requestFile
)
val response = apiService.uploadProfilePicture(userIdPart, imagePart)
// Обработка ответа
}
Более сложный пример API интерфейса, демонстрирующий различные возможности Retrofit:
interface EcommerceApiService {
@GET("products/{productId}")
fun getProductDetails(
@Path("productId") productId: String,
@Query("include") includeSections: List<String>,
@Header("Accept-Language") language: String = "en"
): Call<ProductDetails>
@FormUrlEncoded
@POST("auth/login")
fun login(
@Field("email") email: String,
@Field("password") password: String,
@Field("device_id") deviceId: String
): Call<AuthResponse>
@GET
fun downloadFile(@Url fileUrl: String): Call<ResponseBody>
@Headers("Cache-Control: max-age=640000")
@GET("config")
fun getConfiguration(): Call<AppConfig>
@PATCH("cart/{cartId}")
fun updateCartItem(
@Path("cartId") cartId: String,
@Body updateRequest: CartItemUpdate
): Call<Cart>
@HTTP(method = "DELETE", path = "bookmarks", hasBody = true)
fun removeBookmarks(@Body bookmarkIds: List<String>): Call<Void>
}
Обработка сетевых запросов в разработке Android приложений
Обработка сетевых запросов в Android требует внимания к множеству аспектов: асинхронное выполнение, обработка ошибок, управление жизненным циклом, отмена запросов и интерпретация результатов. Retrofit предоставляет инструменты для решения всех этих задач. 🔄
При разработке приложений на android существует несколько подходов к выполнению сетевых запросов с Retrofit:
- Синхронный вызов — блокирует поток до получения результата (не рекомендуется для главного потока)
- Асинхронный вызов с callback — традиционный подход с использованием Callback
- Использование Coroutines — современный и рекомендуемый способ для Kotlin-разработки
- Интеграция с RxJava — для реактивного программирования
- Работа с LiveData — для интеграции с архитектурными компонентами
Рассмотрим примеры этих подходов:
1. Асинхронный вызов с Callback (базовый подход):
apiService.getUserProfile(userId).enqueue(object : Callback<UserProfile> {
override fun onResponse(call: Call<UserProfile>, response: Response<UserProfile>) {
if (response.isSuccessful) {
val userProfile = response.body()
// Обновляем UI с полученными данными
updateUserInterface(userProfile)
} else {
// Обрабатываем ошибку от сервера
handleApiError(response.code(), response.errorBody())
}
}
override fun onFailure(call: Call<UserProfile>, t: Throwable) {
// Обрабатываем сетевую ошибку
handleNetworkError(t)
}
})
2. Использование Coroutines (рекомендуемый подход):
// В ViewModel или другом компоненте с CoroutineScope
viewModelScope.launch {
try {
val response = apiService.getUserProfile(userId)
if (response.isSuccessful) {
val userProfile = response.body()
// Обрабатываем успешный ответ
_userProfileLiveData.value = userProfile
} else {
// Обрабатываем ошибку API
_errorState.value = parseErrorResponse(response.errorBody())
}
} catch (e: IOException) {
// Обрабатываем сетевую ошибку
_errorState.value = NetworkError("Проверьте подключение к интернету")
} catch (e: Exception) {
// Обрабатываем прочие ошибки
_errorState.value = UnknownError("Произошла неизвестная ошибка")
}
}
При обработке сетевых запросов в разработке приложений под Android необходимо учитывать различные типы ошибок:
| Тип ошибки | Описание | Способ обработки |
|---|---|---|
| HTTP-ошибки (4xx, 5xx) | Ошибки, возвращаемые сервером | Анализ кода и тела ответа (response.errorBody()) |
| Сетевые ошибки | Проблемы с соединением | Перехват IOException, ConnectException |
| Ошибки парсинга | Неверный формат данных | Обработка JsonParseException, JsonSyntaxException |
| Таймауты | Превышение времени ожидания | Перехват SocketTimeoutException |
| SSL/TLS ошибки | Проблемы с сертификатами | Обработка SSLHandshakeException |
Для более эффективной работы с ошибками рекомендуется создать систему типизированных ошибок:
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Throwable) : Result<Nothing>()
object Loading : Result<Nothing>()
}
// Репозиторий с обработкой ошибок
class UserRepository(private val apiService: UserApiService) {
suspend fun getUserProfile(userId: String): Result<UserProfile> {
return try {
val response = apiService.getUserProfile(userId)
if (response.isSuccessful) {
Result.Success(response.body()!!)
} else {
Result.Error(HttpException(response))
}
} catch (e: Exception) {
Result.Error(e)
}
}
}
// Использование в ViewModel
viewModelScope.launch {
_profileState.value = Result.Loading
when (val result = userRepository.getUserProfile(userId)) {
is Result.Success -> {
_profileState.value = Result.Success(result.data)
}
is Result.Error -> {
_profileState.value = Result.Error(result.exception)
}
Result.Loading -> { /* уже установлено */ }
}
}
Отмена запросов — важный аспект при работе с сетью в Android. С Retrofit и Coroutines это делается автоматически при отмене корутины:
// Создаем Job для отслеживания и отмены запроса
val fetchJob = viewModelScope.launch {
try {
val response = apiService.getLargeDataSet()
// Обработка ответа
} catch (e: CancellationException) {
// Запрос был отменен, ничего делать не нужно
} catch (e: Exception) {
// Обработка других ошибок
}
}
// Отмена запроса при необходимости
fun cancelRequest() {
fetchJob.cancel()
}
// Автоматическая отмена при уничтожении ViewModel
override fun onCleared() {
super.onCleared()
viewModelScope.cancel() // Отменяет все запросы
}
Продвинутые техники использования Retrofit в Android
Продвинутое использование Retrofit позволяет создавать сложные, эффективные и масштабируемые решения для работы с API. Комбинируя различные техники, вы сможете значительно улучшить качество вашего кода и опыт пользователей. 🛠️
В этом разделе мы рассмотрим техники, которые выводят программирование андроид приложений с Retrofit на новый уровень.
1. Автоматическое обновление токенов авторизации
Одна из типичных задач при разработке приложений под Android — автоматическое обновление истекших токенов:
class AuthInterceptor(
private val tokenManager: TokenManager,
private val authService: AuthService
) : Interceptor {
@Synchronized
override fun intercept(chain: Interceptor.Chain): Response {
// Добавляем текущий токен
val originalRequest = chain.request().newBuilder()
.header("Authorization", "Bearer ${tokenManager.getAccessToken()}")
.build()
// Делаем запрос
val response = chain.proceed(originalRequest)
// Если токен истек (код 401), пробуем обновить
if (response.code() == 401) {
val newToken = refreshToken()
if (newToken != null) {
// Повторяем оригинальный запрос с новым токеном
val newRequest = originalRequest.newBuilder()
.header("Authorization", "Bearer $newToken")
.build()
response.close()
return chain.proceed(newRequest)
}
}
return response
}
private fun refreshToken(): String? {
// Синхронный запрос для обновления токена
val refreshTokenResponse = authService.refreshToken(
tokenManager.getRefreshToken()
).execute()
return if (refreshTokenResponse.isSuccessful) {
val tokens = refreshTokenResponse.body()!!
tokenManager.saveTokens(tokens.accessToken, tokens.refreshToken)
tokens.accessToken
} else {
// Перенаправление на экран авторизации
null
}
}
}
2. Реализация кэширования для оффлайн-режима
// Настройка кэширования в OkHttpClient
val cacheSize = 10 * 1024 * 1024 // 10 МБ
val cache = Cache(File(context.cacheDir, "http_cache"), cacheSize.toLong())
val okHttpClient = OkHttpClient.Builder()
.cache(cache)
.addNetworkInterceptor { chain ->
// Кэшируем все успешные GET-запросы на 1 час
val response = chain.proceed(chain.request())
if (chain.request().method() == "GET" && response.code() == 200) {
response.newBuilder()
.header("Cache-Control", "public, max-age=3600")
.build()
} else {
response
}
}
.addInterceptor { chain ->
var request = chain.request()
// В режиме оффлайн пытаемся использовать кэш
if (!isNetworkAvailable()) {
request = request.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=2419200")
.build()
}
chain.proceed(request)
}
.build()
Ольга Назарова, Technical Product Manager
В 2021 году мы запускали приложение для региональной транспортной компании, где основной проблемой стала работа в зонах с плохим интернетом. Пользователи жаловались, что не могут получить доступ к расписанию и купить билеты при входе в метро или в движущемся автобусе.
Мы внедрили продвинутую систему кэширования с Retrofit и OkHttp, где каждый ответ API сохранялся локально с информацией о времени актуальности. Когда пользователь открывал приложение без интернета, система автоматически предлагала кэшированные данные с пометкой, когда они были обновлены последний раз.
Особенно сложной была интеграция с платежной системой — тут мы добавили очередь операций, которые выполнялись, как только соединение восстанавливалось. Это решение увеличило конверсию в покупку на 34% и сократило количество негативных отзывов на 62%. Теперь мы внедряем аналогичный паттерн во все проекты, связанные с мобильностью.
3. Динамические базовые URL для мультитенантных приложений
class DynamicBaseUrlInterceptor(
private val configProvider: ConfigProvider
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
// Получаем текущий тенант из провайдера конфигурации
val currentTenant = configProvider.getCurrentTenant()
val baseUrl = getTenantBaseUrl(currentTenant)
// Создаем новый URL, заменяя хост и базовый путь
val newUrl = originalRequest.url().newBuilder()
.scheme("https")
.host(baseUrl.host())
.port(baseUrl.port())
.build()
val newRequest = originalRequest.newBuilder()
.url(newUrl)
.build()
return chain.proceed(newRequest)
}
private fun getTenantBaseUrl(tenant: String): HttpUrl {
return when (tenant) {
"tenant1" -> HttpUrl.parse("https://api.tenant1.com")!!
"tenant2" -> HttpUrl.parse("https://api.tenant2.com")!!
else -> HttpUrl.parse("https://api.default.com")!!
}
}
}
4. Оптимизация пакетной обработки запросов
При разработке приложений на Android часто требуется выполнить несколько связанных запросов. С Retrofit и Coroutines это можно сделать эффективно:
// Параллельное выполнение независимых запросов
suspend fun loadDashboardData(): DashboardData = coroutineScope {
val userProfileDeferred = async { apiService.getUserProfile() }
val notificationsDeferred = async { apiService.getNotifications() }
val recentOrdersDeferred = async { apiService.getRecentOrders(5) }
// Дожидаемся выполнения всех запросов
val userProfile = userProfileDeferred.await()
val notifications = notificationsDeferred.await()
val recentOrders = recentOrdersDeferred.await()
// Комбинируем результаты
DashboardData(userProfile, notifications, recentOrders)
}
// Последовательное выполнение зависимых запросов
suspend fun processOrder(productId: String, quantity: Int): OrderResult {
// 1. Проверяем наличие товара
val stockCheck = apiService.checkStock(productId, quantity)
if (!stockCheck.available) {
return OrderResult.OutOfStock(stockCheck.availableQuantity)
}
// 2. Добавляем в корзину
val cartResponse = apiService.addToCart(productId, quantity)
// 3. Создаем заказ
val orderResponse = apiService.createOrder(cartResponse.cartId)
// 4. Выполняем оплату
return when (val paymentResult = apiService.processPayment(orderResponse.orderId)) {
is PaymentSuccessful -> OrderResult.Success(orderResponse.orderId)
is PaymentFailed -> OrderResult.PaymentFailed(paymentResult.reason)
}
}
5. Реализация поддержки GraphQL с Retrofit
Хотя Retrofit создан для REST API, его можно адаптировать и для работы с GraphQL:
interface GraphQLService {
@POST("graphql")
suspend fun performQuery(
@Body query: GraphQLQuery
): Response<GraphQLResponse>
}
// Класс запроса GraphQL
data class GraphQLQuery(
val query: String,
val variables: Map<String, Any> = emptyMap()
)
// Класс ответа
data class GraphQLResponse(
val data: Map<String, Any>?,
val errors: List<GraphQLError>?
)
// Пример использования
val query = """
query GetUserAndOrders($userId: ID!) {
user(id: $userId) {
id
name
email
orders {
id
date
total
}
}
}
"""
val variables = mapOf("userId" to "user123")
val graphQLQuery = GraphQLQuery(query, variables)
viewModelScope.launch {
val response = graphQLService.performQuery(graphQLQuery)
if (response.isSuccessful) {
val graphQLResponse = response.body()!!
if (graphQLResponse.errors == null) {
// Извлекаем данные из ответа
val userData = graphQLResponse.data?.get("user") as Map<String, Any>
// Обрабатываем данные
} else {
// Обрабатываем ошибки GraphQL
}
} else {
// Обрабатываем HTTP-ошибки
}
}
Дополнительные продвинутые техники для профессиональной разработки:
- Реализация Rate Limiting для соблюдения ограничений API
- Компрессия запросов и ответов для экономии трафика
- Конвертеры для кастомных форматов данных (не только JSON)
- Интеграция с инструментами мониторинга для отслеживания производительности
- Мокирование ответов для тестирования без реального бэкенда
- Генерация клиентского кода из OpenAPI/Swagger-спецификаций
После изучения всех аспектов работы с сетью в Android, становится очевидно: Retrofit — не просто библиотека, а ключевой компонент современной мобильной разработки. Правильная архитектура сетевого слоя определяет, будет ли ваше приложение быстрым, стабильным и масштабируемым. Внедрение описанных техник — от базовой настройки до продвинутых паттернов — позволит вам создавать приложения, которые работают безупречно даже в условиях нестабильного соединения. Инвестируйте время в изучение Retrofit сейчас, и это многократно окупится в каждом вашем проекте.
Читайте также
- Тестирование и отладка в Android-разработке: ключевые инструменты
- Как профилировать производительность Android-приложений
- Мультимедийные API Android: возможности и оптимизация приложений
- Геолокация и карты в Android: интеграция, оптимизация, примеры
- Хранение данных в Android: выбор между SharedPreferences, SQLite, Room
- 20 инструментов для Android-разработчика: от IDE до тестирования
- 5 методов кэширования данных для ускорения Android-приложений
- Адаптивный дизайн Android: техники разработки для всех экранов
- Android-разработка с нуля: простое создание своего приложения
- Уведомления в Android: настройка и оптимизация фоновых процессов