Avatar ·

Как разрешить все типы сетевых подключений HTTP и HTTPS в Android 9 (Pie) на Kotlin?

Привет! Я разрабатываю приложение на Kotlin для Android, и столкнулся с проблемой при тестировании на устройстве с Android 9 (Pie). Мое приложение отправляет HTTP-запросы на сервер, который использует как HTTPS, так и HTTP (для тестового окружения). В Android 8 и ниже все работало отлично, но на Android 9 (API 28) я получаю ошибку "Cleartext HTTP traffic to my-server.com not permitted". Вот как я делаю запрос с помощью Retrofit: ```kotlin interface ApiService { @GET("api/data") suspend fun getData(): Response } val retrofit = Retrofit.Builder() .baseUrl("http://my-test-server.com/") .addConverterFactory(GsonConverterFactory.create()) .build() ``` Я понимаю, что Google ужесточил требования к безопасности в Android 9, но мне нужно временно разрешить все типы подключений для отладки. Я пробовал добавлять `android:usesCleartextTraffic="true"` в манифест, но это не помогло. Также я слышал про файл network_security_config.xml, но не знаю, как его правильно настроить. Подскажите, как полностью отключить проверку сетевой безопасности для всех типов подключений (HTTP и HTTPS) в Android 9? Мне нужно решение для отладки, в продакшене я планирую использовать только HTTPS. <<>>

Решение: настройка Network Security Configuration для Android 9 (Pie)

Проблема, с которой вы столкнулись, связана с изменениями в безопасности Android 9 (API 28). Google ввел строгие правила по умолчанию: разрешен только зашифрованный трафик (HTTPS). Для HTTP-запросов нужно явно настроить исключения. Ваш подход с android:usesCleartextTraffic="true" должен работать, но часто он не помогает, если не сбросить кэш или не переустановить приложение. Давайте рассмотрим правильное решение через файл конфигурации сети.

Шаг 1: Создайте файл network_security_config.xml

Создайте в ресурсах вашего проекта папку res/xml (если её нет) и добавьте файл network_security_config.xml со следующим содержимым:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>

Этот конфиг разрешает HTTP-трафик (cleartext) для всех доменов и доверяет системным сертификатам (для HTTPS). Если вы хотите разрешить только для конкретных доменов, используйте <domain-config>:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">my-test-server.com</domain>
    </domain-config>
</network-security-config>

Шаг 2: Укажите конфигурацию в AndroidManifest.xml

Добавьте атрибут android:networkSecurityConfig в тег <application>:

<application
    android:networkSecurityConfig="@xml/network_security_config"
    ... >
    ...
</application>

Шаг 3: Очистите кэш и переустановите приложение

После внесения изменений обязательно выполните очистку проекта (Build → Clean Project) и переустановите приложение на устройстве или эмуляторе. Простое обновление может не сработать, так как Android кэширует конфигурацию безопасности.

Альтернативный способ: android:usesCleartextTraffic

Если вы предпочитаете более простой (но менее гибкий) подход, добавьте в <application>:

<application
    android:usesCleartextTraffic="true"
    ... >
    ...
</application>

Это разрешит HTTP для всех доменов. Однако этот метод не дает тонкой настройки и может быть не рекомендован для продакшена.

Проверка работы

После настройки ваш код на Kotlin будет работать без ошибок:

val retrofit = Retrofit.Builder()
    .baseUrl("http://my-test-server.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()

// Запрос
CoroutineScope(Dispatchers.IO).launch {
    val response = apiService.getData()
    if (response.isSuccessful) {
        Log.d("SUCCESS", "Data: ${response.body()}")
    }
}

Важно: Для продакшена используйте только HTTPS и настройте <domain-config> для конкретных доверенных доменов. Полное отключение проверки безопасности делает приложение уязвимым для атак "человек посередине" (MITM).

<<>>

Альтернативный подход: программное разрешение через NetworkSpecifier

Если вам нужно разрешить HTTP-трафик только для определенных запросов или в коде (например, для библиотек, которые не поддерживают XML-конфигурацию), можно использовать NetworkSpecifier и ConnectivityManager. Однако этот способ сложнее и подходит для редких случаев.

Вот пример, как программно указать, что приложение может использовать HTTP для конкретного URL:

import android.net.*
import android.content.Context

fun allowCleartextForUrl(context: Context, url: String) {
    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val request = NetworkRequest.Builder()
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .build()

    connectivityManager.registerNetworkCallback(request, object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            // Сеть доступна, можно отправлять HTTP-запросы
            Log.d("NETWORK", "Network available for HTTP")
        }
    })
}

Но помните: этот метод не отключает проверку безопасности полностью. Он лишь помогает управлять сетевыми подключениями. Для решения вашей основной проблемы (ошибка Cleartext HTTP) лучше использовать network_security_config.xml.

Дополнительно: Если вы используете эмулятор Android, убедитесь, что у него есть доступ к интернету и что вы не используете localhost (вместо этого используйте 10.0.2.2 для доступа к хосту).